The signature is an HMAC-SHA256 over
{webhook-id}.{webhook-timestamp}.{raw body}. The key is your secret without its whsec_ prefix, base64 decoded.
To verify a delivery:
- To block replayed requests, refuse a timestamp more than 5 minutes away from your server’s clock.
- Compute the signature over the id, the timestamp and the raw body.
- Accept the delivery when any
v1signature in the header matches the computed one, comparing in constant time.
Verification rules
- Compare in constant time (
timingSafeEqual,compare_digest), never with==. - Check the timestamp. Without the check, a captured delivery could be sent again later and still verify.
- Keep one secret per webhook on your server. The dashboard shows the secret when a webhook is created or its secret is rotated, and the API returns it in the subscribe response.
- Refuse a mismatch with 401 and don’t process the body. A mismatch points to a bug in raw body handling, or to someone else calling your endpoint.
- Drop duplicates by
webhook-id. It stays the same on every retry and resend of a delivery.