POST to your URL.
- Take the raw request body exactly as received. Parsing and re-serialising the JSON changes the bytes and breaks the signature.
- Reject the request if
webhook-timestampis more than five minutes from your clock. - Build the signed content:
`${webhook-id}.${webhook-timestamp}.${body}`. - Compute HMAC-SHA256 over it. The key is your signing secret without the
whsec_prefix, base64-decoded. - Base64-encode the result and compare it, in constant time, with each
v1,entry inwebhook-signature. The header can hold several entries separated by spaces (during a secret rotation); accept if any matches.
Use a Standard Webhooks library
Any Standard Webhooks library verifies Darwin’s signatures, including Svix’s. Pass the raw body, the headers and the secret as Darwin shows it:await Darwin.webhooks.verify(body, headers, secret) does all of this.
Rotate the signing secret
Open the endpoint’s ⋯ menu, select Rotate signing secret and confirm. Darwin shows the new secret once. For the next 24 hours every delivery carries two signatures, one per secret, so your receiver keeps working while you switch it to the new secret. After 24 hours only the new secret signs. With the API:POST /v1/webhook_endpoints/{id}/rotate_secret returns the endpoint with its new secret.