Skip to main content
Anyone who finds your endpoint’s URL can send it requests. Every request from Relay carries a signature made with your endpoint’s signing secret, which only you and Relay know. Check it before you act on the event. The signing secret starts with whsec_. It is shown once when you create the endpoint; find or roll it later under Webhooks → your endpoint → Signing secret.

How it works

Sequence diagram between Relay and the customer's webhook server: Relay computes HMAC-SHA256 over the timestamp and raw body with the signing secret and sends it in the Relay-Signature header; the server reads the raw body, rejects timestamps older than 5 minutes, recomputes the HMAC, compares it in constant time, and answers 200 on a match or 400 otherwise.

Relay signs the timestamp and the raw body with the endpoint's secret. Your server checks the timestamp, recomputes the signature and compares.

The header holds a timestamp and the signature:
  1. Split the header on , and read t and v1.
  2. Reject the request if t is more than 5 minutes from your server’s clock. This stops someone resending a request they captured.
  3. Compute HMAC-SHA256 of {t}.{raw body} with your signing secret, as lowercase hex.
  4. Compare it with v1 in constant time. Answer 400 if they differ.

Use the raw body

Sign and compare the bytes exactly as they arrived. If your framework parses JSON first, the re-serialized body can differ by a space or a key order and the signature won’t match. In Express, use express.raw({ type: "application/json" }) on the webhook route.

Example

The Node SDK does this in relay.webhooks.verify.

Rolling the secret

When you roll a secret, Relay signs with both the old and the new one for 24 hours and sends two v1 values in the header. Accept the request if either matches, and deploy the new secret within the day.