BlogClerk Webhooks

How to Test Clerk Webhooks

Clerk webhooks send user, session, and organization lifecycle events so you can sync identity data to your own systems.

Looking for the broader picture? See the 7 best webhook testing tools (2026), or if you're already on Webhook.site, the 60-second migration to HookRay.

Clerk Official Webhook Docs

1. Clerk Webhook Events

Clerk can send the following webhook events to your endpoint:

user.created
user.updated
session.created
session.ended
organization.created
organizationMembership.created

2. Set Up a Test Endpoint with HookRay

Follow these steps to start receiving Clerk webhooks for testing:

  1. Go to HookRay and click "Start Testing — Free" to get your unique webhook URL.
  2. Copy the URL (e.g., https://h.hookray.com/abc123).
  3. In your Clerk dashboard, navigate to the webhook settings and paste the HookRay URL as your endpoint.
  4. Select the events you want to receive (see list above).
  5. Trigger a test event — HookRay will show the incoming webhook in real-time.

3. Sample Clerk Webhook Payload

Here's an example of what a Clerk webhook payload looks like:

payload.json
{
  "type": "user.created",
  "data": {
    "id": "user_2mK3vP2a91",
    "first_name": "Ava",
    "last_name": "Johnson",
    "email_addresses": [
      {
        "email_address": "ava@example.com"
      }
    ]
  },
  "object": "event",
  "timestamp": 1763582500000
}

4. How to Verify Clerk Webhook Signatures

Signing details
Algorithm
HMAC-SHA256 (Svix)
Header
svix-signature
Encoding
base64

Clerk uses Svix under the hood. Three headers are required: `svix-id`, `svix-timestamp`, `svix-signature`. Signed string is `{svix-id}.{svix-timestamp}.{raw_body}`. The signature header may contain multiple space-separated `v1,...` versions — verify each. Your signing secret has a `whsec_` prefix that you strip before base64-decoding.

Node.js (Express)

// npm install svix — official SDK that handles header parsing.
import { Webhook } from 'svix';
import express from 'express';

const app = express();

app.post(
  '/webhooks/clerk',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const wh = new Webhook(process.env.CLERK_WEBHOOK_SECRET!); // 'whsec_...'
    try {
      const event = wh.verify(req.body, {
        'svix-id': req.headers['svix-id'] as string,
        'svix-timestamp': req.headers['svix-timestamp'] as string,
        'svix-signature': req.headers['svix-signature'] as string,
      });
      // event.type, event.data — verified
      res.json({ ok: true, type: (event as { type: string }).type });
    } catch {
      res.status(403).send('invalid signature');
    }
  },
);

Python (FastAPI)

# pip install svix — official SDK.
import os
from svix.webhooks import Webhook, WebhookVerificationError
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhooks/clerk")
async def clerk_webhook(request: Request):
    body = await request.body()
    headers = {
        'svix-id': request.headers.get('svix-id'),
        'svix-timestamp': request.headers.get('svix-timestamp'),
        'svix-signature': request.headers.get('svix-signature'),
    }
    try:
        wh = Webhook(os.environ['CLERK_WEBHOOK_SECRET'])  # 'whsec_...'
        event = wh.verify(body, headers)
    except WebhookVerificationError:
        raise HTTPException(status_code=403, detail='invalid signature')
    return {'ok': True, 'type': event.get('type')}
Watch out: Clerk webhooks are Svix-compatible. The signature header can contain MULTIPLE space-separated signatures (during key rotation) — accept the request if ANY signature matches. The official Svix SDK handles all of this; rolling your own verifier is a common bug source. Reject timestamps outside ±5 minutes for replay protection.

Capture a real Clerk webhook with HookRay first, then replay the captured request against your verifier locally — that way you can iterate on the verification code without re-triggering events in Clerk. Read Clerk's official signing docs for the canonical reference, or see the cross-service signature verification guide for Ruby and timing-safe comparison patterns.

5. Frequently Asked Questions

How do I test Clerk webhooks without deploying?

Use HookRay to get an instant public webhook URL. Paste it into your Clerk dashboard's webhook configuration, trigger an event, and watch the payload arrive in real time. No code, no ngrok, no deployment required. The free tier captures 100 requests per month and works on all Clerk event types.

Why aren't my Clerk webhooks arriving?

The four most common causes: (1) the endpoint URL isn't publicly accessible — Clerk can't reach localhost; (2) the wrong events are subscribed in your Clerk dashboard; (3) signature verification is rejecting the request before your handler runs; (4) Clerk can't reach your server because of a firewall, expired SSL certificate, or wrong DNS. Use HookRay's URL to isolate which of these four is failing — if HookRay receives the webhook, the problem is in your handler. If HookRay doesn't, the problem is in Clerk configuration.

Why am I getting 400 or 500 errors from my Clerk webhook?

Clerk reports the response status your endpoint returned. HookRay accepts any payload and returns 200 OK by default, so if you see 400/500 in your Clerk dashboard while pointing at HookRay, the issue is in Clerk's configuration (wrong event, malformed signing secret, etc.). If you point at your own endpoint and get 400/500, the issue is in your handler — capture the request with HookRay, replay it locally, and debug from the captured payload.

How do I verify Clerk webhook signatures?

Clerk signs each webhook request with a shared secret. Capture the raw headers and body using HookRay, then verify the signature in your application using Clerk's SDK or a standard HMAC library. Once verification works against HookRay-captured data, you can safely deploy. Clerk's docs (linked above) cover the exact signing algorithm.

Can I replay a captured Clerk webhook?

Yes — HookRay's replay feature re-sends any captured webhook to a different endpoint with one click. This is the fastest way to fix a buggy handler: capture the payload once, fix your code, and replay until it works. No need to re-trigger the event in Clerk.

6. Next Steps

  • Use HookRay's webhook replay feature to re-send captured webhooks while building your handler
  • Enable smart parsing (Pro plan) to see Clerk-specific fields highlighted automatically
  • Check the Clerk webhook documentation for the complete event reference

Ready to test Clerk webhooks?

Get a free webhook URL in 5 seconds. No signup required.

Start Testing Clerk Webhooks — Free

Free PDF: Webhook Testing Cheat Sheet 2026

One-page reference for 50+ APIs — canonical events, signing methods, sample payloads. Print it, pin it, share it.

📄 Download the cheat sheet (PDF, 180KB)