The signing secret
Each endpoint has its own secret. It starts withwhsec_.
- In the dashboard: shown when you add the endpoint, and again any time on the endpoint’s page with Show secret. Only admins can see it.
- With the API: returned once, in the response of
POST /v1/webhook-endpoints.
The signature header
Every request carries aCiarem-Signature header:
The signature is computed over the timestamp, a dot, and the raw request body, using your signing secret as the key:
How to verify
1
Read the raw body
Take the request body exactly as the bytes arrived, before any JSON parsing. Parsing and re-serializing changes spacing and character escaping, and the signature will no longer match.
2
Split the header
Split
Ciarem-Signature on , and then on = to get t and v1.3
Compute the expected signature
Build the message
t + "." + raw body and compute its HMAC-SHA256 with your signing secret. Use the whole secret, including the whsec_ prefix, as it is.4
Compare safely
Compare your result with
v1 using a constant-time comparison function, not ==.5
Check the timestamp
Reject the request if
t is more than 5 minutes away from your server’s clock. This stops someone from capturing a real request and replaying it later.400 and do nothing with the body.
Code you can copy
Check your code with a known example
Use these values to confirm your verification code computes the right signature, without waiting for a real event. Skip the timestamp check while you run this, since the example’s timestamp is in the past.
You can produce the same value from a terminal:
When verification fails
Good practices
- One secret per endpoint. If you run staging and production servers, give each its own endpoint so a leaked staging secret cannot forge production events.
- Rotate by replacing the endpoint. A secret cannot be changed in place. To rotate, add a new endpoint with the same URL and events, update your server to accept the new secret, then delete the old endpoint. While both exist, you receive each event twice, once per endpoint, with the same event
id, so deduplicating onidkeeps this harmless. - Every retry is signed again. A retried delivery has the same body and event
idbut a newtand a new signature, so the 5-minute check works for retries too. - Verification proves origin, not uniqueness. A verified event can still be a duplicate. Deduplicate on
idas well.