Skip to main content
Every webhook delivery carries an X-Yotel-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

Reject the request if:
  1. X-Yotel-Signature doesn’t match expected (timing-safe compare), OR
  2. |now - X-Yotel-Timestamp| > 300 seconds (replay protection)

Reference implementations

Critical gotchas

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.
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.
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.
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.