Clerk webhook requests arrive but are rejected as invalid signatures. Clerk uses Svix to deliver webhooks, so the fix follows the Svix signature pattern.
Copy-paste verified examples. Use the tab that matches your stack.
// app/api/webhooks/clerk/route.js
import { Webhook } from 'svix';
export async function POST(req) {
const webhookSecret = process.env.CLERK_WEBHOOK_SECRET; // whsec_...
const svixId = req.headers.get('webhook-id');
const svixTimestamp = req.headers.get('webhook-timestamp');
const svixSignature = req.headers.get('webhook-signature');
if (!svixId || !svixTimestamp || !svixSignature) {
return new Response('Missing Svix headers', { status: 400 });
}
// MUST get raw body as text — not parsed JSON
const body = await req.text();
let event;
try {
const wh = new Webhook(webhookSecret);
event = wh.verify(body, {
'webhook-id': svixId,
'webhook-timestamp': svixTimestamp,
'webhook-signature': svixSignature,
});
} catch (err) {
console.error('Clerk webhook verification failed:', err.message);
return new Response('Invalid signature', { status: 401 });
}
console.log('Clerk event:', event.type, event.data?.id);
return new Response(JSON.stringify({ received: true }), { status: 200 });
}Header capture → raw payload hash → expected vs received comparison → replay test.
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.