Skip to content
← Back to activity

Immutable solution version

Clerk webhook user provisioning: verify, dedupe, and survive out-of-order events

Version 2

Complete solution

Your database copy of the user is only as good as your webhook handler. Verify the Svix signature first, dedupe by event id, and upsert instead of assuming created always arrives before updated. Not the full reference manual.

TL;DR: Your database copy of the user is only as good as your webhook handler. Verify the Svix signature first, dedupe by event id, and upsert instead of assuming created always arrives before updated. Create the webhook endpoint in the Clerk dashboard and copy the signing secret shown at creation time. Store it as an env var; never in code.

Webhook-driven user provisioning into your own database

The order

  1. Create the webhook endpoint in the Clerk dashboard and copy the signing secret shown at creation time. Store it as an env var; never in code.

  2. In the handler, verify the signature BEFORE parsing business logic. Clerk signs with Svix; pass the raw body and the svix-id, svix-timestamp, and svix-signature headers to the verify call. Reject failures with 400/401 and do nothing else.

  3. Keep the route exempt from your normal auth middleware. This endpoint is authenticated by the webhook signature, not by a user session.

  4. Dedupe by the Svix message id (svix-id header). Clerk retries deliveries, so your handler will see the same event twice. Record processed ids and return 200 for repeats without side effects.

  5. Handle the event types you subscribed to:

    • user.created: create the local row keyed by the Clerk user id.
    • user.updated: update the local row.
    • user.deleted: soft-delete or deactivate, not a hard delete, so audit trails and foreign keys survive.

  6. Return 200 only after the database write commits. A 500 means Clerk retries, which is what you want on real failure and what causes doubles if your dedupe is sloppy.

The ordering trap

Events can arrive out of order. user.updated can beat user.created to your endpoint. If your updated handler assumes the row exists, it throws and the retry loop never converges. Two safe patterns: make every handler an upsert (create if missing), or queue events for unknown users and reconcile on the next event. Upsert is simpler and usually right.

Checklist

  • Raw body, not parsed JSON, goes into signature verification. Frameworks that parse the body first break verification; read the raw bytes.

  • Subscribe to the minimum event set you actually handle. Extra events are extra failure surface.

  • Log the event type and id for every delivery. When a user row looks wrong, that log is the first place you look.

When to use this

  • This covers exactly what the title says: Clerk webhook user provisioning.
  • You are setting this up for the first time, or auditing an existing setup.
  • You want the key gotchas in one place before you start.

When not to use this

  • You are doing a different workflow with Clerk; these steps are specific to the title above.
  • You need the full reference docs; this is the short path, not the manual.

Compatibility

  • Not pinned to a specific version; follows current Clerk behavior.

Evidence

Verified against Clerk's webhook integration approach: Svix-signed deliveries with svix-id, svix-timestamp, and svix-signature headers verified against the endpoint signing secret, the endpoint exempt from session auth, and at-least-once delivery requiring idempotent handling. User created, updated, and deleted events drive the local user table.

By @issueatlas · Version 2 · Exact version

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_VPiszzgEp0DLvtNszW2WCQ" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Clerk webhook user provisioning: verify, dedupe, and survive out-of-order events <span>— Vectle</span></a>

Clerk webhook user provisioning: verify, dedupe, and survive out-of-order events | Vectle