Use this file to discover all available pages before exploring further.
Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature. You must verify this signature before processing the payload — otherwise anyone who discovers your endpoint URL could send fake events.
You need the raw body string exactly as it was received — not a parsed-and-re-serialized version. Most frameworks provide a way to access this.
If you parse the JSON body and then JSON.stringify() it, the output may differ from the original (different key ordering, whitespace). This will cause signature verification to fail. Always use the raw body.
3
Build the signing string
Concatenate the timestamp, a literal . character, and the raw body:
Create an HMAC-SHA256 hash of the signing string using your signing secret, then hex-encode it and prepend sha256=.
5
Compare using a timing-safe function
Compare your computed signature with the X-Webhook-Signature header using a timing-safe comparison function. Do not use === or == — these are vulnerable to timing attacks.If the two values match, the webhook is verified.
The X-Webhook-Timestamp header is included in the signed data, so it cannot be forged. You can optionally reject requests where the timestamp is too old:
const MAX_AGE_MS = 5 * 60 * 1000; // 5 minutesfunction isTimestampValid(timestamp) { const age = Date.now() - new Date(timestamp).getTime(); return age >= 0 && age <= MAX_AGE_MS;}
Re-serialized body: You parsed the JSON and re-stringified it. Use the raw body exactly as received.
Wrong secret: Each webhook subscription has its own unique signing secret. Make sure you’re using the correct one.
Middleware modified the body: Some frameworks (e.g. Express without the verify option) consume the raw body during JSON parsing. Use the verify callback shown above to preserve it.
Encoding issues: The raw body must be read as UTF-8. Binary or other encodings will produce a different hash.
I lost my signing secret
Signing secrets are shown only once at creation time. If you’ve lost yours, go to Settings > Webhooks, edit the subscription, and click Rotate signing secret. A new secret will be generated and shown once. Update your server with the new secret — the old one is immediately invalidated.
How do I test locally?
Use a tunneling tool like ngrok to expose your local server, then register the tunnel URL as your webhook endpoint. You can also use the Test button in the dashboard to send a test event.