Cosmovex Tools

Clerk webhook verification failing? Inspect the Svix delivery and check the signature

Clerk with Hookwatch Webhook Tester

This inbox is set up for Clerk webhooks, which Clerk delivers through Svix. Add it as an endpoint, send a user.created example from the Testing tab, and the request appears here with its svix-id, svix-timestamp and svix-signature headers. The Signature tab rebuilds id.timestamp.body, keys the HMAC with your base64 whsec_ secret and checks the five-minute window.

  1. Copy the inbox URL. It ends in /clerk so Clerk deliveries are easy to tell apart.
  2. In the Clerk Dashboard open Webhooks → Add Endpoint, paste the URL and subscribe to events such as user.created, user.updated and user.deleted.
  3. Use the endpoint's Testing tab to send an example event, sign up a test user, or press Send a signed test Clerk event here first.
  4. Select the request. The Signature tab detects svix-signature and reads svix-id and svix-timestamp.
  5. Paste the endpoint's whsec_ Signing Secret. Valid here but failing in your app usually means your route reads parsed JSON, or the route is protected by clerkMiddleware.
clerk webhook signature verification clerk webhook testerclerk webhook signing secretclerk webhook secretclerk webhook eventsclerk webhook localhost
Open Hookwatch Webhook Tester Free · Pro $5/mo · no account

What to know

Clerk sends webhooks through Svix, which follows the Standard Webhooks scheme. The signed content is the svix-id header, a dot, the svix-timestamp header, a dot and the raw body. The key is not the whsec_ string itself: strip the whsec_ prefix and base64-decode the rest, then compute HMAC-SHA256 and base64-encode the result. svix-signature holds one or more space-separated values like v1,<base64>; any one matching is enough, which is how secret rotation works without downtime.

The most common Clerk-specific failure is not the signature at all. clerkMiddleware protects routes, and a webhook request has no Clerk session, so a protected /api/webhooks route redirects or returns 401 before your handler runs. Make the webhook route public in the middleware matcher. After that, read the body as text before parsing (await req.text() in a Next.js route handler), and use verifyWebhook from Clerk's SDK, which reads CLERK_WEBHOOK_SIGNING_SECRET, or the svix package's Webhook.verify with the three headers.

Development and production instances have separate endpoints and secrets, so a secret copied from the development dashboard never verifies a production delivery. Failed deliveries are retried on a schedule of roughly 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours, and can be replayed from the endpoint's message log. Use svix-id as the idempotency key, because a retry keeps the same id.

Webhooks are the usual way to copy Clerk users into your own database, but they are asynchronous: the user can reach your app before user.created has been processed. Treat the webhook as a sync, not as a gate on the first request. The Body tab shows the full user object, with email_addresses, primary_email_address_id, external_accounts and public_metadata, so you can check which fields your insert reads before you deploy.

Updated · Cosmovex

Questions

Where is the Clerk webhook signing secret?

In the Clerk Dashboard open Webhooks, select the endpoint and reveal Signing Secret. It starts with whsec_. Development and production instances have separate endpoints, so copy it from the instance that sent the request.

Why does my Clerk webhook return 401 or redirect?

The webhook route is protected by clerkMiddleware. Webhook requests carry no session, so mark the route as public in the middleware matcher and verify the signature in the handler instead.

Why is the Clerk signature invalid with the right secret?

The handler is hashing a re-serialised body or reading the wrong headers. Verify against the raw text of the request with svix-id, svix-timestamp and svix-signature. If this page shows Valid with your secret, the secret is right and the body handling is the problem.

Can I test Clerk webhooks without ngrok?

Yes, for seeing what Clerk sends: this URL is public, so Clerk can deliver to it directly. To run a delivery against your local handler, use Copy as curl (free) and run it in a terminal, or with Pro use Replay to send the stored request to localhost from the browser.

Does the timestamp matter?

Yes. Svix-based libraries reject deliveries whose svix-timestamp is more than five minutes from the current time, and the tool shows that check against the time the request reached the inbox. Replaying an old request to your server will fail for that reason even with a valid signature.

The free plan covers everything on this page. Hookwatch Webhook Tester Pro ($5/mo, billed monthly) is described on the Hookwatch Webhook Tester page.