Webhooks
Signed events, retries with backoff, and replay from the dashboard.
Booking lifecycle events are delivered to an HTTPS endpoint you register. Deliveries are signed, retried with exponential backoff for 24 hours, and can be replayed after you fix a bug on your side.
Events
| Event | Fires when |
|---|---|
| booking.confirmed | A supplier confirmed the reservation. |
| booking.failed | The booking could not be completed and no reservation exists. |
| booking.cancelled | A cancellation was accepted, with the penalty actually applied. |
| booking.modified | A supplier-side change altered the reservation. |
| booking.reconciled | An unknown outcome resolved to a definite state. |
| payment.succeeded | A payment cleared. (Payments module: roadmap.) |
| payment.refunded | A refund settled. (Payments module: roadmap.) |
| supplier.degraded | A connector breached its error budget and was shed from the fan-out. |
Verifying a delivery
Node
import crypto from "node:crypto"; // Header: X-TravelCore-Signature: t=1756377600,v1=<hex>export function verify(rawBody, header, secret) { const parts = Object.fromEntries( header.split(",").map((kv) => kv.split("=")), ); const expected = crypto .createHmac("sha256", secret) .update(`${parts.t}.${rawBody}`) .digest("hex"); // Constant-time compare, and reject stale timestamps to stop replay. const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; const ok = crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(parts.v1), ); return fresh && ok;}Note:Verify against the raw request body, before any JSON parsing. A body that has been parsed and re-serialised will not match the signature.
- Return 2xx quickly and process asynchronously; we time out at 10 seconds.
- Deliveries are at-least-once. Deduplicate on the event id.
- Retries back off over 24 hours, then the delivery is marked failed and can be replayed.