X-Weelkit-Signature header carries v1= followed by the lowercase hex HMAC-SHA256 of the string {timestamp}.{raw body}, keyed with your endpoint’s signing secret:
secretis the whole secret string,whsec_prefix included, taken as UTF-8 bytes. Do not base64-decode it.timestampis the value ofX-Weelkit-Timestamp(unix seconds). It is regenerated on every attempt, including retries, so the tolerance window below also holds for a retry that happens hours later.rawBodyis the exact request body bytes. Compute the HMAC before any JSON parsing or re-serialization — a parsed-and-re-encoded body will not match.- Compare signatures with a constant-time comparison, never
==on strings in languages where that short-circuits.
Verify a delivery
Both examples use the placeholder secretwhsec_examplesecretexamplesecretexample — substitute the secret shown when you created or rotated the endpoint.
Check your verifier against a known request
This request is signed with the placeholder secret used above. Your verifier must accept it when its clock reads1786702512 (12 seconds after the timestamp), and must refuse it when any byte of the body, the timestamp or the signature changes.
signature-vector
Replay protection
Signature verification alone does not stop an attacker from re-sending a captured, validly signed request later. Layer two checks on top:- Timestamp tolerance — reject deliveries whose
X-Weelkit-Timestampis older than your tolerance window (5 minutes is a sensible default, as in the examples above). The timestamp is covered by the signature, so it cannot be forged. - Deduplicate by operation identity — record each
X-Weelkit-Operation-Idyou have processed and skip duplicates. Weelkit’s retries deliberately reuse the same operation identity, so this one check handles both replays and legitimate retries: process once, acknowledge every time.
Secret rotation
Rotating an endpoint’s secret bumps its secret version and issues a newwhsec_ value, shown once. The old secret stops signing at the moment of rotation: every later attempt, including the retry of an older delivery, is signed with the new secret only. A delivery your receiver refuses because it still holds the old secret is a definitive rejection (a 4xx) and is not retried.
To roll without refusing deliveries, deploy a verifier that accepts both secrets before you rotate, rotate, then drop the old secret. Accept whichever of the two matches.