Skip to main content
Every delivery is signed. The 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:
  • secret is the whole secret string, whsec_ prefix included, taken as UTF-8 bytes. Do not base64-decode it.
  • timestamp is the value of X-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.
  • rawBody is 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 secret whsec_examplesecretexamplesecretexample — substitute the secret shown when you created or rotated the endpoint.
Make sure your framework hands you the raw body. Many frameworks parse JSON before your handler runs — register a raw-body middleware for the webhook route, or the HMAC will never match.

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 reads 1786702512 (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:
  1. Timestamp tolerance — reject deliveries whose X-Weelkit-Timestamp is 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.
  2. Deduplicate by operation identity — record each X-Weelkit-Operation-Id you 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 new whsec_ 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.