Complete guide to webhooks, debugging, and real-time event monitoring with HookMetry
HMAC SHA256 is the industry-standard method for securing webhooks. Use this for custom integrations or APIs that don't match Stripe/GitHub/Shopify formats.
HMAC (Hash-based Message Authentication Code) combines a secret key with your webhook payload to create a unique signature:
signature = HMAC-SHA256(secret_key, request_body)Hookmetry automatically checks these common header names:
X-SignatureX-Webhook-SignatureX-Hub-SignatureX-HMAC-Signatureconst crypto = require('crypto');
function generateHMAC(payload, secret) {
const hmac = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return hmac;
}
// Usage
const payload = { event: 'user.created', userId: 123 };
const secret = 'your_webhook_secret';
const signature = generateHMAC(payload, secret);
// Send webhook
fetch('https://hookmetry.com/webhook/ep_abc123', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Signature': signature
},
body: JSON.stringify(payload)
});import hmac
import hashlib
import json
import requests
def generate_hmac(payload, secret):
message = json.dumps(payload).encode('utf-8')
secret_bytes = secret.encode('utf-8')
signature = hmac.new(secret_bytes, message, hashlib.sha256).hexdigest()
return signature
# Usage
payload = {'event': 'user.created', 'userId': 123}
secret = 'your_webhook_secret'
signature = generate_hmac(payload, secret)
# Send webhook
requests.post(
'https://hookmetry.com/webhook/ep_abc123',
json=payload,
headers={'X-Signature': signature}
)<?php
function generateHMAC($payload, $secret) {
$json = json_encode($payload);
$signature = hash_hmac('sha256', $json, $secret);
return $signature;
}
// Usage
$payload = ['event' => 'user.created', 'userId' => 123];
$secret = 'your_webhook_secret';
$signature = generateHMAC($payload, $secret);
// Send webhook
$ch = curl_init('https://hookmetry.com/webhook/ep_abc123');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Signature: ' . $signature
]);
curl_exec($ch);
?>const crypto = require('crypto');
function verifyHMAC(payload, receivedSignature, secret) {
// Compute expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
// Use timing-safe comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(receivedSignature),
Buffer.from(expectedSignature)
);
}
// Express.js middleware example
app.post('/webhook', express.json(), (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!verifyHMAC(req.body, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook
console.log('Valid webhook received:', req.body);
res.status(200).json({ received: true });
});Security Best Practices:
Mismatched Payload Serialization
The sender and receiver must serialize the payload identically. Watch out for:
Using Raw Body vs Parsed JSON
If you parse JSON before verification, you may alter the payload. Always verify against the raw body.
Case Sensitivity Issues
HMAC signatures are case-sensitive. Don't convert to uppercase/lowercase.
Testing HMAC Webhooks:
Create a Custom HMAC endpoint in Hookmetry, set your secret, then use the code examples above to send test webhooks. Check the webhook logs to see validation results and debug signature mismatches.