Headers sent with every webhook
Each webhook delivery includes three security headers:| Header | Description |
|---|---|
X-Webhook-Signature | HMAC-SHA256 signature in the format sha256=<hex_digest> |
X-Webhook-Timestamp | Unix timestamp (seconds) of when the request was signed |
X-Webhook-ID | Unique delivery identifier for this event |
Verification algorithm
To verify the signature, you reconstruct the signed string from the timestamp and raw request body, compute the expected HMAC, and compare it to the value inX-Webhook-Signature.
Read the headers
Extract
X-Webhook-Signature and X-Webhook-Timestamp from the incoming request headers.Build the signed string
Concatenate the timestamp and the raw request body separated by a period:Use the raw request body bytes — do not parse JSON first. Parsing and re-serializing the body can alter its byte representation and cause the signature check to fail.
Compute the expected signature
Compute an HMAC-SHA256 of the signed string using your webhook secret as the key, then hex-encode the result.
Compare signatures
Prefix your computed digest with
sha256= and compare it to the value of X-Webhook-Signature. Use a constant-time comparison to prevent timing attacks.Code examples
Security best practices
Always use the raw request body
Always use the raw request body
Compute the signature against the raw bytes of the request body, not a parsed and re-serialized version. JSON serializers can reorder keys or change whitespace, which will break signature verification.
Validate the timestamp to prevent replay attacks
Validate the timestamp to prevent replay attacks
An attacker who intercepts a valid signed request could resubmit it later. Rejecting requests with timestamps older than 5 minutes eliminates this risk without affecting legitimate deliveries.
Use constant-time comparison
Use constant-time comparison
Use
hmac.compare_digest (Python), crypto.timingSafeEqual (Node.js), or hash_equals (PHP) instead of == when comparing signatures. Standard string comparison can leak timing information that helps an attacker forge signatures.Store your webhook secret securely
Store your webhook secret securely
Keep your webhook secret in an environment variable or a secrets manager. Never hardcode it in source code or commit it to version control.