Complete guide to webhooks, debugging, and real-time event monitoring with HookMetry
Svix-managed webhooks throw signature mismatch errors on receipt.
Copy-paste verified examples. Use the tab that matches your stack.
const crypto = require('crypto');
app.post('/webhooks/svix', express.raw({ type: 'application/json' }), (req, res) => {
const webhookId = req.headers['webhook-id'];
const webhookTimestamp = req.headers['webhook-timestamp'];
const webhookSignature = req.headers['webhook-signature'];
if (!webhookId || !webhookTimestamp || !webhookSignature) {
return res.status(400).send('Missing Svix headers');
}
// Build the exact signed content Svix expects
const signedContent = `${webhookId}.${webhookTimestamp}.${req.body.toString()}`;
// Strip "whsec_" prefix, then Base64-decode to get raw key bytes
const secretBytes = Buffer.from(
process.env.SVIX_WEBHOOK_SECRET.replace(/^whsec_/, ''),
'base64'
);
const computed = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64');
// Header may contain multiple signatures: "v1,<base64> v1a,<base64>"
const isValid = webhookSignature
.split(' ')
.map(s => s.split(',')[1])
.some(sig => sig === computed);
if (!isValid) return res.status(401).send('Invalid signature');
const event = JSON.parse(req.body);
console.log('Svix event:', event.type);
res.status(200).send('OK');
});Extract headers → decode secret → construct signature string → hash → compare.
Works with webhooks and other async event systems (including AI callbacks).
Instead of guessing, inspecting the exact payload and headers can help debug faster. Tools like Hookmetry support this workflow.
Try the free webhook testerWas this page helpful?
Your feedback helps us improve the docs.