Skip to main content
Courier signs every outbound webhook request. Check the signature before you act on an event, so a forged request can’t reach your handler.

Read the header

Each destination gets a whsec_... secret when you create it. Courier signs every request with HMAC-SHA256 in a courier-signature header:

Compare the signature

Split the header on , and = to read t, a millisecond timestamp, and signature. Compute HMAC_SHA256(secret, "<t>.<raw_body>") and compare it to signature in constant time:
Sign against the raw request body, not a re-serialized object, so the bytes match what Courier hashed. Retries and timeouts are covered in .

Limits & behavior

  • Verify against the raw body. Re-serializing the JSON changes the bytes, so the signature won’t match.
  • Use a constant-time comparison. A plain string comparison leaks timing information.

FAQ

Verify the courier-signature header. Compute HMAC_SHA256(secret, "<t>.<raw_body>") with your webhook’s whsec_ secret and compare it to the header’s signature. Reject anything that does not match.
Almost always because the body was parsed and re-serialized before hashing. Hash the raw bytes of the request instead.