unwrap with the request headers.
Before you start
- Save
signing_secretfrom the subscription’s create response asRELAY_WEBHOOK_SECRET. - Read the raw body before any JSON middleware touches it; changed whitespace changes the signed bytes.
Verify with the SDK
unwrap checks the three webhook headers against the raw body and returns the parsed event. It throws when a header is missing, the signature does not match, or the body is not JSON. Configure the secret once on the client, or pass key per call when one receiver serves several subscriptions:
TypeScript SDK
Read the signature format
Relay signs requests with Standard Webhooks, so any Standard Webhooks library verifies them too. The signing input joins the ID and the timestamp with periods and appends the raw body. Relay strips thewhsec_ prefix, base64-decodes the key, and computes HMAC-SHA256 over that input:
A retry reuses the event ID and body under a new timestamp, so deduplicate by
event_id, never by the signature.
When it fails
Reject the request with401 before you store anything when a header is missing or the signature does not match; delivery policy treats that as terminal. Reject a body that passes the signature but does not parse the same way. If your secret is missing, restore it from your secret store; the receiver example returns 401 for every case.

