Notifications — subscribe, verify, replay
When the airline changes your order — a schedule change, a refund, a ticket issued, a passenger flown, a payment cleared — it tells you with a signed NDC notification to an HTTPS endpoint you register. You never have to poll.
What you can subscribe to
| Event | Message you receive | When |
|---|---|---|
order.disrupted | IATA_OrderChangeNotifRQ | a schedule change or a cancelled flight |
order.changed, order.divided, order.cancelled | IATA_OrderChangeNotifRQ | a change the airline or the customer made (never your own) |
order.ticketed, order.paid, order.payment_expired, order.refunded | IATA_OrderChangeNotifRQ | documents, payments and refunds |
service.checked_in, service.boarded, service.flown | IATA_ServiceStatusChangeNotifRQ | a delivery milestone of a service |
service.delivered | IATA_ServiceDeliveryNotifRQ | services with no ticket or EMD behind them, delivered |
clearance.requested, clearance.cancelled | IATA_PaymentClearanceNotif | a settlement payment put up for clearance, or cancelled before it cleared |
clearing.completed | IATA_PaymentClearingNotif | a clearing run settled your clearances |
Changes you made yourself are not notified back to you — you already had the answer.
The airline's own accounting feed (accounting.*) is not available to sellers.
1. Subscribe
With a token carrying ndc:notif:subscribe (JSON, not XML):
curl -X POST https://ndc.retailaer.com/subscriptions \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"endpoint":"https://ndc.your-agency.example/notifications",
"events":["order.disrupted","order.refunded","service.flown"],
"content":"snapshot"}'
events— leave it out (or empty) to receive every event above.content—snapshotsends the whole current order with each change;changessends the change only.- The endpoint must be
https, without credentials in the URL, and resolve to a public address. Redirects are not followed.
The answer carries the subscription (status: pending_verification) and its signing
secret (whsec_…) — shown once. Store it like a password.
2. Verify the endpoint
curl -X POST https://ndc.retailaer.com/subscriptions/$SUB_ID/verify \
-H "Authorization: Bearer $TOKEN"
The airline POSTs a signed JSON challenge to your endpoint:
{ "type": "ndc.subscription.challenge", "subscriptionId": "…", "challenge": "…" }
Answer 2xx with a body that contains the challenge value. The subscription turns
active; nothing is delivered before that.
3. Receive — and check the signature
Each notification is an XML POST with:
| Header | |
|---|---|
IATA-Message-Name | the NDC message (table above) |
X-NDC-Event | the event, e.g. order.disrupted |
X-NDC-Sequence | 1, 2, 3 … per subscription and order — a gap means you missed one |
X-NDC-Notification-Id | unique per notification — de-duplicate on it |
X-NDC-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")> |
Verify the signature over the raw body, and refuse old timestamps:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret: string, header: string, rawBody: string, toleranceSec = 300): boolean {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? '');
if (!m) return false;
const t = Number(m[1]);
if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const want = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
const got = Buffer.from(m[2]!, 'hex');
return got.length === want.length && timingSafeEqual(got, want);
}
Answer 2xx quickly and do the work afterwards. Anything else — or no answer within 10
seconds — is a failed delivery: the airline retries with backoff (1, 5, 15, then 60
minutes) and keeps the same notification id and sequence. Deliveries are at least once;
the notification id makes them exactly-once for you.
The body carries the notification's id and sequence in PayloadAttributes
(CorrelationID, SeqNumber); a PaymentClearance message carries them in
PayloadStandardAttributes.
4. Replay
Missed a sequence number, or restored from a backup? Every notification is kept with the XML that was delivered:
# everything for one order after sequence 3
curl "https://ndc.retailaer.com/subscriptions/$SUB_ID/notifications?orderId=ORDER-ID&since=3" \
-H "Authorization: Bearer $TOKEN"
Each entry has the event, sequence, delivery status and the body as delivered.
Managing subscriptions
GET /subscriptions | your subscriptions |
GET /subscriptions/{id} | one |
POST /subscriptions/{id}/rotate | a new signing secret (shown once) — the old one stops at once |
DELETE /subscriptions/{id} | unsubscribe |
You only ever see and manage your own subscriptions.