WorkOS · event: user.created ·
content type: application/json
This is a representative sample — fake IDs, real structure. To see a payload with your data, point the provider at a capture URL (button below).
One click creates a free capture bin with this exact payload already in it — pretty-printed, headers inspectable, replayable to your own endpoint:
Then paste the bin's URL into WorkOS's webhook settings to capture a real delivery. No signup.
content-type: application/json workos-signature: t=1788092049617,v1=da1dcde4857ffc36ec11afa3d75c6428dadb3a4acef5e874e83c45a505b7e169
{
"id": "event_01K3W9YQ5H8Z2M4T6R7XVBNP0D",
"event": "user.created",
"data": {
"object": "user",
"id": "user_01K3W9YQ4C2F8J6P0S5DZHTMRE",
"email": "marcelina.davis@example.com",
"first_name": "Marcelina",
"last_name": "Davis",
"email_verified": true,
"profile_picture_url": null,
"external_id": null,
"metadata": {},
"created_at": "2026-08-30T12:14:09.512Z",
"updated_at": "2026-08-30T12:14:09.512Z"
},
"created_at": "2026-08-30T12:14:09.617Z"
}
WorkOS-Signature: t=<ms>,v1=<hex> — hex HMAC-SHA256 over
timestamp.rawBody, key = the endpoint's webhook secret from the WorkOS
dashboard (sample here: hookden_workos_sample_webhook_secret). It's
Stripe's exact wire format under WorkOS's header except t is in
milliseconds. Try the signature
debugger (WorkOS provider) or set the WorkOS scheme on a capture bin for live
✓/✗ badges.now() - t < 300 — in seconds. With t in milliseconds
that comparison rejects every genuine delivery (or, worse, divides by 1000 twice and
accepts everything). The HMAC itself uses t verbatim, so signatures still
match while your timestamp check fails — "signature valid, tolerance exceeded" is
the tell. WorkOS SDKs default tolerance to 3–5 minutes.WorkOS-Signature but most servers normalize to
workos-signature — their docs call this out; exact-case header lookups
miss it.data carries the complete resource, and WorkOS explicitly does
not guarantee ordering (e.g. dsync.group.user_added can beat
dsync.user.created). Upsert from the payload, compare the object's
updated_at against what you've stored so a retried older event can't
overwrite newer data, and dedupe on the event id — the same event can
be delivered more than once.Reproduce this delivery against any endpoint (your handler, a bin, staging):
$ curl -X POST https://your-endpoint.example/hook \
-H 'content-type: application/json' \
-H 'workos-signature: t=1788092049617,v1=da1dcde4857ffc36ec11afa3d75c6428dadb3a4acef5e874e83c45a505b7e169' \
-d '{
"id": "event_01K3W9YQ5H8Z2M4T6R7XVBNP0D",
"event": "user.created",
"data": {
"object": "user",
"id": "user_01K3W9YQ4C2F8J6P0S5DZHTMRE",
"email": "marcelina.davis@example.com",
"first_name": "Marcelina",
"last_name": "Davis",
"email_verified": true,
"profile_picture_url": null,
"external_id": null,
"metadata": {},
"created_at": "2026-08-30T12:14:09.512Z",
"updated_at": "2026-08-30T12:14:09.512Z"
},
"created_at": "2026-08-30T12:14:09.617Z"
}'