In the sandbox these endpoints work against real carrier test inventory: a search returns real offers and you can create a real booking with a carrier PNR. Two things are worth knowing before you build against them.
- Sandbox orders are test reservations. Nobody flies on them, and they raise no fee. Cancel what you create; anything left over expires on its own payment time limit.
- Sandbox tickets are test documents. A paid order comes back
ticketedwith test ticket and EMD numbers intickets[]; a hold comes backconfirmedwith none. Readstatusandtickets[], not the HTTP code — that is the check production relies on too.
Offer seat map
Full seat map for an offer — rows, availability, price bands and decoded seat features.
/v1/offers/{offerID}/seatsRequires X-API-KeyReturns the airline's SeatAvailability for one offer as a canonical SeatMap, ready to drive an interactive seat picker. Pass segment_id to get the map of one flight of a multi-segment itinerary; the seats it returns carry that segment_id, which you repeat when buying.
The offer ID goes in the path and must be percent-encoded — same rule as the services endpoint. An order ID works here too when ?airline= is added (an order id has no AA- prefix to infer the carrier from): GET /v1/offers/AA001Y1XD7ZA6/seats?airline=AA. Prefer GET /v1/orders/{orderID}/seats, which is scoped to your agency.
A live BCN→JFK Boeing 777-200 map comes back as 24 rows, 240 seats, 190 available, across eight price bands from 23 to 145 EUR. Features are decoded to a stable vocabulary: accessible, aisle, bassinet, blocked, bulkhead, exit_row, extra_legroom, no_window, restricted_recline, window.
Buying a seat. Put the seat in the seats array of POST /v1/orders, alongside the flight. seat_id holds the human designator (17A); Norba resolves the seat's offer item from this map at booking time, so passing row, column and segment_id is enough.
After booking, use GET /v1/orders/{orderID}/seats and POST /v1/orders/{orderID}/seats instead: they are tenancy-scoped, price the seat from the order's own map, and pay for it where the booking is already ticketed. Assigning a paid seat to an existing order is still not available on every carrier — the airline's page says whether it is.
Results are cached per offer ID.
Request example
curl --request GET \
--url https://api.norba.io/v1/offers/{offerID}/seats \
--header "X-API-Key: $NORBA_KEY"Response example
{
"flight_id": "AA-P2E80EC16-51FD-48AD-924B-1",
"airline": "AA",
"aircraft": "772",
"rows": 24,
"seats": [
{
"seat_id": "17A",
"row": 17,
"column": "A",
"cabin": "Economy",
"available": true,
"price": {
"amount": 128.93,
"currency": "EUR"
},
"features": [
"window",
"extra_legroom"
],
"segment_id": "SGM010012275a20c"
},
{
"seat_id": "17B",
"row": 17,
"column": "B",
"cabin": "Economy",
"available": false,
"price": {
"amount": 0,
"currency": "EUR"
},
"features": [
"blocked"
],
"segment_id": "SGM010012275a20c"
}
]
}Path parameters
offerIDstringRequiredCanonical offer ID or order ID. Percent-encode it.
AA-P2E80EC16-51FD-48AD-924B-1|P2E80EC16-51FD-48AD-924B-1-1Query parameters
airlinestringIATA code. Inferred from the ID prefix when omitted.
AAResponse fields
flight_idstringRequiredFlight or offer reference the map belongs to.
airlinestringRequiredIATA airline code.
aircraftstringAircraft type code, e.g. 772.
rowsintegerRequiredNumber of rows in the map.
seats[].seat_idstringRequiredHuman seat designator, e.g. 17A.
seats[].rowintegerRequiredRow number.
seats[].columnstringRequiredColumn letter.
seats[].cabinstringCabin name.
seats[].availablebooleanRequiredWhether the seat can be selected.
seats[].priceobject{amount, currency} — 0 when free.
seats[].featuresarray<string>accessible, aisle, bassinet, blocked, bulkhead, exit_row, extra_legroom, no_window, restricted_recline, window.
seats[].segment_idstringRequiredThe segment this seat is on. Required when you buy the seat.
Error codes
400unknown airline — no recognisable prefix and no ?airline=.
502The airline rejected the reference.