Skip to content

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:

X-Sprypt-Signature: t=1776158903,v1=a1b2c3d4e5f6...
Part Meaning
t Signing timestamp, epoch seconds
v1 Hex-encoded HMAC_SHA256(signature_key, t + "." + raw_request_body)

Verification Algorithm

  1. Extract t and v1 from the X-Sprypt-Signature header.
  2. Reject the webhook if now - t > 300 seconds (5-minute replay window).
  3. Compute the expected signature: HMAC_SHA256(your_signature_key, t + "." + raw_request_body).
  4. Compare v1 to the expected value using a constant-time comparison (e.g. timingSafeEqual).
  5. 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.