X-Zetta-Signature header. Your
handler must verify it before trusting the payload. Without
verification, anyone can POST a fake call.ended to your endpoint.
The contract
X-Zetta-Signaturedoesn’t matchexpected(timing-safe compare), OR|now - X-Zetta-Timestamp| > 300seconds (replay protection)
Reference implementations
Critical gotchas
Read the raw body BEFORE parsing JSON
Read the raw body BEFORE parsing JSON
Many frameworks (Express, FastAPI) auto-parse JSON bodies. If you
sign the parsed-then-reserialized body, whitespace or key-order
drift breaks the signature. Always sign the raw bytes.
Sync your server's clock
Sync your server's clock
The 5-minute replay window requires NTP sync on your host. A drift
of more than ±5 min rejects every delivery. If you see
401: Replay window exceeded, check your server’s clock first.Use timing-safe compare
Use timing-safe compare
Don’t use
== to compare signatures — it short-circuits on the
first different byte and leaks information to an attacker probing
your endpoint. Use hmac.compare_digest / crypto.timingSafeEqual
as shown above.Handle duplicate deliveries (event_id)
Handle duplicate deliveries (event_id)
Retries use the same
event_id. Your handler should key on that
— store a seen_event_ids set (Redis + TTL matches our 7.5h retry
window) and skip duplicates. Skipping is fine: respond 200 without
reprocessing.