Webhooks
Add endpoints under Webhooks in the portal. We POST JSON to your URL (public https only) when something happens.
| Event | When |
|---|---|
booking.ticketed |
Ticket issued |
booking.failed |
Booking couldn't be completed. Money returned |
booking.cancelled |
Booking cancelled |
booking.refunded |
A refund reached your wallet |
{ "id": "7d1f…", "type": "booking.ticketed", "created_at": "2026-12-01T10:00:00+00:00", "data": { "id": "FR-AB12CD34", "status": "ticketed", "pnr": "XK92QA", "total": 11368250 } }
Payloads carry no passenger details. Fetch the booking if you need them.
Verify the signature#
Every delivery has X-FlyRail-Signature: t=<unix time>,v1=<hex>. v1 is the HMAC-SHA256 of "<t>.<raw body>" using your endpoint's signing secret (shown once when you create it).
$header = $request->header('X-FlyRail-Signature');
preg_match('/t=(\d+),v1=([a-f0-9]+)/', $header, $m);
$expected = hash_hmac('sha256', $m[1].'.'.$request->getContent(), $secret);
abort_unless(hash_equals($expected, $m[2]) && abs(time() - (int) $m[1]) < 300, 400);
const [, t, v1] = header.match(/t=(\d+),v1=([a-f0-9]+)/);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);
import hmac, hashlib, time, re
t, v1 = re.match(r"t=(\d+),v1=([a-f0-9]+)", header).groups()
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, v1) and abs(time.time() - int(t)) < 300
Use the raw body, compare in constant time, and reject anything older than five minutes.
Retries#
Reply 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then marked failed (retry it from the portal). Deliveries can repeat, so dedupe on the event id. Events aren't guaranteed to arrive in order: use status from the payload or fetch the booking.
GET /webhooks/events lists recent deliveries and their state. Send test in the portal sends a webhook.test event.