Sandbox: real offers, real PNRs, test tickets

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 ticketed with test ticket and EMD numbers in tickets[]; a hold comes back confirmed with none. Read status and tickets[], not the HTTP code — that is the check production relies on too.
Orders

Commit order

Finalise an upsell — add ancillaries and seats and settle payment on an existing order.

POST/v1/orders/{orderID}/commitRequires X-API-Key

The 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

Response example
{
  "order_id": "AA001Y1XD7ZA6",
  "pnr": "JBQGEY",
  "status": "ticketed",
  "total_amount": 380.62,
  "tickets": [
    {
      "ticket_number": "001-1234567890",
      "passenger_id": "PAX96101",
      "status": "issued"
    }
  ]
}

Authorization

X-API-KeystringRequired

Opaque API key issued from the Norba dashboard. Sent on every request as the X-API-Key HTTP header. Missing or invalid keys are rejected with 401 Unauthorized.

Path parameters

orderIDstringRequired

Canonical order ID.

Example: AA001Y1XD7ZA6

Query parameters

airlinestring

IATA 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.

Example: AA

Request body

paymentobject

method (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

400

invalid 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.

409

illegal_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.

422

hold_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.

502

Only 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.

503

coming_soon — order writes disabled; or airline_unreachable with Retry-After — nothing reached the airline.

POST /v1/orders/{orderID}/commit — Commit order | Norba API