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.
Try the free webhook testerWas this page helpful?
Your feedback helps us improve the docs.