Webhook Signatures
Verify HMAC-SHA256 signatures on incoming webhook deliveries.
Every outbound webhook delivery is signed with HMAC-SHA256. Your handler must verify the signature before trusting the payload. Without verification, any attacker who knows your endpoint URL could POST arbitrary JSON to it.
Headers
SurfacedBy sets two headers on every delivery:
X-SurfacedBy-Timestamp- Unix epoch seconds at signing time.X-SurfacedBy-Signature-t=<timestamp>,v1=<hex-hmac-sha256>.
The signature is computed over the concatenation of the timestamp, a single dot, and the raw request body. Using the raw bytes (not a re-serialised JSON object) matters: any whitespace difference invalidates the signature.
Verification algorithm
- Read the
X-SurfacedBy-Signatureheader and parse out thev1=segment. - Read the
X-SurfacedBy-Timestampheader. - Reject the request if the timestamp is older than your tolerance window (5 minutes is a sensible default). This prevents replay attacks.
- Compute
HMAC-SHA256(secret, timestamp + "." + raw_body)and hex-encode it. - Compare the computed value to the
v1=segment using a constant-time comparison.
If any step fails, return 400 or 401 and do not process the payload.
Python
Node.js
PHP
Storing the secret
Signing secrets are shown once when you register an endpoint and are not recoverable afterwards. Store them in a secret manager. Rotate by registering a new endpoint with a fresh secret, cutting traffic over, then deleting the old endpoint.