Skip to main content
Every webhook delivery is signed. Verify the signature before you parse the JSON body or act on the event — this is what protects your endpoint from forged requests, detects payload tampering, and rejects replayed deliveries. Webhook deliveries are signed, adding these headers to every request:
The signing secret returned by POST /webhooks/subscriptions (and by rotation) looks like whsec_.... That prefixed value is base64 — decode it before using it as the HMAC key.

Verification Steps

  1. Read webhook-id, webhook-timestamp, and webhook-signature from the request headers.
  2. Build the signed content by concatenating the message ID, the timestamp, and the raw request body, separated by dots: {webhook-id}.{webhook-timestamp}.{body}. Use the exact raw bytes/string you received — do not parse and re-serialize the JSON first, since that can change field order or whitespace and break the signature.
  3. Base64-decode the signing secret (after stripping the whsec_ prefix) and compute the HMAC-SHA256 digest of the signed content using it as the key.
  4. Base64-encode the digest and compare it against each v1 value in webhook-signature using a constant-time comparison. The header can contain multiple space-separated signatures (for example during secret rotation); accept the delivery if any one of them matches.
  5. Reject the request if no signature matches, or if webhook-timestamp is older than 5 minutes (or further in the future than your allowed clock skew), to prevent replay attacks.
  6. Only after verification succeeds, parse the JSON body and process the event.
Respond with a 2xx status once the event has been safely accepted (even if asynchronous processing is still pending) — see Delivery Semantics for retry behaviour.

Code Examples

Store the signing secret securely and never expose it in client-side code or logs. If you suspect it has been compromised, rotate it with POST /webhooks/subscriptions/{subscriptionId}/secret.
If your stack already has a library for verifying webhook-id / webhook-timestamp / webhook-signature style signatures, prefer it over a hand-rolled implementation — it already handles edge cases such as multiple signature versions and secret encoding.