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.
Commit order
Finalise an upsell — add ancillaries and seats and settle payment on an existing order.
/v1/orders/{orderID}/commitRequires X-API-KeyThe OrderChange variant that closes out an upsell: ancillaries, seats and a payment in one call. **This is the closest thing to a "pay" endpoint — there is no POST /v1/orders/{orderID}/pay.**
Pay a hold here, before its expires_at — past it the airline has released the seats and the call is 422 hold_expired before anything is sent — or finalise an upsell. A settled hold comes back ticketed with the ticket and EMD numbers in tickets[] — in the sandbox too, with test documents. Read tickets[] on the response — until it is non-empty, no ticket exists. When the carrier accepts the order but cannot issue the document, the order is left as it was (still confirmed, no tickets, no payments) and the response says why in warnings[], so the call is safe to retry once the cause is resolved.
Seats added here are subject to the paid-seat limitation described on PUT /v1/orders/{orderID}. ?airline= is optional: the airline comes from your stored order, not from this parameter. A value that disagrees with the persisted carrier is refused with 400 airline does not match the order.
Idempotency. Send an Idempotency-Key (up to 128 characters, one per action) and keep it across retries: a repeat returns what the first call produced with Idempotent-Replayed: true, a key still in flight is 409 idempotency_key_in_flight, and another write still running on the same order is 409 order_busy — both with Retry-After, after which you send the same request again with the same key. A refusal from the airline is 422 (a named code, or airline_refused), never a 502; a write that never reached the airline is 503 airline_unreachable with Retry-After. A 502 with code: "commit_outcome_unknown", attempt_id and Retry-After means the write reached the airline and its answer was lost — do not send it again; a retry with the same key is 409 with the same code until a person has settled it. A change the airline made but Norba could not store is still a 200 with X-Norba-Order-Persisted: false and a warning.
Request example
curl --request POST \
--url https://api.norba.io/v1/orders/{orderID}/commit \
--header "X-API-Key: $NORBA_KEY" \
--header "Content-Type: application/json" \
--data '{ "payment": { "method": "CARD", "currency": "EUR", "amount": 380.62 } }'Response example
{
"order_id": "AA001Y1XD7ZA6",
"pnr": "JBQGEY",
"status": "ticketed",
"total_amount": 380.62,
"tickets": [
{
"ticket_number": "001-1234567890",
"passenger_id": "PAX96101",
"status": "issued"
}
]
}Path parameters
orderIDstringRequiredCanonical order ID.
AA001Y1XD7ZA6Query parameters
airlinestringIATA code. Optional — inferred from your stored order. A value that disagrees with the persisted carrier is refused with 400 airline does not match the order.
AARequest body
paymentobjectmethod (CARD, TRANSFER, WALLET), currency, amount, optional card (number, holder, expiry_month, expiry_year, brand, and optional cvv) and optional authentication — the same shapes as POST /v1/orders. See [Payment Flow](/docs/payment-flow).
ancillariesarray<Ancillary>Ancillaries to add.
seatsarray<Seat>Seats to assign — see the paid-seat limitation.
Error codes
400invalid json, a seat whose passenger_id names an infant, or airline does not match the order when a supplied ?airline= disagrees with the persisted carrier.
409illegal_transition; order_busy or idempotency_key_in_flight (with Retry-After); idempotency_key_reused; or commit_outcome_unknown (code, with attempt_id) — an earlier request with this key reached the airline and its answer was lost; do not pay again.
422hold_expired — the hold's expires_at has passed and the airline has released the seats; nothing was sent, search again. airline_refused — the airline rejected the commit and the connector could not name why. Or a named refusal such as card_required.
502Only with code: "commit_outcome_unknown", attempt_id and Retry-After: the payment reached the airline and its answer was lost — do not send it again.
503coming_soon — order writes disabled; or airline_unreachable with Retry-After — nothing reached the airline.