X-Kash-Signature, and the timestamp t= is in Unix milliseconds, not seconds.
The signature format
During the secret rotation overlap window the header may carry two
v1= entries (current + previous secret). Accept the request if any matches.
The verification algorithm
- Read the raw request body as a string. Don’t reparse JSON — the signature is over the exact bytes that were sent.
- Parse the header: split on
,, then on=, into a map. - Read
t(the timestamp). If|now - t| > 5 minutes, reject — the signature is too old (replay attack). - Compute
expected = HMAC_SHA256(secret, "${t}.${rawBody}")and hex-encode it. - For each
v1=entry in the header, compare withexpectedusing a constant-time comparator. If any matches, accept.
400 to a request that fails verification: the API does not retry a 400.
Reference implementations
TypeScript / Node.js (the SDK does this for you)
kash.webhooks.verifySignature(body, header, secret) returns { valid, reason } without parsing, if you only need the check.
TypeScript / Node.js (no SDK)
Python
Go
Rust
Common mistakes
- Re-serialising the JSON body before verifying. The signature is over the exact bytes we POSTed. If you parse + re-stringify, whitespace differs and the signature fails. Always verify against the raw body, then parse JSON.
- Using
==instead of constant-time comparison.==short-circuits on the first differing byte and leaks signature bytes via timing. Usecrypto.timingSafeEqual(Node),hmac.compare_digest(Python),hmac.Equal(Go),ct_eq(Rust). - Skipping the timestamp freshness check. Without
tvalidation, an attacker who captures one signed payload can replay it forever. The 5-minute tolerance handles legitimate clock skew. - Ignoring multiple
v1=entries. During the rotation overlap window we send two — accept either. A picky verifier rejects valid traffic mid-rotation.
Testing your verifier locally
The CLI can sign a payload the same way the API does (t=<unix-ms>,v1=<hex>) and POST it to your endpoint:
--timestamp-ms to test your freshness check, and use kash webhooks verify to check a signature offline.
Reporting verification problems
If deliveries fail to verify, report it from the info button in the Kash web app → Feedback → Report a bug, with the rawX-Kash-Signature header, the X-Kash-Event-Id, and your verification code. Never send the secret itself. Most failures come from verifying a re-serialised body instead of the raw bytes.
Next
Retries & Redelivery
What happens when your endpoint fails — and how to manually replay.
Secret Rotation
Rotate without losing a single delivery.