Skip to main content
Every delivery carries three headers:

The algorithm

  1. Read the raw request body as bytes/text — do not re-serialise the parsed JSON, or whitespace/key-order differences will break the signature.
  2. Reject the request if now - IC-Timestamp is greater than 300 seconds (5-minute replay window).
  3. Compute HMAC_SHA256(secret, "{IC-Timestamp}.{raw body}") as hex.
  4. Compare it to IC-Signature with a constant-time comparison.
  5. Only if it matches, parse the body and dedupe on IC-Event-Id.
The secret is the whsec_… value shown once when you created the endpoint.

Node.js example

Verify the signature on the raw body, before parsing. Frameworks that auto-parse JSON (Express express.json(), Next.js route handlers) may not expose the raw bytes by default — configure a raw-body reader for your webhook route.

If verification fails

Return a 4xx and do nothing else. A failed signature means the request did not come from us (or was tampered with) — never process it. If legitimate events start failing verification, confirm you’re using the right endpoint’s secret and signing over the raw body.