Docs
Get an API key

Add bags and seats to a flight booking

Choose seats and bags when you prebook, or buy bags on a confirmed booking as a separate purchase.

In short

4 calls, in the order this page makes themFor people and agents
  1. 1GET/v1/flight/offers/{offer_reference}/available-servicesList an offer's bags
  2. 2GET/v1/flight/offers/{offer_reference}/seat-mapsGet seat maps
  3. 3GET/v1/flight/bookings/{id}/available-servicesList a booking's purchasable bags
  4. 4POST/v1/flight/bookings/{id}/servicesBuy bags for a booking

The same list heads this page's Markdown:/flight/flight-extras.md

A traveller wants a checked or carry-on bag beyond what the fare includes, or a particular seat. You add both when you prebook, so they ride on the fare's payment. Bags can also be bought later on a confirmed booking as a separate purchase; seats cannot.

Only bookings held with Duffel sell extras; a supplier without them answers 422 supplier_operation_unsupported.

Before you prebook: bags

List what the airline sells with the offer:

Request
curl "$BASE_URL/v1/flight/offers/<offer_reference>/available-services" \
  -H "x-api-key: $API_KEY"
Response
{ "data": [ { "id": "ase_00009UhD4ongolulWd9123", "type": "BAGGAGE", "baggage": { "type": "CHECKED", "max_weight_kg": 23 }, "passenger": { "type": "ADULT", "number": 1 }, "segments": [ { "journey": 0, "segment": 0 } ], "maximum_quantity": 3, "unit_amount": { "amount": 40, "currency": "USD" } } ] }

Each entry is one bag for one traveller on some flights. passenger names the traveller as the Nth passenger of that type in your prebook passengers, counting from 1, and segments names the flights it covers by their journey and segment positions in the offer, counting from 0.

Before you prebook: seats

Read the seat map of each flight:

Request
curl "$BASE_URL/v1/flight/offers/<offer_reference>/seat-maps" \
  -H "x-api-key: $API_KEY"
Response
{ "data": [ { "journey": 0, "segment": 0, "cabins": [ { "cabin_class": "ECONOMY", "deck": 0, "aisles": 1, "wings": { "first_row_index": 8, "last_row_index": 14 }, "rows": [ { "sections": [ { "elements": [ { "type": "SEAT", "designator": "12A", "disclosures": [], "services": [ { "id": "ase_00009UhD4ongolulWAAA1A", "passenger": { "type": "ADULT", "number": 1 }, "unit_amount": { "amount": 30, "currency": "USD" } } ] }, { "type": "SEAT", "designator": "12B", "disclosures": [ "Passenger must be an adult" ], "services": [] } ] }, { "elements": [ { "type": "LAVATORY" } ] } ] } ] } ] } ], "meta": { "supplier": "DUFFEL" } }

Draw each cabin front to back from rows, and each row left to right from its sections, with an aisle between sections. A SEAT carries its designator, the airline's disclosures to show, and one entry in services per passenger who may take it, with that passenger's price; 0 is a free seat. An empty services means the seat cannot be chosen: it is taken, or not sold to this party. Any other type is a fixture to draw in place of a seat; OTHER is one this API does not name, so draw a blank. An empty data means the airline publishes no map for these flights.

A passenger takes one seat per flight, and two passengers cannot take the same seat; prebook refuses either as 422 services_unavailable.

Choose them at prebook

Send the ids you want in the prebook request, a bag's with how many and a seat's with quantity: 1:

"services": [ { "id": "ase_00009UhD4ongolulWd9123", "quantity": 1 }, { "id": "ase_00009UhD4ongolulWAAA1A", "quantity": 1 } ]

They are priced at the airline's current price and included in the booking's total_amount, so book pays the fare and the extras in one charge. fare_amount is the flights' share and services_amount the extras'; the two add up to total_amount, and fare_tax_amount is the tax already inside fare_amount. Each extra appears in services with its unit_amount and total_amount, as PENDING until book confirms it.

A seat can be taken by another traveller between your prebook and your book. Book checks again before it places the order: if a chosen extra is gone or priced differently, the booking is FAILED with 422 services_unavailable, nothing is captured, and OEX attempts to release any hold on the card. Search again and prebook the new offer with a new key. If the seat goes after that check, while the airline places the order, you receive 422 supplier_rejected or 500 result_unknown instead; follow Flight API errors for each.

After booking

Seats cannot be added once the ticket is issued. Bags can. List what can still be added to a confirmed booking:

Request
curl "$BASE_URL/v1/flight/bookings/bkg_0192f3a4b5c64d7e8f9a0b1c2d3e4f5b/available-services" \
  -H "x-api-key: $API_KEY"

Entries here name the booking's own ids, passenger_id and segment_ids, instead of positions. Buy them with an idempotency key and the instrument to charge:

Request
curl -X POST "$BASE_URL/v1/flight/bookings/bkg_0192f3a4b5c64d7e8f9a0b1c2d3e4f5b/services" \
  -H "x-api-key: $API_KEY" -H "x-idempotency-key: bags-bkg-0192f3a4-1" -H "Content-Type: application/json" \
  -d '{ "services": [ { "id": "ase_00009UhD4ongolulWd9123", "quantity": 1 } ], "payment_instrument_id": "inst_78a44f9bb56641a1a8196fba0174c25d" }'

This is its own payment, separate from the fare's, and the response is the booking with the new entries in services as CONFIRMED and services_amount raised. A purchase the airline refused stays listed as FAILED and is not in services_amount. Repeating the same key returns the stored result and never buys again; what to do when the outcome is unknown is in Retry flight calls safely.

Keep learning

On this page