How to Test Clerk Webhooks Locally
What Webhooks Does Clerk Send?
Clerk is an authentication and user management platform that sends webhooks for user lifecycle events. Key events include user.created, user.updated, user.deleted, session.created, session.ended, session.revoked, organization.created, organization.updated, organizationMembership.created, email.created, and sms.created.
Clerk webhooks use the Svix webhook delivery platform, which means payloads include standard Svix headers: svix-id (unique message ID), svix-timestamp, and svix-signature. The signature is an HMAC-SHA256 computed over the message ID, timestamp, and body, using your webhook signing secret.
The JSON payload structure includes the event type in a type field and the event data in a data object. For user.created, the data contains the full user object with email addresses, phone numbers, metadata, and authentication details.
Setting Up Clerk Webhooks
In the Clerk Dashboard, navigate to Webhooks and click "Add Endpoint." Enter your ReqPour URL, such as https://abc123.reqpour.com/clerk. Select the events you want to subscribe to — for syncing users to your database, choose user.created, user.updated, and user.deleted.
Clerk shows the signing secret for each endpoint. Copy this and store it in an environment variable — you will need it for signature verification. Clerk also provides a "Test" button that sends a sample event to your endpoint, which you can verify in the ReqPour dashboard.
Start the relay to forward events to your local server:
npx reqpour relay --to http://localhost:3000/api/webhooks/clerkNow sign up a test user in your application. The user.created event arrives at your ReqPour endpoint and gets forwarded to your local handler.
Handling Clerk Webhooks in Code
Here is a Next.js API route handler for Clerk webhooks using the Svix library for verification:
import { Webhook } from 'svix';
import { NextRequest, NextResponse } from 'next/server';export async function POST(req: NextRequest) { const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET; const svix = new Webhook(WEBHOOK_SECRET);
const body = await req.text(); const headers = { 'svix-id': req.headers.get('svix-id')!, 'svix-timestamp': req.headers.get('svix-timestamp')!, 'svix-signature': req.headers.get('svix-signature')!, };
let event; try { event = svix.verify(body, headers); } catch (err) { console.error('Webhook verification failed:', err); return NextResponse.json({ error: 'Invalid signature' }, { status: 400 }); }
switch (event.type) {
case 'user.created':
await db.users.create({
clerkId: event.data.id,
email: event.data.email_addresses[0]?.email_address,
name: ${event.data.first_name} ${event.data.last_name},
});
break;
case 'user.updated':
await db.users.update({
where: { clerkId: event.data.id },
data: { email: event.data.email_addresses[0]?.email_address },
});
break;
case 'user.deleted':
await db.users.delete({ where: { clerkId: event.data.id } });
break;
}
return NextResponse.json({ received: true }); } ```
The Svix library handles the timestamp validation and signature verification. During development, the ReqPour dashboard lets you see the exact svix headers and payload for debugging verification issues.
Syncing Users with Clerk Webhooks
The most common use case for Clerk webhooks is syncing user data to your own database. When a user signs up through Clerk, your application needs a corresponding record in your database for application-specific data like preferences, roles, and usage stats.
The user.created event contains the full user object including all email addresses, phone numbers, external accounts (OAuth providers), and any public/private metadata you have set. Use the id field as the foreign key linking your database record to the Clerk user.
Handle the user.updated event to keep your database in sync when users change their profile — name, email, or metadata updates. And handle user.deleted to clean up your database when users are removed from Clerk.
Test the full lifecycle with ReqPour: create a user, update their profile, then delete them. Review the event sequence in the ReqPour dashboard to ensure your handler processes each event correctly and in order.
Clerk Webhook Tips
Clerk's Svix-based delivery system includes automatic retries with exponential backoff. Failed deliveries are retried for up to 72 hours. Use the Clerk Dashboard's webhook logs alongside the ReqPour request inspector to debug delivery issues from both sides.
When testing organization-related events, create organizations and add/remove members in Clerk. The organizationMembership.created and organizationMembership.deleted events include both the organization and user details, which is useful for implementing team-based access control.
For development, Clerk provides a test mode with its own set of API keys and webhook endpoints. Keep your test and production webhook configurations separate. ReqPour makes this easy — use different endpoints for each environment and switch between them in the dashboard.
Related
Get started with ReqPour
Catch, inspect, and relay webhooks to localhost. Free to start, $3/mo for Pro.