Event catalogue
Event envelope
id— idempotent event ID. Deduplicate on it (the same event may be delivered more than once).test—truewhen the event comes from ansk_test_*key or fromPOST /webhooks/{id}/test. Your production handler must ignore or clearly label these.
Delivery headers
The
signing_secret is returned once, in the POST /webhooks response
(WebhookEndpointWithSecret). It is never displayable again — store it securely.
Verifying the signature
Compare in constant time. Reject on mismatch.Retries
A delivery is successful on a2xx response within the timeout. Otherwise
Safariat retries up to 5 attempts with backoff: 5 min → 15 min → 1 h →
6 h → 24 h. After the last attempt the delivery is dead-lettered. Because
retries happen, your handler must be idempotent on X-SafarApi-Event-Id.
Respond 2xx immediately and process asynchronously — slow handlers cause
timeouts and unnecessary retries.
Delivery semantics
SafarAPI guarantees at-least-once delivery, not exactly-once. Two rules follow:- Deduplicate. The same event (
X-SafarApi-Event-Id) may be delivered more than once — on retry, and in a narrow window if a delivery is received but our status write is interrupted. Treat the first successful processing as authoritative and make re-processing a no-op (upsert / dedup table keyed byEvent-Id). - Do not rely on ordering. Events are dispatched concurrently and retried
with backoff, so they can arrive out of order (e.g.
booking.cancelledbefore itsbooking.confirmed, or a retried event after a newer one). Drive your state from the event’screated_atand your own state machine — never assume the arrival order reflects the real sequence.
Missed or dead-lettered a delivery? List and inspect attempts with
GET /webhooks/deliveries, and re-drive one with
POST /webhooks/deliveries/{id}/replay. You can also reconcile from the
authoritative REST resources (GET /bookings/{n}, GET /settlements) at any time —
webhooks are an optimization over polling, not the source of truth.Testing
POST /webhooks/{id}/test sends a synthetic event to your endpoint and returns
the HTTP status and response time it observed — use it to validate connectivity
and your signature check before going live.