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

Quote a cancellation

What the traveller gets back, and what the airline keeps, before you cancel.

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

Returns a CancellationQuote — refund amount, penalty amount, and a 30-minute validity window — so you can show the financial consequence before acting.

**Note this is a POST that changes nothing.** The order is untouched; only POST /v1/orders/{orderID}/cancel/confirm (or DELETE /v1/orders/{orderID}) actually cancels.

refund_amount is net: the original fare less penalty_amount, floored at zero, in the currency the fare was sold in. It is the same figure GET /v1/orders/{orderID}/refund-eligibility reports. (Before 11 September 2026 this field carried the gross fare with the penalty undeducted, so the two endpoints disagreed whenever a penalty applied.)

Whole or partial. With no segment_ids the whole order is quoted: on a held order from the airline's reprice, on a ticketed one from its cancel offer. With segment_ids the airline is asked what dropping only those flights would return — a partial-cancel reshop — and the response carries partial: true, the segment_ids echoed, and a quote_id that is the cancel offer POST /v1/orders/{orderID}/cancel/confirm must accept (as cancellation_quote_id, with the same segment_ids) for the refund to be the one quoted. passenger_ids and reason are accepted and not acted on.

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

Request example

curl --request POST \
  --url https://api.norba.io/v1/orders/{orderID}/cancel \
  --header "X-API-Key: $NORBA_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "segment_ids": ["SEG-2"] }'

Response example

Response example
{
  "quote_id": "qte_01K2M8ZC3AC5N8",
  "order_id": "AA001Y1XD7ZA6",
  "refund_amount": {
    "amount": 355.62,
    "currency": "EUR"
  },
  "penalty_amount": {
    "amount": 25,
    "currency": "EUR"
  },
  "valid_until": "2026-08-14T10:15:00Z"
}

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

passenger_idsarray<string>

Accepted but not acted on.

segment_idsarray<string>

Quote dropping only these flights. Empty quotes the whole order.

reasonstring

Accepted but not acted on.

Error codes

400

invalid json, or airline does not match the order when a supplied ?airline= disagrees with the persisted carrier.

409

illegal_transition — a partial quote was asked of a closed order.

422

partial_cancel_unavailable — the airline offered no way to cancel only those flights.

502

The airline rejected the reprice or the reshop the quote is derived from.

POST /v1/orders/{orderID}/cancel — Quote a cancellation