Dugble signs every webhook request with the signing secret shown when you create
or rotate an endpoint. Verify the signature before decoding JSON or applying
any side effects.
Use the complete secret, including its whsec_ prefix, as the HMAC key.
Do not Base64-decode the characters after the prefix.
Signature contract
The X-Dugble-Signature header has this format:
t is the delivery attempt time in Unix seconds. v1 is the lowercase
hexadecimal encoding of an HMAC-SHA256 digest.
To calculate the expected digest:
- Read the request body as its original bytes. Do not parse and re-serialize it.
- Build the signed payload by concatenating the decimal timestamp, one ASCII
period (
.), and the raw body bytes.
- Compute HMAC-SHA256 with the complete endpoint signing secret as the key.
- Hex-encode the digest in lowercase.
- Compare the received and expected digests with a constant-time function.
- Reject timestamps more than five minutes away from your server time.
In bytes, the signed payload is:
TypeScript example
This Express example installs a route-specific raw-body parser. Register it
before any application-wide JSON parser that would consume the same request.
Python example
Test vector
Use this deterministic vector to test your implementation. The timestamp is
intentionally old, so disable the replay-window check for this fixture only.
Operational guidance
- Return
400 for a malformed, stale, or invalid signature.
- Store the event ID with a unique constraint because delivery is at least once.
- Keep endpoint secrets out of logs, analytics, and error reports.
- Rotating an endpoint secret immediately makes the newly displayed secret the
key for subsequent delivery attempts. Update your receiver before testing it.