Receive events with webhooks
What you’ll do: register an HTTPS endpoint, prove you own it (echo a challenge token), then verify the signature on every event we deliver. Webhooks let your app react to changes — a request booking getting its PNR, a watched flight running late — without polling.
Console shortcut: you can also add and verify endpoints on the Webhooks page of your console — sandbox.kitionz.com for Test endpoints, kitionz.com for Live ones. Each console registers endpoints for its own world. This guide shows the same thing from your own server, with your API key.
Step 1 — Register an endpoint
Every call that creates or changes something here — registering, verifying, testing, replaying —
needs a new Idempotency-Key (a UUID), like every other write.
The response includes a signing secret (whsec_…) shown once — store it securely. The
endpoint starts in status PENDING (Awaiting verification). Deploy your handler (Step 2)
before you register: if the challenge finds nobody, the endpoint stays PENDING until you call
POST /v1/webhooks/endpoints/:id/verify.
Subscribe to
flight_booking.*to receive every booking event (current and future), or*for everything Kitionz emits and filter on thetypefield in your handler. The events are listed under Events below.
Step 2 — Verify you own the URL (echo the challenge)
When you create an endpoint (or call POST /v1/webhooks/endpoints/:id/verify), Kitionz sends a
verification request to your URL. Its body is
{ "type": "webhook.endpoint.verify", "timestamp": "…", "data": { "token": "…", "endpointId": "whk_…" } }.
Respond 200 with the token in the body — then the endpoint flips to ENABLED and events
start flowing. Handle this request before your de-duplication (Step 5): it is signed like an
event, but its webhook-id is the endpoint’s id (whk_…), the same on every re-verification.
Step 3 — Verify the signature on every delivery
Every delivery carries Standard Webhooks headers: webhook-id, webhook-timestamp, and
webhook-signature. Recompute the HMAC over ${webhook-id}.${webhook-timestamp}.${rawBody} with
your whsec_ secret and compare — reject anything that doesn’t match, or whose timestamp is more
than 5 minutes old.
Always read the raw request body for both the echo (Step 2) and the signature check — JSON re-serialization changes bytes and breaks the HMAC. Reply
2xxquickly; we retry failed deliveries with backoff and you can inspect attempts in the delivery log. Any Standard Webhooks library (for examplestandardwebhooks) verifies these headers too.
Step 4 — Send yourself a test event
Prove the whole receiver (raw body, signature, dedupe) before any real event exists:
A signed webhook.test delivery travels the real pipeline to your endpoint (it must be
verified first, Step 2). If your handler logs webhook.test … verified, your integration is
correct — no need to wait for a real event.
Step 5 — Deduplicate, watch deliveries, replay
Deliveries are at-least-once: store each webhook-id you’ve processed and skip repeats
(a tiny processed_webhooks table keyed on that id is enough). A retry of the same event carries
the same webhook-id.
Check delivery attempts (status, attempt count, last response) per endpoint:
Failed deliveries retry automatically for ~27 hours (backoff: 5s → 5m → 30m → 2h → 5h → 10h → 10h).
If your server was down past that window the delivery ends DEAD — recover it with a fresh
retry chain:
The Webhooks page of your console shows the same log under each endpoint’s Deliveries.
After every 5 failed deliveries in a row, Kitionz waits 5 minutes before trying that endpoint
again. After 40 in a row it disables the endpoint: a disabled endpoint receives nothing, cannot
be verified again, and its undelivered events end DEAD and cannot be replayed. Fix your receiver,
delete the endpoint and register it again (a new id and a new secret), then read what you missed
from the API — for example GET /v1/flights/bookings.
Events
Every delivery has the same body. type says which event it is, and data carries the event’s
own fields:
id is the event’s id — the same value as the webhook-id header. timestamp is when this
delivery attempt was sent (a retry has a later one). version is the version of data’s shape: a
new field never changes it, a breaking change does.
Every Flight Status event can also carry notification, a ready-to-send customer message. Booking
events come to endpoints in both worlds, Test and Live. Flight Status is a Live-only module: its
events come only to Live endpoints. Booking events still name the seller supplier and
returnSupplier; a later version adds seller beside them, as bookings already have.