Guides · Orders

Orders Lifecycle

Everything that happens after a customer picks an offer. Orders are the stateful half of NDC — they persist at the airline and carry the full booking lifecycle from creation through ticketing, servicing, and eventual completion.

The happy path

A typical booking flows through these states. Every transition writes an order_event for the audit trail.

1
Search
POST /v1/shopping/offers → the aggregator fans out to every connected airline and returns normalized offers, cached with a TTL. Search one-way with max_stops: 0 for offers that book reliably.
2
Service shop (optional)
GET /v1/offers/{offerID}/services → ancillary catalogue for the offer. An empty list is a normal answer — some fares carry no à-la-carte services.
3
Seat shop (optional)
GET /v1/offers/{offerID}/seats → seat map with availability, price bands and decoded features. Percent-encode the offer ID in the path.
4
Price the offer
POST /v1/offers/price → confirms the price and mints the BOOKABLE offer ID (AA-X… becomes AA-P…), guaranteed roughly 20 minutes. Mandatory: the shopped ID is rejected at booking.
5
Create order
POST /v1/orders → passengers, contact, and any seats and ancillaries, against the PRICED offer. Returns the order with the carrier's PNR. Send payment with your own card and the carrier authorises the charge in the same call — that is the model on Payment Flow. Omit payment entirely and the order is created as an unpaid hold, confirmed with expires_at at the airline's payment time limit.
6
Settle the hold
POST /v1/orders/{orderID}/commit → settles a hold created without payment. There is no /pay endpoint. With seats or ancillaries in the body the same call is the post-sale upsell path, which carries the ad-valorem fee on the increase plus the flat servicing fee. Read tickets[] on the response: until it is non-empty, no ticket exists.
7
Servicing
Quote → confirm for changes and cancellations, refund eligibility, reshop. The airline is inferred from your stored order; ?airline= is optional, and if you send it, it must name the carrier holding the booking or the call is refused with 400 airline does not match the order.

Order states

An order can be in one of these states at any time:

StatusMeaning
pendingOrder created, nothing confirmed by the airline yet. No PNR, no ticket, no payment.
confirmedThe airline holds the reservation and the PNR exists. No payment is implied and no ticket exists. An order created without payment is confirmed with expires_at set to the airline's payment time limit — that is the unpaid hold.
ticketedAt least one ticket number came back from the airline. Read the numbers from tickets[]. Until the status is ticketed and tickets[] is non-empty, do not tell the traveller they are ticketed.
partially_cancelledSome passengers or segments are cancelled; the rest of the booking stands.
disruptedThe airline changed or cancelled a flight. Reprotection is the carrier's.
cancelledOrder cancelled. May be refundable or non-refundable depending on fare rules.
refundedRefund recorded. Money returned to the original form of payment.
failedOrder creation or payment processing failed at the airline.

Those eight are the whole set; the stored row also permits expired, which nothing emits today. held, voided and exchanged are not order statuses: a hold is confirmed with expires_at, the ticket-level states void, exchanged, refunded and flown live on tickets[].status, and voided is an order event. An order can come back confirmed carrying a document-issuance failure in warnings[] — read them. A cancellation or refund within 24 hours of booking credits the Norba fees back.

Payment methods

The payment block — { method, currency, amount, card?, authentication? } — has only one field the airline actually branches on. Everything below is enforced on POST /v1/orders: POST /v1/orders/{orderID}/commit forwards the block to the carrier as sent, without the amount reconciliation or the commission withholding, and only the 3-D Secure refusal still applies there.

FieldWhat it does
card (object)Its presence, not the method string, is what selects the card rail: brand, holder, number, CVV and expiry are sent to the airline as a real card charge.
method (string, no card)Free text, capped at 16 characters — not a validated enum. With no card object every payment settles offline (NDC's "OT"/other payment type) and method is carried through verbatim as the settlement remark, e.g. "CASH", "BSP", "INVOICE".
amount / currencySend the total you are charging for this booking. Only a floor is enforced: it must be at least the carrier's own price for the offer plus any extras you priced with it, allowing a rounding cent, or the booking is refused with 400 "payment.amount is less than the airline's price for this offer" before the airline is called. Anything above the floor is your margin — a Norba commission rule or a markup from your own checkout, Norba treats them the same — and it is withheld rather than tendered to the carrier. Interpreted as an exact decimal with at most two decimal places — send it as a number or a numeric string, never rounded by you. currency is a 3-letter ISO 4217 code.
card.cvv (optional)Send the security code only where the carrier asks for it. Omitted by default on purpose: a code sent where it is not needed is exposure bought for nothing. When present it must be 3 or 4 digits.
authentication (object)The 3-D Secure result your own provider returned, for carriers that accept one. Norba never authenticates a cardholder, and a carrier that cannot forward the tokens refuses the booking rather than dropping them. See the Payment Flow guide.

This is your own payment to the carrier: the airline’s amount, charged to your card. The traveller’s payment to you was taken on your own checkout and never reaches Norba, and what Norba charges you for the booking is a third, separate flow. See Payment Flow for the three movements, and Billing & Payments for the last one.

Important notes

  • Offer IDs expire quickly. Always price the offer immediately before creating the order, and book with the ID that POST /v1/offers/price returned — never the one from search.
  • Passenger emails must belong to the actual passenger, not the agency. Airlines validate this.
  • Infants must not outnumber adults. Norba enforces the lap-infant rule itself, on both POST /v1/shopping/offers and POST /v1/orders, and refuses with 400 "infants must not outnumber adults" before the airline is called.
  • The carrier PNR is returned as pnr on order creation — preferred over the gateway's own booking reference, which is also carried. The airline reassigns passenger IDs at this point too: the T1 you sent comes back as something like PAX96101, and later calls must use the new value.
  • The event timeline is not exposed yet: history[] is reserved for it and is absent from GET /v1/orders/{orderID} today, and there is no separate /history endpoint. Until it ships, follow the lifecycle with status, tickets[] and payments[] on the order, plus webhooks.
  • GET /v1/orders/{orderID} answers from Norba's copy of the order. Add ?refresh=true to have the airline asked instead — the current status, tickets and payments as the carrier holds them — and the answer is stored before it is returned. Use it after a delayed payment, or whenever the carrier may have acted without telling you.
  • On the rare occasion the airline confirms a booking but Norba fails to record it, the 201 and the order body still stand — the airline really did confirm it — but the response carries X-Norba-Order-Persisted: false and a warning field. Keep the PNR from that response: the order will not be retrievable or serviceable through this API afterwards.
  • A 404 on GET /v1/orders/{orderID} or any servicing call names the field to copy the id from, and — if an order exactly one character away exists in your account — suggests it by id in did_you_mean. Two equally-close candidates suppress the suggestion rather than guess wrong.
Flight Order Lifecycle: Create, Pay, Ticket, Cancel | Norba