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 services
The à-la-carte catalogue for an offer — bags, meals, lounge, Wi-Fi, priority boarding.
/v1/offers/{offerID}/servicesRequires X-API-KeyReturns the airline's ServiceList for one offer, normalized into canonical Service objects.
The offer ID goes in the path and must be percent-encoded. AA offer tokens contain | and .; the API decodes the segment before routing, but your HTTP client has to encode it first. The airline query parameter is optional — it is inferred from the ID prefix.
This endpoint also accepts an order ID — with ?airline=AA added, because an order id carries no AA- prefix to infer the carrier from (400 unknown airline without it). Prefer GET /v1/orders/{orderID}/services, which is scoped to your agency and remembers what it showed.
An empty list is a normal, successful answer. AA reports "There are no available services at this time" as an error in its NDC response; the adapter recognises a wholly benign error set and translates it to [] rather than failing the call. Basic Economy and Main Cabin fares on BCN→JFK both legitimately return zero services.
Where the services get used: pass them to POST /v1/orders in the ancillaries array. Their offer references belong to the shopping context of the offer, so that is the only point at which the airline still recognises them. After booking, list and buy against the order instead — GET and POST /v1/orders/{orderID}/services — where the airline prices them against the passengers and flights it now holds.
Results are cached per offer ID.
Request example
curl --request GET \
--url https://api.norba.io/v1/offers/{offerID}/services \
--header "X-API-Key: $NORBA_KEY"Response example
[
{
"service_id": "SRVDEF08001b075a14f",
"type": "bag_checked",
"name": "First checked bag",
"description": "One piece up to 23kg",
"price": {
"amount": 75,
"currency": "EUR"
},
"segment_refs": [
"SGM010012275a20c"
],
"pax_refs": [
"T1"
]
}
]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
[].service_idstringRequiredAirline's service id — the reference you quote when buying it.
[].typestringRequiredOne of bag_checked, bag_carry_on, seat, meal, lounge, fast_track, priority_boarding, insurance, upgrade, pet, sport_equipment, extra_legroom, wifi, other.
[].namestringRequiredDisplay name.
[].descriptionstringLonger description when the airline supplies one.
[].priceobject{amount, currency}.
[].segment_refsarray<string>Segments the service applies to.
[].pax_refsarray<string>Passengers the service applies to.
Error codes
400unknown airline — the ID carried no recognisable prefix and no ?airline= was given.
502The airline rejected the reference (an expired offer, typically). The body is the generic sentence; the carrier's text is in Norba's logs under your X-Request-Id.