# Book a flight offer

> Book an offer you have priced: ticketed at once (booking_type: issue) or held without payment (hold).

Endpoint: `POST /flights/book_flight`

Source: https://fx-port.com/docs/api/book-flight

`POST /api/v1/flights/book_flight`

Requires an API key with **Read+Write** permission: a Read key is refused with `read_write_permission_required`.

Price the offer first with `price_flight`: an offer that was not priced is refused with `404 offer_not_priced`. Send the `search_id` and the `offer_id`, one object for each traveler in `passengers` (lap infants included, at most 18) and `contact_details` with the `email` and the `phone_number`, both required. Send the passport of every traveler: airlines can require it. `booking_type` is `issue` by default; `hold` is only valid when `supportHold` is `true` on the priced offer (`400 hold_not_supported` otherwise), and a hold is paid later with `pay_order`. An unknown `booking_type` is refused with `400`: it never becomes an order.

The answer is the order: `booking.pnr`, the supplier's order id in `booking.reference` (use it as `order_id` afterwards) and in `postProcessing.bookingDbId` the FX-Port id (a UUID, the stable key of the booking), then the price block, the passengers and the itinerary. In the sandbox nothing is charged (`balanceDebited: false`); send `send_email: false` to keep the confirmation e-mail from going out. A fare with `pricing.acd.applied: true` also carries `requiresACD: true`: the booking needs one passport copy for every traveler, see Upload ACD documents.

**A failed booking is always a JSON body**: `409` when the offer or the search was already booked, `503` when the supplier failed with a 5xx status, `422` for every other refusal of the supplier. You never receive an HTML `502` page. Duffel allows one booking per search: book a second offer of a search that was booked and you get `409`.

## Request

### Body

Required, JSON.

- `search_id` (string, required): The `searchId` of the supplier entry the priced offer came from.
- `offer_id` (string, required): The `id` of the priced offer.
- `booking_type` (string, optional): `issue` (default) for instant ticketing, `hold` for an unpaid reservation when `supportHold` is `true` after pricing. Any letter case; any other value is `400 invalid_booking_type`. One of `issue`, `hold`. Default `issue`.
- `passengers` (array of object, required): One object per traveler, lap infants included. At most 18. At most 18 items.
  - `type` (string, required): `adult`, `senior`, `young_adult`, `child`, `seated_infant` or `infant` (a lap infant), in any letter case. Also accepted: `young`, `young_adults`, `infant_with_seat`, `held_infant`, `infant_without_seat`.
  - `gender` (string, required): `MALE` or `FEMALE` (`M` and `F` too), in any letter case. `X`, `U` or `UNSPECIFIED` for a passport marked X.
  - `first_name` (string, required): Given name as in the passport. Latin letters are what airlines need. Spaces and line breaks around it are removed before the checks. At most 64 characters.
  - `last_name` (string, required): Family name as in the passport. Spaces and line breaks around it are removed before the checks. At most 64 characters.
  - `date_of_birth` (string, required): `YYYY-MM-DD`, a real date that is not in the future (tomorrow is still accepted: time-zone slack).
  - `identity_documents` (array of object, optional): The passport, up to 5 documents. At most 5 items.
    - `type` (string, optional): `passport` (default).
    - `number` (string, optional): The document number: 1 to 40 letters, digits, spaces and `- . / _`. A JSON number is read as text with a `coerced_value` warning.
    - `issuing_country` (string, optional): The country that issued the document: ISO 3166-1 alpha-2 (`DZ`). Any code of 2 to 4 letters, optionally followed by `-` and 1 to 3 letters or digits (`GB-SCT`, `USPR`), is accepted and passed on in upper case without conversion. Anything else is `400 invalid_country_code`.
    - `expiry_date` (string, optional): `YYYY-MM-DD`.
    - `nationality` (string, optional): Accepted and ignored: suppliers use the issuing country.
    - `issuingCountry` (string, deprecated, optional): Legacy name: use `issuing_country`. The previous spelling of `issuing_country`: The country that issued the document: ISO 3166-1 alpha-2 (`DZ`).
    - `issuing_country_code` (string, deprecated, optional): Legacy name: use `issuing_country`. The previous spelling of `issuing_country`: The country that issued the document: ISO 3166-1 alpha-2 (`DZ`).
    - `issuingCountryCode` (string, deprecated, optional): Legacy name: use `issuing_country`. The previous spelling of `issuing_country`: The country that issued the document: ISO 3166-1 alpha-2 (`DZ`).
    - `expiryDate` (string, deprecated, optional): Legacy name: use `expiry_date`. The previous spelling of `expiry_date`: `YYYY-MM-DD`.
    - `expires_on` (string, deprecated, optional): Legacy name: use `expiry_date`. The previous spelling of `expiry_date`: `YYYY-MM-DD`.
    - `documentNumber` (string, deprecated, optional): Legacy name: use `number`. The previous spelling of `number`: The document number: 1 to 40 letters, digits, spaces and `- .
    - `unique_identifier` (string, deprecated, optional): Legacy name: use `number`. The previous spelling of `number`: The document number: 1 to 40 letters, digits, spaces and `- .
  - `id` (string, optional): An id of yours for the passenger.
  - `firstName` (string, deprecated, optional): Legacy name: use `first_name`. The previous spelling of `first_name`: Given name as in the passport.
  - `givenName` (string, deprecated, optional): Legacy name: use `first_name`. The previous spelling of `first_name`: Given name as in the passport.
  - `given_name` (string, deprecated, optional): Legacy name: use `first_name`. The previous spelling of `first_name`: Given name as in the passport.
  - `lastName` (string, deprecated, optional): Legacy name: use `last_name`. The previous spelling of `last_name`: Family name as in the passport.
  - `familyName` (string, deprecated, optional): Legacy name: use `last_name`. The previous spelling of `last_name`: Family name as in the passport.
  - `family_name` (string, deprecated, optional): Legacy name: use `last_name`. The previous spelling of `last_name`: Family name as in the passport.
  - `surname` (string, deprecated, optional): Legacy name: use `last_name`. The previous spelling of `last_name`: Family name as in the passport.
  - `dateOfBirth` (string, deprecated, optional): Legacy name: use `date_of_birth`. The previous spelling of `date_of_birth`: `YYYY-MM-DD`, a real date that is not in the future (tomorrow is still accepted: time-zone slack).
  - `bornOn` (string, deprecated, optional): Legacy name: use `date_of_birth`. The previous spelling of `date_of_birth`: `YYYY-MM-DD`, a real date that is not in the future (tomorrow is still accepted: time-zone slack).
  - `born_on` (string, deprecated, optional): Legacy name: use `date_of_birth`. The previous spelling of `date_of_birth`: `YYYY-MM-DD`, a real date that is not in the future (tomorrow is still accepted: time-zone slack).
  - `dob` (string, deprecated, optional): Legacy name: use `date_of_birth`. The previous spelling of `date_of_birth`: `YYYY-MM-DD`, a real date that is not in the future (tomorrow is still accepted: time-zone slack).
  - `identityDocuments` (array, deprecated, optional): Legacy name: use `identity_documents`. The previous spelling of `identity_documents`: The passport, up to 5 documents.
  - `documents` (array, deprecated, optional): Legacy name: use `identity_documents`. The previous spelling of `identity_documents`: The passport, up to 5 documents.
  - `passengerType` (string, deprecated, optional): Legacy name: use `type`. The previous spelling of `type`: `adult`, `senior`, `young_adult`, `child`, `seated_infant` or `infant` (a lap infant), in any letter case.
- `contact_details` (object, required): How to reach the traveler. Both fields are required.
  - `email` (string, required): The e-mail of the contact, 254 characters at most.
  - `phone_number` (string, required): An international phone number (`+213...`): Duffel validates the format. FX-Port accepts digits with spaces, dots, dashes, slashes and brackets, one leading `+`, 5 to 24 digits and 32 characters at most; a dial code alone (`+213`) is `400 invalid_phone_number`.
  - `phone` (string, deprecated, optional): Legacy name: use `phone_number`. The previous spelling of `phone_number`: An international phone number (`+213...`): Duffel validates the format.
  - `phoneNumber` (string, deprecated, optional): Legacy name: use `phone_number`. The previous spelling of `phone_number`: An international phone number (`+213...`): Duffel validates the format.
  - `mobile` (string, deprecated, optional): Legacy name: use `phone_number`. The previous spelling of `phone_number`: An international phone number (`+213...`): Duffel validates the format.
  - `emailAddress` (string, deprecated, optional): Legacy name: use `email`. The previous spelling of `email`: The e-mail of the contact, 254 characters at most.
- `send_email` (boolean, optional): `false` suppresses the confirmation e-mail. Default `true`.
- `commission_override` (object, optional): Replaces your agency commission for this booking: `pricing.agencyCommission` and `pricing.finalPrice` change, `b2bPrice` does not.
  - `type` (string, required): `percentage` is a share of `b2bPrice`; `fixed` is `value` times the number of travelers. One of `percentage`, `fixed`.
  - `value` (number, required): A number from 0. Numeric text such as `"10"` is read as a number with a `coerced_value` warning. At least 0.
- `agent_id` (string, optional): Your own id of the agent who books; stored on the booking.
- `agency_id` (string, optional): Not needed: the agency of a call is always the one of your API key. The field is accepted so that older clients keep working, and it is ignored.
- `searchId` (string, deprecated, optional): Legacy name: use `search_id`. The previous spelling of `search_id`: The `searchId` of the supplier entry the priced offer came from.
- `offerId` (string, deprecated, optional): Legacy name: use `offer_id`. The previous spelling of `offer_id`: The `id` of the priced offer.
- `bookingType` (string, deprecated, optional): Legacy name: use `booking_type`. The previous spelling of `booking_type`: `issue` (default) for instant ticketing, `hold` for an unpaid reservation when `supportHold` is `true` after pricing.
- `sendEmail` (boolean, deprecated, optional): Legacy name: use `send_email`. The previous spelling of `send_email`: `false` suppresses the confirmation e-mail.
- `commissionOverride` (object, deprecated, optional): Legacy name: use `commission_override`. The previous spelling of `commission_override`: `commission_override`.
- `agentId` (string, deprecated, optional): Legacy name: use `agent_id`. The previous spelling of `agent_id`: Your own id of the agent who books; stored on the booking.
- `contact` (object, deprecated, optional): Legacy name: use `contact_details`. The previous spelling of `contact_details`: `contact_details`.
- `contacts` (object, deprecated, optional): Legacy name: use `contact_details`. The previous spelling of `contact_details`: `contact_details`.
- `contactDetails` (object, deprecated, optional): Legacy name: use `contact_details`. The previous spelling of `contact_details`: `contact_details`.
- `agencyId` (string, deprecated, optional): Legacy name: use `agency_id`. The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.

### Example request

```bash
curl https://api.fx-port.com/api/v1/flights/book_flight \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "search_id": "fs_85ac5f12-7456-4edf-a2fb-1d51239d278b",
  "offer_id": "fx_offer_9de60e2a",
  "booking_type": "hold",
  "send_email": false,
  "passengers": [
    {
      "type": "adult",
      "gender": "MALE",
      "first_name": "James",
      "last_name": "Smith",
      "date_of_birth": "1988-06-15",
      "identity_documents": [
        {
          "type": "passport",
          "number": "A12345678",
          "issuing_country": "DZ",
          "expiry_date": "2031-04-18"
        }
      ]
    }
  ],
  "contact_details": {
    "email": "traveler@example.com",
    "phone_number": "+15555550123"
  }
}'
```

## Response

### 200: The order. Keep `booking.reference` (the supplier's order id), `booking.pnr` and `postProcessing.bookingDbId`.

- `environment` (string): The environment of the key that made the call: `sandbox` for fxp_test_ keys, `live` for fxp_live_ keys. Present in every JSON body. One of `sandbox`, `live`.
- `request_id` (string): The id of the request, also in the `X-Request-Id` header.
- `warnings` (array of object): Present only when the request used a previous spelling of a field or something the endpoint ignored.
  - `code` (string): What the warning is about. One of `deprecated_field`, `ignored_field`, `coerced_value`, `deprecated_value`, `deprecated_endpoint`.
  - `field` (string): The request field the warning is about.
  - `replacement` (string): The name to use instead, for `deprecated_*` warnings.
  - `message` (string): A sentence that says what happened and what to do.
  - `documentation_url` (string): Where the naming rules are explained.
- `success` (boolean): `true` when the order was created (booking) or paid (payment).
- `requestId` (string, deprecated): Legacy name: use `request_id`. The id of this request, 8 characters, for support.
- `paymentSucceeded` (boolean): `pay_order` only: whether the payment succeeded. Read it together with `success`.
- `searchId` (string, deprecated): Legacy name: use `search_id`. `book_flight` only: the `search_id` you sent.
- `search_id` (string): `book_flight` only: the `search_id` you sent. The snake_case twin of `searchId`, with the same value: use this name.
- `offerId` (string, deprecated): Legacy name: use `offer_id`. `book_flight` only: the `offer_id` you sent.
- `offer_id` (string): `book_flight` only: the `offer_id` you sent. The snake_case twin of `offerId`, with the same value: use this name.
- `orderId` (string, deprecated): Legacy name: use `order_id`. `pay_order` only: the supplier's order id.
- `order_id` (string): `pay_order` only: the supplier's order id. The snake_case twin of `orderId`, with the same value: use this name.
- `processingTime` (number, deprecated): Legacy name: use `processing_time`. Seconds spent.
- `processing_time` (number): Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name.
- `paymentDetails` (object): `pay_order` only: `paymentId`, `status`, `type` (`issuance`), `paymentAmount`, `currency`, `createdAt`, `message`.
- `booking` (object): The order as the supplier knows it.
  - `reference` (string, deprecated): Legacy name: use `order_id`. The supplier's order id (`ord_...` for Duffel, a base64-like text for Amadeus; for a cancelled hold of Amadeus Algeria the PNR). Send it as `order_id` to `pay_order`, `cancel_order` and `cancel_quote`.
  - `order_id` (string): The supplier's order id (`ord_...` for Duffel, a base64-like text for Amadeus; for a cancelled hold of Amadeus Algeria the PNR). Send it as `order_id` to `pay_order`, `cancel_order` and `cancel_quote`. The snake_case twin of `reference`, with the same value: use this name.
  - `status` (string): `confirmed`, `pending` (a hold that is not paid yet), `awaiting_payment`, `cancelled`, `failed` or `partial_refund`.
  - `pnr` (string): The airline record locator. Not unique across suppliers and airlines.
  - `type` (string): `instant`: ticketed at once. `hold`: reserved, not paid. One of `instant`, `hold`.
  - `createdAt` (string): When the order was created.
  - `cancelledAt` (string, nullable): When the order was cancelled.
  - `content` (string): Who manages the order: `managed`.
  - `syncedAt` (string): When FX-Port last synchronised the order with the supplier.
  - `availableActions` (array of string): What can still be done with the order.
  - `voidableUntil` (string, nullable): ISO 8601 UTC end of the same-day void window.
  - `cancellation` (object, nullable): How the order was cancelled: `type`, `deleted`, `deletedAt`.
- `supplier` (object): The supplier of the order.
  - `id` (string, deprecated): Legacy name: use `supplier_id`. Id of the supplier.
  - `supplier_id` (string): Id of the supplier. The snake_case twin of `id`, with the same value: use this name.
  - `technology` (string): The technology of the supplier: `amadeus_aqc` or `duffel`.
  - `name` (string, deprecated): Legacy name: use `supplier_name`. Name of the supplier.
  - `supplier_name` (string): Name of the supplier. The snake_case twin of `name`, with the same value: use this name.
  - `offerId` (string, nullable, deprecated): Legacy name: use `supplier_offer_id`. The supplier's own id of the offer.
  - `supplier_offer_id` (string, nullable): The supplier's own id of the offer. The snake_case twin of `offerId`, with the same value: use this name.
- `agency` (object): Your agency. The id of the agency is not returned: you know it.
  - `currency` (string): ISO 4217 currency of your agency. Every amount of the response is in it.
- `pricing` (object): The agency-facing price of an offer, a booking or a quote. Every amount is in `agencyCurrency`; the rate, when the currencies differ, is `exchangeRate` at the top level of the response.
  - `supplierCurrency` (string): ISO 4217 currency in which the supplier prices the fare.
  - `agencyCurrency` (string): ISO 4217 currency of your agency. Every amount of the block is in it.
  - `basePrice` (number, deprecated): Legacy name: use `base_price`. The airline's base fare, before taxes, in the agency currency.
  - `base_price` (number): The airline's base fare, before taxes, in the agency currency. The snake_case twin of `basePrice`, with the same value: use this name.
  - `taxesAndFees` (number, deprecated): Legacy name: use `taxes_and_fees`. Taxes plus the platform's fees, folded together. Excludes your agency commission.
  - `taxes_and_fees` (number): Taxes plus the platform's fees, folded together. Excludes your agency commission. The snake_case twin of `taxesAndFees`, with the same value: use this name.
  - `agencyCommission` (number, deprecated): Legacy name: use `agency_commission`. The commission of your agency on this sale.
  - `agency_commission` (number): The commission of your agency on this sale. The snake_case twin of `agencyCommission`, with the same value: use this name.
  - `finalPrice` (number, deprecated): Legacy name: use `final_price`. What the traveler pays: `basePrice + taxesAndFees + agencyCommission`.
  - `final_price` (number): What the traveler pays: `basePrice + taxesAndFees + agencyCommission`. The snake_case twin of `finalPrice`, with the same value: use this name.
  - `b2bPrice` (number, deprecated): Legacy name: use `b2b_price`. The amount debited from your agency balance: `finalPrice - agencyCommission`.
  - `b2b_price` (number): The amount debited from your agency balance: `finalPrice - agencyCommission`. The snake_case twin of `b2bPrice`, with the same value: use this name.
  - `totalAmount` (number): Order quote only: an alias of `b2bPrice`.
  - `taxAmount` (number): Order quote of a Duffel order only: the airline's taxes in your agency currency.
  - `originalTaxAmount` (number): Order quote of a Duffel order only: the same taxes in the supplier currency.
  - `additionalServices` (array of object): Paid extras. A list in the offers of a search (empty for most offers).
  - `acd` (object): Whether the fare is an ACD fare. Present on the offers of Amadeus Algeria, absent on Duffel offers. An ACD fare carries more keys with the amounts of the adjustment: they are informational, can change without notice and are not part of the contract.
    - `applied` (boolean): `true` for an ACD fare: the booking needs one passport copy for every traveler, sent with `upload_acd_documents`. `false` for an ordinary fare.
    - `scenario` (string): Only on an ACD fare: the rule that adjusted the price. Informational. One of `A`, `B`.
    - `exempt` (boolean): `true` when the search named an ACD-exempt company of your agency: the fare is priced without ACD.
    - `reason` (string): Only on some ACD fares: why the rule applied. Informational; the wording can change.
- `requiresACD` (boolean): Present only when `true`: the booking needs one passport copy for every traveler: see `upload_acd_documents`.
- `payment` (object): The payment state of the order.
  - `status` (string): `awaiting_payment` for a hold, `paid` once ticketed.
  - `paidAt` (string, nullable): When the order was paid.
  - `paymentRequiredBy` (string, nullable): The payment deadline of a hold, when the supplier gives one.
  - `priceGuaranteeExpiresAt` (string, nullable): Until when the price is guaranteed.
  - `awaitingPayment` (boolean): `true` for a hold that still has to be paid.
- `documents` (array of object): The e-tickets. Empty for a hold.
  - `type` (string): `electronic_ticket`.
  - `uniqueIdentifier` (string): The ticket number (Duffel sandbox: the text `1`).
  - `passengerIds` (array of string): The passengers the document is for.
  - `documentType` (string): `ETICKET`.
  - `status` (string): `ISSUED`.
  - `segmentIds` (array of string): The segments the document covers.
- `bookingReferences` (array of object): The record locators.
  - `pnr` (string): The airline record locator.
  - `carrier` (object): The airline that issued the record locator.
    - `iataCode` (string): IATA code of the airline.
    - `name` (string): Name of the airline.
    - `logoUrl` (string): Address of the logo.
    - `logoLockupUrl` (string, nullable): Address of the wide logo.
- `passengers` (array of object): The passengers.
  - `id` (string, deprecated): Legacy name: use `passenger_id`. Id of the passenger on the order: `"1"` (Amadeus) or `pas_...` (Duffel).
  - `passenger_id` (string): Id of the passenger on the order: `"1"` (Amadeus) or `pas_...` (Duffel). The snake_case twin of `id`, with the same value: use this name.
  - `type` (string, deprecated): Legacy name: use `passenger_type`. `ADULT`, `CHILD`, `INFANT_WITHOUT_SEAT` ... as the supplier writes it.
  - `passenger_type` (string): `ADULT`, `CHILD`, `INFANT_WITHOUT_SEAT` ... as the supplier writes it. The snake_case twin of `type`, with the same value: use this name.
  - `title` (string): `mr`, `mrs` ... derived from the gender.
  - `givenName` (string, deprecated): Legacy name: use `first_name`. Given name, as stored (Amadeus writes it in upper case).
  - `first_name` (string): Given name, as stored (Amadeus writes it in upper case). The snake_case twin of `givenName`, with the same value: use this name.
  - `familyName` (string, deprecated): Legacy name: use `last_name`. Family name, as stored.
  - `last_name` (string): Family name, as stored. The snake_case twin of `familyName`, with the same value: use this name.
  - `gender` (string): `male` or `female`.
  - `bornOn` (string, deprecated): Legacy name: use `date_of_birth`. Date of birth, `YYYY-MM-DD`.
  - `date_of_birth` (string): Date of birth, `YYYY-MM-DD`. The snake_case twin of `bornOn`, with the same value: use this name.
  - `email` (string): The e-mail of the contact.
  - `phoneNumber` (string, deprecated): Legacy name: use `phone_number`. The phone number of the contact.
  - `phone_number` (string): The phone number of the contact. The snake_case twin of `phoneNumber`, with the same value: use this name.
  - `infantPassengerId` (string, nullable): The id of the lap infant this adult carries.
  - `loyaltyProgrammeAccounts` (array of object): Frequent-flyer accounts.
  - `ticketNumber` (string, nullable): The ticket number of this passenger. `null` until the airline returns it.
- `travelerPricing` (array of object): The price of each traveler.
  - `travelerId` (string, deprecated): Legacy name: use `passenger_id`. `"1"`, `"2"` ... for Amadeus, `pas_...` for Duffel.
  - `passenger_id` (string): `"1"`, `"2"` ... for Amadeus, `pas_...` for Duffel. The snake_case twin of `travelerId`, with the same value: use this name.
  - `travelerType` (string, deprecated): Legacy name: use `passenger_type`. `ADULT`, `SENIOR`, `YOUNG`, `CHILD`, `SEATED_INFANT` or `HELD_INFANT` (a lap infant).
  - `passenger_type` (string): `ADULT`, `SENIOR`, `YOUNG`, `CHILD`, `SEATED_INFANT` or `HELD_INFANT` (a lap infant). The snake_case twin of `travelerType`, with the same value: use this name.
  - `fareOption` (string): The fare option, for example `STANDARD`.
  - `price` (object): The price of one traveler.
    - `supplierCurrency` (string): ISO 4217 currency in which the supplier prices the fare.
    - `agencyCurrency` (string): ISO 4217 currency of your agency.
    - `base` (number): The traveler's base fare in the agency currency.
    - `total` (number): The traveler's final price: what this traveler pays. The totals of all travelers add up to `pricing.finalPrice`.
    - `b2bPrice` (number): The traveler's share of the amount debited from your balance.
  - `fareDetailsBySegment` (array of object): The fare of this traveler on every segment.
    - `segmentId` (string): The `id` of the segment this fare detail is about.
    - `cabin` (string): Cabin of the fare on this segment. One of `ECONOMY`, `PREMIUM_ECONOMY`, `BUSINESS`, `FIRST`.
    - `fareBasis` (string, nullable): Fare basis code.
    - `brandedFare` (string, nullable): Fare family code.
    - `brandedFareLabel` (string, nullable): Display label of the fare family.
    - `bookingClass` (string, nullable): Booking class letter.
    - `includedCheckedBags` (object): The checked baggage included in the fare.
      - `quantity` (integer): Number of checked bags included in the fare.
    - `includedCabinBags` (object): The cabin baggage included in the fare.
      - `quantity` (integer): Number of cabin bags included in the fare.
    - `amenities` (array of object): What else the fare includes or sells (seat, bag, meal, Wi-Fi ...).
- `itinerary` (array of object): The flights of the order.
  - `direction` (string): `outbound`, `return` ...
  - `duration` (string): ISO 8601 duration.
  - `stops` (integer): Number of stops.
  - `fareBrandName` (string): Name of the fare brand.
  - `origin` (object): Where this direction starts.
    - `airportCode` (string): IATA code of the airport.
    - `airportName` (string): Name of the airport.
    - `cityName` (string): Name of the city.
    - `countryCode` (string): ISO 3166-1 alpha-2 country code.
    - `timeZone` (string): IANA time zone.
  - `destination` (object): Where this direction ends.
    - `airportCode` (string): IATA code of the airport.
    - `airportName` (string): Name of the airport.
    - `cityName` (string): Name of the city.
    - `countryCode` (string): ISO 3166-1 alpha-2 country code.
    - `timeZone` (string): IANA time zone.
  - `segments` (array of object): The flights of this direction.
    - `id` (string): Id of the segment.
    - `departure` (object): Where and when a segment leaves or arrives.
      - `airportCode` (string): IATA code of the airport.
      - `airportName` (string): Name of the airport.
      - `cityName` (string): Name of the city.
      - `countryCode` (string): ISO 3166-1 alpha-2 country code.
      - `terminal` (string, nullable): Terminal, when the supplier says.
      - `datetime` (string): Local date and time at the airport, `YYYY-MM-DDTHH:MM:SS`, without a time zone.
      - `timeZone` (string): IANA time zone of the airport.
    - `arrival` (object): Where and when a segment leaves or arrives.
      - `airportCode` (string): IATA code of the airport.
      - `airportName` (string): Name of the airport.
      - `cityName` (string): Name of the city.
      - `countryCode` (string): ISO 3166-1 alpha-2 country code.
      - `terminal` (string, nullable): Terminal, when the supplier says.
      - `datetime` (string): Local date and time at the airport, `YYYY-MM-DDTHH:MM:SS`, without a time zone.
      - `timeZone` (string): IANA time zone of the airport.
    - `flight` (object): The airline and the number of the flight.
      - `carrierCode` (string): IATA code of the marketing airline.
      - `flightNumber` (string): The flight number.
      - `operatingCarrier` (string): IATA code of the operating airline.
    - `duration` (string): ISO 8601 duration.
    - `cabin` (string): The cabin.
    - `fareBasis` (string, nullable): Fare basis code.
    - `passengerDetails` (array of object): What each passenger has on this segment.
  - `conditions` (object, nullable): What the fare allows before departure. Indicative only. A Duffel order can carry `null` for both when the airline does not say.
    - `refundBeforeDeparture` (object, nullable): What the fare says about a refund before departure.
      - `allowed` (boolean): Whether a refund is allowed before departure. Indicative, not a promise: the fare rules of the airline decide.
    - `changeBeforeDeparture` (object, nullable): What the fare says about a change before departure.
      - `allowed` (boolean): Whether a change is allowed before departure. Indicative, not a promise: the fare rules of the airline decide.
- `conditions` (object): What the fare allows before departure. Indicative only. A Duffel order can carry `null` for both when the airline does not say.
  - `refundBeforeDeparture` (object, nullable): What the fare says about a refund before departure.
    - `allowed` (boolean): Whether a refund is allowed before departure. Indicative, not a promise: the fare rules of the airline decide.
  - `changeBeforeDeparture` (object, nullable): What the fare says about a change before departure.
    - `allowed` (boolean): Whether a change is allowed before departure. Indicative, not a promise: the fare rules of the airline decide.
- `fareRules` (object): What exchange, refund and revalidation allow.
- `owner` (object): The airline that owns the order.
  - `id` (string, nullable): Duffel's id of the airline.
  - `iataCode` (string): IATA code of the airline that owns the order.
  - `name` (string): Name of the airline.
  - `logoUrl` (string): Address of the logo.
  - `logoLockupUrl` (string, nullable): Address of the wide logo.
  - `conditionsOfCarriageUrl` (string, nullable): Address of the conditions of carriage.
- `services` (array of object): Extra services of the order (bags, seats). Empty.
- `meta` (object): Capabilities of the order and what the supplier reports about it.
  - `supportHold` (boolean, deprecated): Legacy name: use `support_hold`. Whether the supplier can hold.
  - `support_hold` (boolean): Whether the supplier can hold. The snake_case twin of `supportHold`, with the same value: use this name.
  - `supportVoid` (boolean, deprecated): Legacy name: use `support_void`. Whether the supplier can void the ticket the same day.
  - `support_void` (boolean): Whether the supplier can void the ticket the same day. The snake_case twin of `supportVoid`, with the same value: use this name.
  - `voidDeadline` (string, nullable, deprecated): Legacy name: use `void_deadline`. End of the same-day void window.
  - `void_deadline` (string, nullable): End of the same-day void window. The snake_case twin of `voidDeadline`, with the same value: use this name.
  - `voidWindowEndsAt` (string, nullable): End of the void window, when the supplier gives it.
  - `airlineInitiatedChanges` (array of object): Schedule changes the airline made.
  - `changes` (array of object): Changes of the order.
  - `users` (array of object): Users of the order.
  - `orderMetadata` (object, nullable): Supplier-specific metadata of the order.
- `exchangeRate` (number, deprecated): Legacy name: use `exchange_rate`. Only when the supplier's currency is not your agency's.
- `exchange_rate` (number): Only when the supplier's currency is not your agency's. The snake_case twin of `exchangeRate`, with the same value: use this name.
- `internalBooking` (object): The FX-Port identifiers of the booking.
  - `bookingId` (string): The PNR.
  - `internalId` (string): The FX-Port id of the booking (a UUID), the same as `postProcessing.bookingDbId` of the booking call.
- `postProcessing` (object): What FX-Port did after the supplier call.
  - `bookingDbId` (string): The FX-Port id of the booking (a UUID). **The best key to store**: it is accepted as `booking_id` by `get_booking`.
  - `bucketLink` (string, nullable): Address of the PDF of the ticket, `null` until it is generated. A public link.
  - `emailSent` (boolean): Whether the confirmation e-mail went out.
  - `bookingStatusUpdated` (boolean): `cancel_order`: whether the FX-Port booking was updated.
  - `refundMethod` (string): `confirm_cancel`: where the refund went: `balance`.
- `balanceDebited` (boolean): `book_flight` only. Always `false` in the sandbox: nothing is charged.
- `transactionCreated` (boolean): `book_flight` only. Always `false` in the sandbox.
- `payoutCreated` (boolean): `book_flight` only. Always `false` in the sandbox.
- `error` (object): Only when `success` is `false`: the refusal of the supplier for `pay_order`.
  - `message` (string): The supplier's message.
  - `code` (string): The supplier's code: a number as text for Amadeus (`4926`), a name for Duffel (`order_not_cancellable`).
  - `type` (string): The supplier's error type.
  - `details` (array of object): The supplier's details, in its own format.
  - `userMessage` (string): A sentence the supplier wrote for the traveler, when there is one.
- `errors` (array of object): Only when `success` is `false`: the unified error list. `errors[0].code` is the supplier's code in lower case and `errors[0].type` is `supplier_error`.
  - `code` (string): Stable, machine-readable code in snake_case. Branch on this, never on the message.
  - `type` (string): The family of the error: tells you how to react. One of `authentication_error`, `permission_error`, `validation_error`, `not_found`, `conflict`, `payment_error`, `rate_limit_error`, `supplier_error`, `internal_error`.
  - `title` (string): A short label of the error.
  - `message` (string): A sentence for a human: what is wrong and how to fix it. Wording can change; the code does not.
  - `field` (string, nullable): The request field the error is about, when there is one.
  - `details` (object, nullable): Structured extras of the error (for example `available` and `required` for an insufficient balance).
  - `documentation_url` (string): The documentation of this error code.

```json
{
  "environment": "sandbox",
  "success": true,
  "requestId": "66e4b1a8",
  "searchId": "fs_85ac5f12-7456-4edf-a2fb-1d51239d278b",
  "offerId": "fx_offer_9de60e2a",
  "processingTime": 2.6235511302948,
  "booking": {
    "reference": "eJzTd9ePNA9wdA0FAAs8AmE",
    "status": "pending",
    "pnr": "Y7PAEU",
    "type": "hold",
    "createdAt": "2026-11-01T15:26:00.000",
    "cancelledAt": null,
    "content": "managed",
    "syncedAt": "2026-11-01T15:26:00.000",
    "availableActions": [
      "confirm",
      "cancel"
    ],
    "voidableUntil": null,
    "cancellation": null
  },
  "supplier": {
    "id": "amadeus_aqc_dz_1",
    "technology": "amadeus_aqc",
    "name": "Amadeus AQC",
    "offerId": "3"
  },
  "agency": {
    "currency": "DZD"
  },
  "pricing": {
    "supplierCurrency": "DZD",
    "agencyCurrency": "DZD",
    "basePrice": 3160,
    "taxesAndFees": 1765.44,
    "agencyCommission": 4.93,
    "finalPrice": 4930.37,
    "b2bPrice": 4925.44,
    "additionalServices": [],
    "acd": {
      "applied": false
    }
  },
  "payment": {
    "status": "awaiting_payment",
    "paidAt": null,
    "paymentRequiredBy": null,
    "priceGuaranteeExpiresAt": null,
    "awaitingPayment": true
  },
  "documents": [],
  "bookingReferences": [
    {
      "pnr": "Y7PAEU",
      "carrier": {
        "iataCode": "AH",
        "name": "Air Algerie",
        "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
        "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg"
      }
    }
  ],
  "passengers": [
    {
      "id": "1",
      "type": "ADULT",
      "title": "mr",
      "givenName": "JAMES",
      "familyName": "SMITH",
      "gender": "male",
      "bornOn": "1988-06-15",
      "email": "traveler@example.com",
      "phoneNumber": "15555550123",
      "infantPassengerId": null,
      "loyaltyProgrammeAccounts": [],
      "ticketNumber": null
    }
  ],
  "travelerPricing": [
    {
      "travelerId": "1",
      "travelerType": "ADULT",
      "fareOption": "STANDARD",
      "price": {
        "supplierCurrency": "DZD",
        "agencyCurrency": "DZD",
        "base": 3160,
        "total": 4930.37,
        "b2bPrice": 4925.44
      },
      "fareDetailsBySegment": [
        {
          "segmentId": "3",
          "cabin": "ECONOMY",
          "fareBasis": "Y",
          "brandedFare": "STANDARD",
          "brandedFareLabel": "STANDARD",
          "bookingClass": "Y",
          "includedCheckedBags": {
            "quantity": 1
          },
          "includedCabinBags": {
            "quantity": 1
          },
          "amenities": []
        }
      ]
    }
  ],
  "itinerary": [
    {
      "direction": "outbound",
      "duration": "PT1H",
      "stops": 0,
      "fareBrandName": "",
      "origin": {
        "airportCode": "ALG",
        "airportName": "Houari Boumediene Airport",
        "cityName": "Algiers",
        "countryCode": "DZ",
        "timeZone": "Africa/Algiers"
      },
      "destination": {
        "airportCode": "ORN",
        "airportName": "Oran International Airport",
        "cityName": "Oran",
        "countryCode": "DZ",
        "timeZone": "Africa/Algiers"
      },
      "segments": [
        {
          "id": "3",
          "departure": {
            "airportCode": "ALG",
            "airportName": "Houari Boumediene Airport",
            "cityName": "Algiers",
            "countryCode": "DZ",
            "terminal": "1",
            "datetime": "2026-12-22T19:45:00",
            "timeZone": "Africa/Algiers"
          },
          "arrival": {
            "airportCode": "ORN",
            "airportName": "Oran International Airport",
            "cityName": "Oran",
            "countryCode": "DZ",
            "terminal": null,
            "datetime": "2026-12-22T20:45:00",
            "timeZone": "Africa/Algiers"
          },
          "flight": {
            "carrierCode": "AH",
            "carrierName": "Air Algerie",
            "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
            "flightNumber": "6106",
            "aircraftCode": "73H",
            "aircraftName": null,
            "operatingCarrier": "AH",
            "operatingCarrierName": "Air Algerie",
            "operatingFlightNumber": "6106"
          },
          "duration": "PT1H",
          "distance": null,
          "stops": [],
          "cabin": "ECONOMY",
          "cabinMarketingName": "Economy",
          "fareBasis": "Y",
          "brandedFare": "",
          "brandedFareLabel": "",
          "passengerDetails": [
            {
              "passengerId": "1",
              "cabin": "ECONOMY",
              "cabinMarketingName": "Economy",
              "seat": null,
              "checkedBags": {
                "quantity": 1
              },
              "cabinBags": {
                "quantity": 1
              }
            }
          ]
        }
      ],
      "conditions": {
        "changeBeforeDeparture": {
          "allowed": true
        }
      }
    }
  ],
  "conditions": {
    "refundBeforeDeparture": {
      "allowed": true
    },
    "changeBeforeDeparture": {
      "allowed": true
    }
  },
  "owner": {
    "id": null,
    "iataCode": "AH",
    "name": "Air Algerie",
    "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
    "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg",
    "conditionsOfCarriageUrl": null
  },
  "services": [],
  "meta": {
    "supportHold": true,
    "supportVoid": true,
    "voidDeadline": "2026-11-01T23:00:00Z",
    "voidWindowEndsAt": null,
    "airlineInitiatedChanges": [],
    "changes": [],
    "users": [],
    "orderMetadata": null
  },
  "fareRules": {
    "refund": {
      "allowed": true
    },
    "exchange": {
      "allowed": true
    },
    "agencyCurrency": "DZD"
  },
  "postProcessing": {
    "bookingDbId": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
    "bucketLink": null,
    "emailSent": false
  },
  "balanceDebited": false,
  "transactionCreated": false,
  "payoutCreated": false
}
```

## Errors

| Status | Code | Example message |
| --- | --- | --- |
| 400 | `bad_request` | Missing required parameters: search_id and offer_id are required |
| 400 | `missing_field` | `passengers[0].first_name` is required. |
| 400 | `invalid_booking_type` | `booking_type` must be "issue" (instant ticket) or "hold". |
| 400 | `hold_not_supported` | This offer cannot be held; it must be booked instantly. |
| 400 | `too_many_passengers` | A booking takes at most 18 passengers. |
| 402 | `insufficient_balance` | Agency balance is insufficient. Available: 120.0 DZD, Required: 29876.81 DZD. |
| 403 | `read_write_permission_required` | This endpoint requires an API key with read_write permission. |
| 404 | `SEARCH_EXPIRED_OR_NOT_FOUND` | Search ID 'fs_does_not_exist' not found or has expired |
| 404 | `OFFER_NOT_FOUND_IN_SEARCH` | Offer ID 'fx_offer_00000000' not found in search results |
| 404 | `OFFER_NOT_PRICED` | Priced offer 'fx_offer_250b4eb7' not found in cache. Please price the offer first before booking. |
| 409 | `OFFER_ALREADY_BOOKED` | This offer has already been booked successfully (PNR: BOKT52). Duplicate submissions are not allowed. |
| 409 | `offer_request_already_booked` | Field 'selected_offers' has offers included in a offer request that has already been booked; please perform a new search |
| 422 | `validation_type` | Field 'born_on' is invalid. Expected a date in ISO 8601 format (YYYY-MM-DD) |
| 500 | `post_booking_failed` | The booking was placed but could not be recorded completely. Contact support with this request id before retrying. |
| 503 | `supplier_temporarily_unavailable` | Supplier 'duffel_1' circuit breaker is open: retry in a few moments. |
| 503 | `balance_lock_timeout` | The agency balance is busy with another operation. Retry in a few seconds. |

See [Errors](https://fx-port.com/docs/errors.md) for every code and how to react.

## Related guides

- [Book a flight](https://fx-port.com/docs/flights/book.md)
- [Flight passengers & ages](https://fx-port.com/docs/flights/passengers.md)
- [Booking flows](https://fx-port.com/docs/flights/booking-flows.md)
- [Upload ACD documents](https://fx-port.com/docs/flights/acd-documents.md)
