whsec_...) using HMAC-SHA256. Verify the signature before trusting any payload — an unverified webhook could be forged by anyone who knows your URL.
Header format
t— delivery timestamp in milliseconds.v1— hex HMAC-SHA256 of the string"<t>.<raw body>"keyed with your secret.- During the 24 hours after a secret rotation, the header carries two
v1entries — one per secret. A match on either is valid.
Verification steps
- Read the raw request body, before any JSON parsing or re-serialization.
- Parse the header: extract
tand everyv1value. - Compute
HMAC-SHA256(secret, "<t>.<rawBody>")as lowercase hex. - Compare against each
v1using a constant-time comparison. Any match passes. - Reject if the timestamp is stale — 5 minutes tolerance is a good default.
Rotating secrets
Response
v1 entries), so you can deploy the new secret at your own pace. After the window, only the new secret signs.
Test the full path any time with POST /retailer/v1/webhook/test — it sends a real signed ping synchronously and reports your endpoint’s response code.
Checklist
- Verify on the raw body, before JSON parsing
- Constant-time comparison (
timingSafeEqual,hmac.compare_digest,hash_equals) - Accept any matching
v1entry (rotation window has two) - Enforce a timestamp tolerance (about 5 minutes)
- Deduplicate on the delivery id (
X-Wizzgift-Delivery) - Respond
2xxfast; process asynchronously