X-DSS-Signature header. Verify it on
every request before doing anything with the body — a missing or invalid
signature means the request didn’t come from us (or it’s a replay).
The signature
t— Unix timestamp (seconds) of when we signed.v1— HMAC-SHA256 of<t>.<raw-body-bytes>, encoded as lowercase hex.
client_secret. Rotate it independently via your DSS
account contact.
The verification rules
A verifier must enforce all four:- Parse the header. If
torv1is missing or malformed, reject with400. - Reject stale events. If
|now - t| > 300 seconds, reject. This is the replay protection window. - Recompute HMAC-SHA256 of
f"{t}.".encode() + raw_body_bytesusing the signing secret. Compare in constant time againstv1. - Reject mismatches. Don’t trust any field of the body until the signature checks.
Python
Copy this verbatim:FastAPI handler
Flask handler
Tests against the spec
Our canonical fixture:t=1716714840,v1=99d56ccfe6de640971036fc31a8bb476415322e6b687301c96fe15ac81e3fcff
only within 5 minutes of the timestamp. If you’re testing offline, freeze
the clock at 1716714840.
Node
Express handler
Next.js Route Handler (App Router)
What you must do (checklist)
1
Verify the signature on every request
No verified signature, no body parsing. Return
400.2
Enforce the 5-minute replay window
Even with a valid signature, reject events older than 300 seconds.
3
Compare in constant time
hmac.compare_digest (Python) or crypto.timingSafeEqual (Node).
String == is wrong.4
Dedupe on event id
At-least-once delivery means you will see duplicates. The
id is stable
across attempts — store the last N you’ve seen in Redis or your DB.5
Respond 2xx within 5 seconds
Hand off heavy work to a background queue. Acknowledging fast keeps you
out of the retry schedule.
6
Always return 2xx on duplicates
A 4xx on a duplicate looks like a delivery failure to us and triggers
pointless retries.
What you must not do
- Do not trust the body before the signature checks.
- Do not verify against a re-serialised body. The signature is over the bytes we sent, in the order we sent them.
- Do not log the signing secret. Treat it like any other long-lived credential.
- Do not allow
http://for your webhook URL outside local development. We won’t deliver to plain HTTP in production.

