Signature Verification¶
Every webhook is signed with HMAC-SHA256 using your subscription's signature_key (returned once when you create the subscription, or rotate via POST /api/v2/subscriptions/:id/rotate-secret). Verify the signature on every delivery so you only act on webhooks that genuinely came from Spry.
Signature Format¶
The signature is sent in the X-Sprypt-Signature header:
| Part | Meaning |
|---|---|
t |
Signing timestamp, epoch seconds |
v1 |
Hex-encoded HMAC_SHA256(signature_key, t + "." + raw_request_body) |
Verification Algorithm¶
- Extract
tandv1from theX-Sprypt-Signatureheader. - Reject the webhook if
now - t > 300seconds (5-minute replay window). - Compute the expected signature:
HMAC_SHA256(your_signature_key, t + "." + raw_request_body). - Compare
v1to the expected value using a constant-time comparison (e.g.timingSafeEqual). - If they match, the webhook is authentic.
Use the raw request body as received, not a re-serialised version. Any whitespace difference will produce a different signature.
Replay Protection¶
The t timestamp is part of the signed payload, so it cannot be tampered with. Reject any webhook where now - t > 300 seconds. This prevents replay attacks even if a signed payload is intercepted.
If your endpoint's clock is significantly off from real time, you may see false rejections — keep it NTP-synced.