{
  "openapi": "3.1.0",
  "info": {
    "title": "FX-Port API",
    "version": "1.0.0",
    "description": "FX-Port is one API for flights, hotels and eSIMs: you integrate once and reach many suppliers, with prices in your agency's currency.\n\nEvery endpoint lives under `https://api.fx-port.com/api/v1`. Authenticate with `Authorization: Bearer <key>`. A key starting with `fxp_test_` works on the sandbox and a key starting with `fxp_live_` on live; there is no other switch. Every JSON body carries `environment`, and failures carry the same error body everywhere (`errors[]` with a stable `code`).\n\nNames: requests use `snake_case`. Older camelCase spellings still work and answer with a `warnings` array that names the spelling to use; responses keep their older keys and add `snake_case` twins (`?naming=snake` returns snake_case only). See the Conventions guide.",
    "contact": {
      "name": "FX-Port developer support",
      "email": "dev@fx-port.com",
      "url": "https://fx-port.com/docs"
    },
    "x-updated": "2026-11-01"
  },
  "servers": [
    {
      "url": "https://api.fx-port.com/api/v1",
      "description": "Sandbox and live: the key decides"
    }
  ],
  "tags": [
    {
      "name": "Flights",
      "description": "Search flights across suppliers (as one JSON answer or as server-sent events), price and book an offer, hold it and pay later, read bookings and tickets, cancel, and upload the passport copies an ACD booking needs. The sandbox has two suppliers: Amadeus Algeria (`amadeus_aqc_dz_1`, routes that touch Algeria; hold, void, flexible dates) and Duffel (`duffel_1`, worldwide; instant ticketing)."
    },
    {
      "name": "Hotels",
      "description": "Geo search lists and prices the hotels around a point (`search_advanced` with a latitude, a longitude and a radius) or one hotel by id. Around it: locations and name search, hotel content as a compact card (mini) or a full document, rates, pre-book, book, voucher and cancel. A sandbox key searches and prices any hotel but books only the supplier test hotels. Booking confirms the hotel: there is no hold or pay-later option for API keys."
    },
    {
      "name": "eSIMs",
      "description": "Browse eSIM packages, order eSIMs, top them up and read usage and installation instructions. Orders and topups need a Read+Write key; everything else needs a Read key. Amounts are in your agency currency."
    },
    {
      "name": "Account",
      "description": "Balances, balance history, booking history and the detail of any booking. Every endpoint needs a Read key and takes the agency from the key: never send `agency_id`."
    },
    {
      "name": "Suppliers",
      "description": "The suppliers your key can reach and what each one supports."
    }
  ],
  "paths": {
    "/flights/airports": {
      "get": {
        "operationId": "searchAirports",
        "tags": [
          "Flights"
        ],
        "summary": "Search airports",
        "description": "Airport and city autocomplete for the origin and destination of a search: send what the user has typed (an IATA code, a city or an airport name) and use the `ap` of the chosen record as `origin` or `destination`.\n\nNo key is needed. The endpoint is built for a debounced typeahead: wait about 300 ms after the last keystroke and cancel the request that is in flight. The answer is a flat array with no `environment` key, and it can be kept for a minute by a browser and five minutes by a CDN (`Cache-Control: public, max-age=60, s-maxage=300`). A city code such as `PAR` (`isMetroOnly: true`) is valid as an origin or a destination and stands for every airport of the city.\n\nThe lookups of one IP address are limited: a `429` carries `Retry-After`. When the lookup service is down the endpoint can answer `200` with up to 10 airports from a local list instead of an error.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "What the user typed: an IATA code (`ALG`), a city (`Paris`) or an airport name. 1 to 64 characters once cleaned: control characters are removed and runs of spaces become one.",
            "schema": {
              "type": "string",
              "description": "An IATA code, a city or an airport name."
            },
            "example": "ALG"
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "The language of the names, `ll` or `ll-RR` (`en`, `fr`, `ar`, `es-AR`, `zh-CN` ...). Case and `_` or `-` are normalised (`FR` is `fr`); anything else is read as `en`. A well-formed locale the lookup does not know is not rejected: the names come back in English.",
            "schema": {
              "type": "string",
              "description": "A locale.",
              "default": "en"
            },
            "example": "en"
          }
        ],
        "responses": {
          "200": {
            "description": "A flat **array** of airports and cities, best match first. A real record has about 40 keys: only the ones listed in the schema are meant for your use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FlightAirport"
                  }
                },
                "examples": {
                  "alg": {
                    "value": [
                      {
                        "id": "ALG",
                        "displayname": "Houari Boumediene, Algiers, Algeria, (ALG)",
                        "loctype": "ap",
                        "cid": 3,
                        "rid": 480,
                        "ctid": 10465,
                        "lat": 36.6985,
                        "lng": 3.20672,
                        "cc": "DZ",
                        "country": "Algeria",
                        "rc": "AL",
                        "cityname": "Algiers, Algeria",
                        "timezone": "Africa/Algiers",
                        "utc": "+01:00",
                        "airportname": "Houari Boumediene",
                        "ap": "ALG",
                        "apicode": "ALG",
                        "box_maxX": 3.26672,
                        "box_maxY": 36.7335,
                        "box_minX": 3.1467199999999997,
                        "box_minY": 36.663500000000006,
                        "cityonly": "Algiers",
                        "destination_images": {
                          "image_jpeg": "https://content.r9cdn.net/rimg/dimg/a2/04/dd9815df-city-10465-164dc51f899.jpg?width=128&height=128&xhint=2298&yhint=1960&outputtype=JPEG&crop=true",
                          "image_webp": "https://content.r9cdn.net/rimg/dimg/a2/04/dd9815df-city-10465-164dc51f899.jpg?width=128&height=128&xhint=2298&yhint=1960&outputtype=WEBP&crop=true"
                        },
                        "displayType": {
                          "type": "airport",
                          "displayName": "Airport"
                        },
                        "entityKey": "place:Houari_Boumediene_Airport",
                        "indexId": "176626",
                        "isMetroOnly": false,
                        "kayakId": "ALG",
                        "kayakType": "ap",
                        "locationname": "Algiers, Algeria",
                        "name": "Houari Boumediene",
                        "objectID": "P189646",
                        "placeID": "176626",
                        "ptid": "AALG",
                        "region": "Algiers",
                        "searchFormPrimary": "ALG",
                        "searchFormSecondary": "Algiers, Algeria",
                        "secondary": "Algiers, Algeria",
                        "shortdisplayname": "Algiers, Algeria (ALG)",
                        "smartyDisplay": "Houari Boumediene, Algiers, Algeria, (ALG)"
                      },
                      {
                        "id": "AHO",
                        "displayname": "Alghero-Fertilia, Alghero, Sardinia, Italy, (AHO)",
                        "loctype": "ap",
                        "cid": 118,
                        "rid": 1673,
                        "ctid": 32049,
                        "lat": 40.63213,
                        "lng": 8.29077,
                        "cc": "IT",
                        "country": "Italy",
                        "rc": "SA",
                        "cityname": "Alghero, Sardinia, Italy",
                        "timezone": "Europe/Rome",
                        "utc": "+02:00",
                        "airportname": "Alghero-Fertilia",
                        "ap": "AHO",
                        "apicode": "AHO",
                        "box_maxX": 8.35077,
                        "box_maxY": 40.66712999999999,
                        "box_minX": 8.23077,
                        "box_minY": 40.59713,
                        "cityonly": "Alghero",
                        "destination_images": {
                          "image_jpeg": "https://content.r9cdn.net/rimg/dimg/ee/86/623980ff-city-32049-1700cc8a82e.jpg?width=128&height=128&xhint=2050&yhint=1293&outputtype=JPEG&crop=true",
                          "image_webp": "https://content.r9cdn.net/rimg/dimg/ee/86/623980ff-city-32049-1700cc8a82e.jpg?width=128&height=128&xhint=2050&yhint=1293&outputtype=WEBP&crop=true"
                        },
                        "displayType": {
                          "type": "airport",
                          "displayName": "Airport"
                        },
                        "entityKey": "place:Fertilia_Airport",
                        "indexId": "177457",
                        "isMetroOnly": false,
                        "kayakId": "AHO",
                        "kayakType": "ap",
                        "locationname": "Alghero, Sardinia, Italy",
                        "name": "Alghero-Fertilia",
                        "objectID": "P233628",
                        "placeID": "177457",
                        "ptid": "AAHO",
                        "region": "Sardinia",
                        "searchFormPrimary": "AHO",
                        "searchFormSecondary": "Alghero, Sardinia, Italy",
                        "secondary": "Alghero, Sardinia, Italy",
                        "shortdisplayname": "Alghero, Sardinia, Italy (AHO)",
                        "smartyDisplay": "Alghero-Fertilia, Alghero, Sardinia, Italy, (AHO)",
                        "subregion": "Sassari"
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "`query` is blank (or longer than 64 characters once cleaned): `invalid_query`; or it is missing: `validation_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "invalid_query": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_query",
                        "message": "query must be 1-64 characters.",
                        "field": "query"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_query",
                          "type": "validation_error",
                          "title": "Invalid query",
                          "message": "query must be 1-64 characters.",
                          "field": "query",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_query"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 422
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "public",
        "x-guides": [
          {
            "label": "Search airports",
            "path": "/docs/flights/airports"
          }
        ],
        "x-sample-query": "query=ALG&locale=en",
        "security": []
      }
    },
    "/flights/top-airports": {
      "get": {
        "operationId": "topAirports",
        "tags": [
          "Flights"
        ],
        "summary": "Get the most searched airports",
        "description": "The most searched airports, for the first suggestions of an empty search box before the user types anything.\n\nNo key is needed. `airport_type` chooses the ranking: `from` for departure airports (the default) or `to` for arrival airports. Add `market_region`, a country code, to rank for one market. Each airport has the fields of the airport lookup plus `popularity_score`, `popularity_scopes` (the `locale` and `global` scores), `recent_score` and `last_seen_day`. Without a key `environment` is always `live`.",
        "parameters": [
          {
            "name": "airport_type",
            "in": "query",
            "required": false,
            "description": "`from` for departure airports or `to` for arrival airports.",
            "schema": {
              "type": "string",
              "enum": [
                "from",
                "to"
              ],
              "description": "Which ranking.",
              "default": "from"
            },
            "example": "to"
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "The language of the names, read as for the airport lookup.",
            "schema": {
              "type": "string",
              "description": "A locale.",
              "default": "en"
            },
            "example": "en"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many airports, 1 to 10.",
            "schema": {
              "type": "integer",
              "description": "A number of airports.",
              "minimum": 1,
              "maximum": 10,
              "default": 5
            },
            "example": 1
          },
          {
            "name": "market_region",
            "in": "query",
            "required": false,
            "description": "A country or region code (`DZ`, `FR`): ranks the airports for one market first. Also accepted as `market_country`.",
            "schema": {
              "type": "string",
              "description": "A country or region code."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The airports and what the ranking used.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightTopAirportsResponse"
                },
                "examples": {
                  "to": {
                    "value": {
                      "environment": "live",
                      "airports": [
                        {
                          "ap": "JED",
                          "airportname": "King Abdulaziz Intl",
                          "cityname": "Jeddah, Saudi Arabia",
                          "cityonly": "Jeddah",
                          "cc": "SA",
                          "country": "Saudi Arabia",
                          "timezone": "Asia/Riyadh",
                          "utc": "+03:00",
                          "lat": 21.66982,
                          "lng": 39.15052,
                          "displayname": "King Abdulaziz Intl, Jeddah, Saudi Arabia, (JED)",
                          "shortdisplayname": "Jeddah, Saudi Arabia (JED)",
                          "loctype": "ap",
                          "isMetroOnly": false,
                          "payload_locale": "en",
                          "popularity_score": 366.667,
                          "popularity_scopes": {
                            "locale": 25.54597518549043,
                            "global": 97.28943021306868
                          },
                          "recent_score": 242,
                          "last_seen_day": "2026-11-01"
                        }
                      ],
                      "context": {
                        "locale": "en",
                        "airport_type": "to",
                        "agency_id": null,
                        "market_region": null
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`airport_type` is not `from` or `to`, or `limit` is outside 1 to 10: one entry for each problem in `errors[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "validation_error": {
                    "value": {
                      "environment": "sandbox",
                      "detail": [
                        {
                          "type": "literal_error",
                          "loc": [
                            "query",
                            "airport_type"
                          ],
                          "msg": "Input should be 'from' or 'to'"
                        }
                      ],
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Input should be 'from' or 'to'",
                          "field": "airport_type",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 422
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "public",
        "x-guides": [
          {
            "label": "Search airports",
            "path": "/docs/flights/airports#top-airports"
          }
        ],
        "x-sample-query": "airport_type=to&locale=en&limit=1",
        "security": []
      }
    },
    "/get_flights": {
      "post": {
        "operationId": "getFlights",
        "tags": [
          "Flights"
        ],
        "summary": "Search flights",
        "description": "Search flights across every compatible supplier and read the offers grouped by supplier.\n\nThe route is a one-way (`origin`, `destination`, `departure_date`), a round trip (add `return_date`) or up to six flights in `segments` for an open-jaw or multi-city trip. Values a real client can send are read (`\"2\"` for a count, `premium economy` for a cabin, a space between airline codes), with a `coerced_value` warning when their meaning had to be guessed; only garbage is refused with a `400` and a stable code, before any supplier is called.\n\n**What comes back.** `suppliers[]` has one entry for each supplier that answered, with its own `searchId` and its offers in `results.offers[]`. Keep the `searchId` of the entry an offer is in together with the `id` of the offer: `price_flight` and `book_flight` need the pair. A supplier that fails does not fail the search: it is listed in `failures[]` with a stable `cause` (`supplier_busy` or `service_overloaded` when FX-Port lacked the capacity to serve it for a moment) and the status is still `200`. Only when no supplier answers is the status `503`: `all_suppliers_failed`, or `supplier_busy` or `service_overloaded` with `Retry-After` when every supplier was refused for lack of capacity.\n\n**Size and time.** An answer takes two to eight seconds, because it waits for the slowest supplier, and weighs 0.7 MB to 14 MB of JSON with no pagination. Send `Accept-Encoding: gzip`: a typical 695 KB answer is 26 KB on the wire. Identical searches that arrive at the same moment from the same agency, environment and currency are merged into one supplier fan-out; nothing is kept afterwards, and every caller still gets its own `requestId` and headers and a `searchId` that works with `price_flight`.\n\n**Streaming.** Add `stream=true` (or send `Accept: text/event-stream`) and the same search arrives as server-sent events: the first offers after about two and a half seconds, the rest as each supplier answers. The events are listed under *Streaming events*; the Streaming guide has complete client code. Keep at most 4 streams open at the same time for one API key.\n\n**Sandbox.** Two suppliers answer: `amadeus_aqc_dz_1` (routes that touch Algeria: hold, same-day void, flexible dates) and `duffel_1` (worldwide, instant ticketing). `CDG` to `ALG` returns fares with `pricing.acd.applied: true`, which need passports after booking.",
        "parameters": [
          {
            "name": "stream",
            "in": "query",
            "required": false,
            "description": "`true` (`1` and `yes` also work) returns the answer as server-sent events (`text/event-stream`) instead of one JSON document. `Accept: text/event-stream` has the same effect. The body of the request is the same in both modes; a request that is refused is answered with an ordinary JSON error before the stream starts. Keep at most 4 streams open at the same time per API key.",
            "schema": {
              "type": "boolean",
              "description": "Stream the answer.",
              "default": false
            },
            "example": false
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightSearchRequest"
              },
              "examples": {
                "one_way": {
                  "value": {
                    "origin": "ALG",
                    "destination": "CDG",
                    "departure_date": "2026-12-15",
                    "cabin_class": "economy",
                    "passengers": {
                      "adults": 1
                    }
                  }
                },
                "round_trip": {
                  "value": {
                    "origin": "ALG",
                    "destination": "CDG",
                    "departure_date": "2026-12-15",
                    "return_date": "2026-12-22",
                    "cabin_class": "economy",
                    "passengers": {
                      "adults": 2,
                      "children": 1,
                      "child_ages": [
                        8
                      ]
                    }
                  }
                },
                "multi_city": {
                  "value": {
                    "segments": [
                      {
                        "origin": "ALG",
                        "destination": "CDG",
                        "departure_date": "2026-12-15"
                      },
                      {
                        "origin": "CDG",
                        "destination": "MAD",
                        "departure_date": "2026-12-18"
                      },
                      {
                        "origin": "MAD",
                        "destination": "ALG",
                        "departure_date": "2026-12-22"
                      }
                    ],
                    "supplier_id": "duffel_1",
                    "passengers": {
                      "adults": 1
                    }
                  }
                },
                "acd_route": {
                  "value": {
                    "origin": "CDG",
                    "destination": "ALG",
                    "departure_date": "2026-12-15",
                    "supplier_id": "amadeus_aqc_dz_1",
                    "passengers": {
                      "adults": 1
                    }
                  }
                },
                "filtered_with_matrix": {
                  "value": {
                    "origin": "ALG",
                    "destination": "CDG",
                    "departure_date": "2026-12-15",
                    "nonstop": true,
                    "included_airlines": [
                      "AH",
                      "AF"
                    ],
                    "flexible": true,
                    "passengers": {
                      "adults": 1
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The offers grouped by supplier (the JSON mode; with `stream=true` the same data arrives as the events below). A supplier that failed is in `failures` and the status is still `200`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightSearchResponse"
                },
                "examples": {
                  "two_suppliers": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "54139f32",
                      "searchType": "ONE_WAY",
                      "flexible": false,
                      "processingTime": 2.243,
                      "suppliers": [
                        {
                          "success": true,
                          "searchId": "fs_e1853430-16e2-4bcb-8f11-4f7bfbc33602",
                          "supportHold": true,
                          "supportVoid": true,
                          "voidDeadline": "2026-11-01T23:00:00Z",
                          "filtersApplied": {},
                          "warnings": [],
                          "meta": {
                            "supplierId": "amadeus_aqc_dz_1",
                            "technologyType": "amadeus_aqc",
                            "supplierName": "Amadeus Algeria",
                            "processingTime": 2.174,
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD"
                          },
                          "results": {
                            "totalOffers": 66,
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD",
                            "offers": [
                              {
                                "id": "fx_offer_b1c2a850",
                                "type": "flight_offer",
                                "supplier": {
                                  "id": "amadeus_aqc_dz_1",
                                  "name": "Amadeus Algeria",
                                  "originalId": "6"
                                },
                                "bookableSeats": 9,
                                "supportHold": true,
                                "supportVoid": true,
                                "voidDeadline": "2026-11-01T23:00:00Z",
                                "filtersApplied": {},
                                "warnings": [],
                                "instantTicketingRequired": false,
                                "pricing": {
                                  "supplierCurrency": "DZD",
                                  "agencyCurrency": "DZD",
                                  "basePrice": 18520,
                                  "taxesAndFees": 11306.17,
                                  "agencyCommission": 29.83,
                                  "finalPrice": 29856,
                                  "b2bPrice": 29826.17,
                                  "acd": {
                                    "applied": false
                                  },
                                  "additionalServices": []
                                },
                                "itinerary": [
                                  {
                                    "direction": "outbound",
                                    "duration": "PT2H30M",
                                    "stops": 0,
                                    "segments": [
                                      {
                                        "id": "27",
                                        "departure": {
                                          "airportCode": "ALG",
                                          "airportName": "Houari Boumediene Airport",
                                          "cityName": "Algiers",
                                          "countryCode": "DZ",
                                          "terminal": "4",
                                          "datetime": "2026-12-15T07:15:00",
                                          "timeZone": "Africa/Algiers"
                                        },
                                        "arrival": {
                                          "airportCode": "CDG",
                                          "airportName": "Paris Charles de Gaulle Airport",
                                          "cityName": "Paris",
                                          "countryCode": "FR",
                                          "terminal": "2D",
                                          "datetime": "2026-12-15T09:45:00",
                                          "timeZone": "Europe/Paris"
                                        },
                                        "flight": {
                                          "carrierCode": "AH",
                                          "carrierName": "AIR ALGERIE",
                                          "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                                          "flightNumber": "1002",
                                          "aircraftCode": "7M8",
                                          "aircraftName": "BOEING 737 MAX 8",
                                          "operatingCarrier": "AH",
                                          "operatingCarrierName": "AIR ALGERIE"
                                        },
                                        "duration": "PT2H30M",
                                        "cabin": "ECONOMY",
                                        "bookingClass": "Q",
                                        "fareBasis": "QDZOO",
                                        "brandedFare": "STANDARD",
                                        "brandedFareLabel": ""
                                      }
                                    ],
                                    "conditions": {
                                      "refundBeforeDeparture": {
                                        "allowed": false
                                      },
                                      "changeBeforeDeparture": {
                                        "allowed": true
                                      }
                                    }
                                  }
                                ],
                                "travelerPricing": [
                                  {
                                    "travelerId": "1",
                                    "travelerType": "ADULT",
                                    "fareOption": "STANDARD",
                                    "price": {
                                      "supplierCurrency": "DZD",
                                      "agencyCurrency": "DZD",
                                      "base": 18520,
                                      "total": 29856,
                                      "b2bPrice": 29826.17
                                    },
                                    "fareDetailsBySegment": [
                                      {
                                        "segmentId": "27",
                                        "cabin": "ECONOMY",
                                        "fareBasis": "QDZOO",
                                        "brandedFare": "STANDARD",
                                        "brandedFareLabel": "",
                                        "bookingClass": "Q",
                                        "includedCheckedBags": {
                                          "quantity": 1
                                        },
                                        "includedCabinBags": {
                                          "quantity": 1
                                        },
                                        "amenities": []
                                      }
                                    ]
                                  }
                                ],
                                "conditions": {
                                  "refundBeforeDeparture": {
                                    "allowed": false
                                  },
                                  "changeBeforeDeparture": {
                                    "allowed": true
                                  }
                                },
                                "fareRules": {
                                  "exchange": {
                                    "allowed": true
                                  },
                                  "refund": {
                                    "allowed": false
                                  },
                                  "revalidation": {
                                    "allowed": false
                                  },
                                  "supplierCurrency": "DZD",
                                  "agencyCurrency": "DZD"
                                },
                                "validatingAirline": [
                                  {
                                    "code": "AH",
                                    "name": "AIR ALGERIE"
                                  }
                                ],
                                "additionalServices": []
                              }
                            ]
                          }
                        },
                        {
                          "success": true,
                          "searchId": "fs_dfx_a3aeaa36-74ad-424b-b88d-e6bdc9a4ba94",
                          "supportHold": false,
                          "supportVoid": false,
                          "voidDeadline": null,
                          "filtersApplied": {},
                          "warnings": [],
                          "meta": {
                            "supplierId": "duffel_1",
                            "technologyType": "duffel",
                            "supplierName": "Duffel",
                            "processingTime": 1.34,
                            "supplierCurrency": "USD",
                            "agencyCurrency": "DZD"
                          },
                          "results": {
                            "totalOffers": 92,
                            "supplierCurrency": "USD",
                            "agencyCurrency": "DZD",
                            "offers": [
                              {
                                "id": "fx_offer_9ab6d940",
                                "type": "flight_offer",
                                "supplier": {
                                  "id": "duffel_1",
                                  "name": "Duffel",
                                  "originalId": "off_034q6JKdiQDZ8HIDMFus64"
                                },
                                "bookableSeats": null,
                                "supportHold": false,
                                "supportVoid": false,
                                "voidDeadline": null,
                                "filtersApplied": {},
                                "warnings": [],
                                "instantTicketingRequired": false,
                                "lastTicketingDate": "2026-11-01",
                                "lastTicketingDateTime": "2026-11-01T15:52:40.830072Z",
                                "pricing": {
                                  "supplierCurrency": "USD",
                                  "agencyCurrency": "DZD",
                                  "basePrice": 16385,
                                  "taxesAndFees": 5226.2,
                                  "agencyCommission": 21.62,
                                  "finalPrice": 21632.82,
                                  "b2bPrice": 21611.2,
                                  "additionalServices": []
                                },
                                "itinerary": [
                                  {
                                    "direction": "outbound",
                                    "duration": "PT2H24M",
                                    "stops": 0,
                                    "segments": [
                                      {
                                        "id": "seg_0000BC0a5iUChSV6Qrz0Co",
                                        "departure": {
                                          "airportCode": "ALG",
                                          "airportName": "Houari Boumediene Airport",
                                          "cityName": "Algiers",
                                          "countryCode": "DZ",
                                          "terminal": "2",
                                          "datetime": "2026-12-15T10:50:00",
                                          "timeZone": "Africa/Algiers"
                                        },
                                        "arrival": {
                                          "airportCode": "CDG",
                                          "airportName": "Paris Charles de Gaulle Airport",
                                          "cityName": "Paris",
                                          "countryCode": "FR",
                                          "terminal": "1",
                                          "datetime": "2026-12-15T13:14:00",
                                          "timeZone": "Europe/Paris"
                                        },
                                        "flight": {
                                          "carrierCode": "ZZ",
                                          "carrierName": "Duffel Airways",
                                          "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                                          "flightNumber": "7426",
                                          "aircraftCode": null,
                                          "aircraftName": null,
                                          "operatingCarrier": "ZZ",
                                          "operatingCarrierName": "Duffel Airways"
                                        },
                                        "duration": "PT2H24M",
                                        "cabin": "ECONOMY",
                                        "bookingClass": null,
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic"
                                      }
                                    ],
                                    "conditions": {
                                      "changeBeforeDeparture": {
                                        "allowed": false
                                      },
                                      "refundBeforeDeparture": {
                                        "allowed": false
                                      }
                                    }
                                  }
                                ],
                                "travelerPricing": [
                                  {
                                    "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                                    "travelerType": "ADULT",
                                    "fareOption": "STANDARD",
                                    "price": {
                                      "supplierCurrency": "USD",
                                      "agencyCurrency": "DZD",
                                      "base": 16385,
                                      "total": 21632.82,
                                      "b2bPrice": 21611.2
                                    },
                                    "fareDetailsBySegment": [
                                      {
                                        "segmentId": "seg_0000BC0a5iUChSV6Qrz0Co",
                                        "cabin": "ECONOMY",
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic",
                                        "bookingClass": null,
                                        "includedCheckedBags": {
                                          "quantity": 1
                                        },
                                        "includedCabinBags": {
                                          "quantity": 1
                                        },
                                        "amenities": [
                                          {
                                            "description": "WIFI",
                                            "isChargeable": true,
                                            "amenityType": "WIFI",
                                            "amenityProvider": {
                                              "name": "BrandedFare"
                                            }
                                          },
                                          {
                                            "description": "POWER SUPPLY",
                                            "isChargeable": false,
                                            "amenityType": "ENTERTAINMENT",
                                            "amenityProvider": {
                                              "name": "BrandedFare"
                                            }
                                          }
                                        ]
                                      }
                                    ]
                                  }
                                ],
                                "conditions": {
                                  "refundBeforeDeparture": {
                                    "allowed": false
                                  },
                                  "changeBeforeDeparture": {
                                    "allowed": false
                                  }
                                },
                                "fareRules": {
                                  "exchange": {
                                    "allowed": false
                                  },
                                  "refund": {
                                    "allowed": false
                                  },
                                  "revalidation": {
                                    "allowed": false
                                  },
                                  "supplierCurrency": "USD",
                                  "agencyCurrency": "DZD"
                                },
                                "validatingAirline": [
                                  {
                                    "code": "ZZ",
                                    "name": "Duffel Airways"
                                  }
                                ]
                              }
                            ]
                          },
                          "exchangeRate": 250
                        }
                      ],
                      "summary": {
                        "totalSuppliers": 2,
                        "successfulSuppliers": 2,
                        "failedSuppliers": 0,
                        "totalOffers": 158
                      }
                    }
                  },
                  "multi_city_with_a_failed_supplier": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "39931c32",
                      "searchType": "MULTI_CITY",
                      "flexible": false,
                      "processingTime": 4.407,
                      "suppliers": [
                        {
                          "success": true,
                          "searchId": "fs_dfx_a9115ccf-12d2-4755-a1cf-4dce4ff1c57a",
                          "supportHold": false,
                          "supportVoid": false,
                          "voidDeadline": null,
                          "filtersApplied": {},
                          "warnings": [],
                          "meta": {
                            "supplierId": "duffel_1",
                            "technologyType": "duffel",
                            "supplierName": "Duffel",
                            "processingTime": 4.252,
                            "supplierCurrency": "USD",
                            "agencyCurrency": "DZD"
                          },
                          "results": {
                            "totalOffers": 568,
                            "supplierCurrency": "USD",
                            "agencyCurrency": "DZD",
                            "offers": [
                              {
                                "id": "fx_offer_8da92b71",
                                "type": "flight_offer",
                                "supplier": {
                                  "id": "duffel_1",
                                  "name": "Duffel",
                                  "originalId": "off_034q71ndyhYPPUHIZ2X60m"
                                },
                                "bookableSeats": null,
                                "supportHold": false,
                                "supportVoid": false,
                                "voidDeadline": null,
                                "filtersApplied": {},
                                "warnings": [],
                                "instantTicketingRequired": false,
                                "lastTicketingDate": "2026-11-01",
                                "lastTicketingDateTime": "2026-11-01T16:21:54.360124Z",
                                "pricing": {
                                  "supplierCurrency": "USD",
                                  "agencyCurrency": "DZD",
                                  "basePrice": 29490,
                                  "taxesAndFees": 8885.99,
                                  "agencyCommission": 38.39,
                                  "finalPrice": 38414.38,
                                  "b2bPrice": 38375.99,
                                  "additionalServices": []
                                },
                                "itinerary": [
                                  {
                                    "direction": "outbound",
                                    "duration": "PT2H24M",
                                    "stops": 0,
                                    "segments": [
                                      {
                                        "id": "seg_0000BC0chQbAwlJhSuri7K",
                                        "departure": {
                                          "airportCode": "ALG",
                                          "airportName": "Houari Boumediene Airport",
                                          "cityName": "Algiers",
                                          "countryCode": "DZ",
                                          "terminal": "2",
                                          "datetime": "2026-12-15T10:50:00",
                                          "timeZone": "Africa/Algiers"
                                        },
                                        "arrival": {
                                          "airportCode": "CDG",
                                          "airportName": "Paris Charles de Gaulle Airport",
                                          "cityName": "Paris",
                                          "countryCode": "FR",
                                          "terminal": "1",
                                          "datetime": "2026-12-15T13:14:00",
                                          "timeZone": "Europe/Paris"
                                        },
                                        "flight": {
                                          "carrierCode": "ZZ",
                                          "carrierName": "Duffel Airways",
                                          "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                                          "flightNumber": "7426",
                                          "aircraftCode": null,
                                          "aircraftName": null,
                                          "operatingCarrier": "ZZ",
                                          "operatingCarrierName": "Duffel Airways"
                                        },
                                        "duration": "PT2H24M",
                                        "cabin": "ECONOMY",
                                        "bookingClass": null,
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic"
                                      }
                                    ],
                                    "conditions": {
                                      "changeBeforeDeparture": {
                                        "allowed": true
                                      },
                                      "refundBeforeDeparture": {
                                        "allowed": true
                                      }
                                    }
                                  },
                                  {
                                    "direction": "return",
                                    "duration": "PT1H59M",
                                    "stops": 0,
                                    "segments": [
                                      {
                                        "id": "seg_0000BC0chQbAwlJhSuri7c",
                                        "departure": {
                                          "airportCode": "CDG",
                                          "airportName": "Paris Charles de Gaulle Airport",
                                          "cityName": "Paris",
                                          "countryCode": "FR",
                                          "terminal": "2",
                                          "datetime": "2026-12-18T13:09:00",
                                          "timeZone": "Europe/Paris"
                                        },
                                        "arrival": {
                                          "airportCode": "MAD",
                                          "airportName": "Adolfo Suárez Madrid-Barajas Airport",
                                          "cityName": "Madrid",
                                          "countryCode": "ES",
                                          "terminal": "1",
                                          "datetime": "2026-12-18T15:08:00",
                                          "timeZone": "Europe/Madrid"
                                        },
                                        "flight": {
                                          "carrierCode": "ZZ",
                                          "carrierName": "Duffel Airways",
                                          "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                                          "flightNumber": "7426",
                                          "aircraftCode": null,
                                          "aircraftName": null,
                                          "operatingCarrier": "ZZ",
                                          "operatingCarrierName": "Duffel Airways"
                                        },
                                        "duration": "PT1H59M",
                                        "cabin": "ECONOMY",
                                        "bookingClass": null,
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic"
                                      }
                                    ],
                                    "conditions": {
                                      "changeBeforeDeparture": {
                                        "allowed": true
                                      },
                                      "refundBeforeDeparture": {
                                        "allowed": true
                                      }
                                    }
                                  },
                                  {
                                    "direction": "segment_3",
                                    "duration": "PT1H32M",
                                    "stops": 0,
                                    "segments": [
                                      {
                                        "id": "seg_0000BC0chQbWvRbHU11zfO",
                                        "departure": {
                                          "airportCode": "MAD",
                                          "airportName": "Adolfo Suárez Madrid-Barajas Airport",
                                          "cityName": "Madrid",
                                          "countryCode": "ES",
                                          "terminal": "2",
                                          "datetime": "2026-12-22T16:15:00",
                                          "timeZone": "Europe/Madrid"
                                        },
                                        "arrival": {
                                          "airportCode": "ALG",
                                          "airportName": "Houari Boumediene Airport",
                                          "cityName": "Algiers",
                                          "countryCode": "DZ",
                                          "terminal": "1",
                                          "datetime": "2026-12-22T17:47:00",
                                          "timeZone": "Africa/Algiers"
                                        },
                                        "flight": {
                                          "carrierCode": "ZZ",
                                          "carrierName": "Duffel Airways",
                                          "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                                          "flightNumber": "7426",
                                          "aircraftCode": null,
                                          "aircraftName": null,
                                          "operatingCarrier": "ZZ",
                                          "operatingCarrierName": "Duffel Airways"
                                        },
                                        "duration": "PT1H32M",
                                        "cabin": "ECONOMY",
                                        "bookingClass": null,
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic"
                                      }
                                    ],
                                    "conditions": {
                                      "changeBeforeDeparture": {
                                        "allowed": true
                                      },
                                      "refundBeforeDeparture": {
                                        "allowed": true
                                      }
                                    }
                                  }
                                ],
                                "travelerPricing": [
                                  {
                                    "travelerId": "pas_0000BC0chQMznTySkwEO0q",
                                    "travelerType": "ADULT",
                                    "fareOption": "STANDARD",
                                    "price": {
                                      "supplierCurrency": "USD",
                                      "agencyCurrency": "DZD",
                                      "base": 29490,
                                      "total": 38414.38,
                                      "b2bPrice": 38376
                                    },
                                    "fareDetailsBySegment": [
                                      {
                                        "segmentId": "seg_0000BC0chQbAwlJhSuri7K",
                                        "cabin": "ECONOMY",
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic",
                                        "bookingClass": null,
                                        "includedCheckedBags": {
                                          "quantity": 1
                                        },
                                        "includedCabinBags": {
                                          "quantity": 1
                                        },
                                        "amenities": [
                                          {
                                            "description": "WIFI",
                                            "isChargeable": true,
                                            "amenityType": "WIFI",
                                            "amenityProvider": {
                                              "name": "BrandedFare"
                                            }
                                          }
                                        ]
                                      },
                                      {
                                        "segmentId": "seg_0000BC0chQbAwlJhSuri7c",
                                        "cabin": "ECONOMY",
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic",
                                        "bookingClass": null,
                                        "includedCheckedBags": {
                                          "quantity": 1
                                        },
                                        "includedCabinBags": {
                                          "quantity": 1
                                        },
                                        "amenities": [
                                          {
                                            "description": "WIFI",
                                            "isChargeable": true,
                                            "amenityType": "WIFI",
                                            "amenityProvider": {
                                              "name": "BrandedFare"
                                            }
                                          }
                                        ]
                                      },
                                      {
                                        "segmentId": "seg_0000BC0chQbWvRbHU11zfO",
                                        "cabin": "ECONOMY",
                                        "fareBasis": "Y20LGTN2",
                                        "brandedFare": "BASIC",
                                        "brandedFareLabel": "Basic",
                                        "bookingClass": null,
                                        "includedCheckedBags": {
                                          "quantity": 1
                                        },
                                        "includedCabinBags": {
                                          "quantity": 1
                                        },
                                        "amenities": [
                                          {
                                            "description": "WIFI",
                                            "isChargeable": true,
                                            "amenityType": "WIFI",
                                            "amenityProvider": {
                                              "name": "BrandedFare"
                                            }
                                          }
                                        ]
                                      }
                                    ]
                                  }
                                ],
                                "conditions": {
                                  "refundBeforeDeparture": {
                                    "allowed": true
                                  },
                                  "changeBeforeDeparture": {
                                    "allowed": true
                                  }
                                },
                                "fareRules": {
                                  "exchange": {
                                    "allowed": true
                                  },
                                  "refund": {
                                    "allowed": true
                                  },
                                  "revalidation": {
                                    "allowed": false
                                  },
                                  "supplierCurrency": "USD",
                                  "agencyCurrency": "DZD"
                                },
                                "validatingAirline": [
                                  {
                                    "code": "ZZ",
                                    "name": "Duffel Airways"
                                  }
                                ]
                              }
                            ]
                          },
                          "exchangeRate": 250
                        }
                      ],
                      "summary": {
                        "totalSuppliers": 1,
                        "successfulSuppliers": 1,
                        "failedSuppliers": 1,
                        "totalOffers": 568
                      },
                      "failures": [
                        {
                          "supplierId": "amadeus_aqc_dz_1",
                          "error": "The supplier returned an error.",
                          "cause": "supplier_error"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused **before any supplier was called**, so it cost nothing. `errors[0].code` says what is wrong and `errors[0].field` names the field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "missing_search_params": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "missing_search_params",
                        "message": "Provide either 'url' or structured params (origin, destination, departure_date, cabin_class).",
                        "example_url": "flights/ALG-CDG/2026-12-15/economy/1adults",
                        "example_params": {
                          "origin": "ALG",
                          "destination": "CDG",
                          "departure_date": "2026-12-15",
                          "cabin_class": "economy",
                          "passengers": {
                            "adults": 1
                          }
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_search_params",
                          "type": "validation_error",
                          "title": "Missing search params",
                          "message": "Provide either 'url' or structured params (origin, destination, departure_date, cabin_class).",
                          "field": null,
                          "details": {
                            "example_url": "flights/ALG-CDG/2026-12-15/economy/1adults",
                            "example_params": {
                              "origin": "ALG",
                              "destination": "CDG",
                              "departure_date": "2026-12-15",
                              "cabin_class": "economy",
                              "passengers": {
                                "adults": 1
                              }
                            }
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#missing_search_params"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_airport_code": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_airport_code",
                        "message": "`origin` must be a 3-letter IATA code such as ALG.",
                        "field": "origin",
                        "received": "ALGIERS"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_airport_code",
                          "type": "validation_error",
                          "title": "Invalid airport code",
                          "message": "`origin` must be a 3-letter IATA code such as ALG.",
                          "field": "origin",
                          "details": {
                            "received": "ALGIERS"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_airport_code"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "date_in_past": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "date_in_past",
                        "message": "`departure_date` is in the past; use today or a later date.",
                        "field": "departure_date",
                        "received": "2025-01-10"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "date_in_past",
                          "type": "validation_error",
                          "title": "Date in past",
                          "message": "`departure_date` is in the past; use today or a later date.",
                          "field": "departure_date",
                          "details": {
                            "received": "2025-01-10"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#date_in_past"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_cabin_class": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_cabin_class",
                        "message": "`cabin_class` must be one of: economy, premium_economy, business, first, any.",
                        "field": "cabin_class",
                        "allowed": [
                          "economy",
                          "premium_economy",
                          "business",
                          "first",
                          "any"
                        ],
                        "received": "luxury"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_cabin_class",
                          "type": "validation_error",
                          "title": "Invalid cabin class",
                          "message": "`cabin_class` must be one of: economy, premium_economy, business, first, any.",
                          "field": "cabin_class",
                          "details": {
                            "allowed": [
                              "economy",
                              "premium_economy",
                              "business",
                              "first",
                              "any"
                            ],
                            "received": "luxury"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_cabin_class"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_supplier_selection": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_supplier_selection",
                        "message": "One or more requested suppliers cannot be used for this flight search.",
                        "invalidSuppliers": [
                          "nope_1"
                        ],
                        "disabledSuppliers": [],
                        "unsupportedFlexibleSuppliers": [],
                        "availableSuppliers": [
                          "amadeus_aqc_dz_1",
                          "duffel_1"
                        ]
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_supplier_selection",
                          "type": "validation_error",
                          "title": "Invalid supplier selection",
                          "message": "One or more requested suppliers cannot be used for this flight search.",
                          "field": null,
                          "details": {
                            "invalidSuppliers": [
                              "nope_1"
                            ],
                            "disabledSuppliers": [],
                            "unsupportedFlexibleSuppliers": [],
                            "availableSuppliers": [
                              "amadeus_aqc_dz_1",
                              "duffel_1"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_supplier_selection"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "traveler_rule_violation": {
                    "value": {
                      "environment": "sandbox",
                      "error": "TRAVELER_RULE_VIOLATION",
                      "message": "Total seated passengers cannot exceed 9",
                      "requestId": "2b773b87",
                      "violations": [
                        {
                          "code": "MAX_SEATS_EXCEEDED",
                          "message": "Total seated passengers cannot exceed 9",
                          "max_seats": 9,
                          "seated_passengers": 10
                        }
                      ],
                      "success": false,
                      "errors": [
                        {
                          "code": "traveler_rule_violation",
                          "type": "validation_error",
                          "title": "Traveler rule violation",
                          "message": "Total seated passengers cannot exceed 9",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#traveler_rule_violation"
                        }
                      ],
                      "meta": {
                        "request_id": "2b773b87",
                        "status": 400
                      },
                      "request_id": "2b773b87"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The IP address or the agency is not allowed, or `acd_exempt_company_id` is not linked to your agency or is no longer approved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "acd_exemption_invalid": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "acd_exemption_invalid",
                        "message": "The selected ACD exempt company is not linked to this agency."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "acd_exemption_invalid",
                          "type": "permission_error",
                          "title": "Acd exemption invalid",
                          "message": "The selected ACD exempt company is not linked to this agency.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#acd_exemption_invalid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than 64 KB or holds more than 5,000 values.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "request_too_large": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "request_too_large",
                        "message": "The request body carries too many values."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "request_too_large",
                          "type": "validation_error",
                          "title": "Request too large",
                          "message": "The request body carries too many values.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#request_too_large"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 413
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`flexible_only` on a route that no flexible-capable supplier serves (flexible dates exist on routes that touch Algeria).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "no_supplier_supports_flexible_search": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "no_supplier_supports_flexible_search",
                        "message": "No flight supplier supports flexible-date search for this route. Flexible search is offered on routes that touch Algeria; use get_flights for fixed dates.",
                        "field": "flexible_only"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "no_supplier_supports_flexible_search",
                          "type": "validation_error",
                          "title": "No supplier supports flexible search",
                          "message": "No flight supplier supports flexible-date search for this route. Flexible search is offered on routes that touch Algeria; use get_flights for fixed dates.",
                          "field": "flexible_only",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#no_supplier_supports_flexible_search"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 422
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Two causes. `rate_limit_exceeded`: more requests than the limit of this endpoint group per minute. `concurrency_limit_exceeded` (only with `stream=true`): too many streams are open at the same time for your key. Keep at most 4 streams open per key: a fifth is refused with `Retry-After: 5` once the limit is enforced; until then the streams are only counted. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "concurrency_limit_exceeded": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "concurrency_limit_exceeded",
                        "message": "At most 4 streaming searches can run at the same time for one API key; wait for one to finish (or use the JSON response) and retry.",
                        "retry_after": 5,
                        "errors": [
                          {
                            "code": "concurrency_limit_exceeded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "At most 4 streaming searches can run at the same time for one API key; wait for one to finish (or use the JSON response) and retry.",
                            "title": "Too many concurrent requests",
                            "type": "rate_limit_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_8f3a61c2d9e4",
                          "status": 429,
                          "retry_after": 5
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "concurrency_limit_exceeded",
                          "type": "rate_limit_error",
                          "title": "Concurrency limit exceeded",
                          "message": "At most 4 streaming searches can run at the same time for one API key; wait for one to finish (or use the JSON response) and retry.",
                          "field": null,
                          "details": {
                            "retry_after": 5
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#concurrency_limit_exceeded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 429
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "No supplier answered. `all_suppliers_failed`: `supplierErrors[]` says why for each one, with a stable `cause` (`supplier_busy` or `service_overloaded` for a supplier that was refused for lack of capacity). A well-formed code of an airport that does not exist, a date with no schedule and a supplier outage all end here: wait, then retry with backoff or search another route. `supplier_busy`: **every** supplier refused because FX-Port's capacity towards it is full for a moment; the suppliers were not called, nothing was booked, and `Retry-After` says how many seconds to wait before you send the same request again (when only some suppliers are busy the answer is a `200` and the busy ones are in `failures[]`). `service_overloaded`: FX-Port was too busy preparing other large search answers to prepare this one; nothing was booked and nothing was charged. Retry with backoff, after the seconds of `Retry-After` (when only one supplier's results were affected the answer is a `200` and that supplier is in `failures[]` with the cause `service_overloaded`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "all_suppliers_failed": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "all_suppliers_failed",
                        "message": "No supplier returned results. See supplierErrors for details.",
                        "supplierErrors": [
                          {
                            "supplierId": "duffel_1",
                            "cause": "supplier_timeout",
                            "message": "The supplier did not respond in time. Retry or try a different supplier."
                          }
                        ]
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "all_suppliers_failed",
                          "type": "supplier_error",
                          "title": "All suppliers failed",
                          "message": "No supplier returned results. See supplierErrors for details.",
                          "field": null,
                          "details": {
                            "supplierErrors": [
                              {
                                "supplierId": "duffel_1",
                                "cause": "supplier_timeout",
                                "message": "The supplier did not respond in time. Retry or try a different supplier."
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#all_suppliers_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                        "retry_after": 5,
                        "errors": [
                          {
                            "code": "supplier_busy",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                            "title": "Supplier temporarily at capacity",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_8f3a61c2d9e4",
                          "status": 503,
                          "retry_after": 5
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 5
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                        "retry_after": 5,
                        "errors": [
                          {
                            "code": "service_overloaded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                            "title": "Service temporarily overloaded",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_8f3a61c2d9e4",
                          "status": 503,
                          "retry_after": 5
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 5
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Search flights",
            "path": "/docs/flights/search"
          },
          {
            "label": "Streaming search results",
            "path": "/docs/streaming"
          },
          {
            "label": "Flight passengers & ages",
            "path": "/docs/flights/passengers"
          },
          {
            "label": "Filters",
            "path": "/docs/flights/search/filters"
          },
          {
            "label": "Supplier selection",
            "path": "/docs/flights/search/suppliers"
          },
          {
            "label": "Open-jaw and multi-city",
            "path": "/docs/flights/search/complex-itineraries"
          }
        ],
        "x-streaming": true,
        "x-stream-events": [
          {
            "event": "start",
            "description": "First, at once (`id` 0). The request id, the suppliers that will answer, and `environment` as the first key.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamStart"
            },
            "example": {
              "environment": "sandbox",
              "type": "start",
              "requestId": "d36b6918",
              "searchType": "ONE WAY",
              "flexible": false,
              "timestamp": "2026-11-01T15:22:57.762071",
              "expectedSuppliers": [
                "amadeus_aqc_dz_1",
                "duffel_1"
              ],
              "search_type": "ONE_WAY"
            }
          },
          {
            "event": "supplier_start",
            "description": "A supplier answered with **more than 50 offers**: the result set without its offers (`results.chunked`, `chunkSize`, `totalChunks`). Its offers follow in `offers_chunk` events.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamSupplierResult"
            },
            "example": {
              "type": "supplier_start",
              "supplierId": "duffel_1",
              "success": true,
              "data": {
                "success": true,
                "requestId": "d36b6918",
                "searchId": "fs_dfx_59ce98b7-ce5a-4911-abb8-b43d4786e6c9",
                "searchType": "ONE_WAY",
                "processingTime": 1.381,
                "supplierCurrency": "USD",
                "agencyCurrency": "DZD",
                "results": {
                  "totalOffers": 92,
                  "supplierCurrency": "USD",
                  "agencyCurrency": "DZD",
                  "offers": [],
                  "chunked": true,
                  "chunkSize": 50,
                  "totalChunks": 2
                },
                "meta": {
                  "filtersApplied": {},
                  "warnings": [],
                  "supplierId": "duffel_1",
                  "technologyType": "duffel",
                  "supportHold": false,
                  "supportVoid": false,
                  "voidDeadline": null
                },
                "exchangeRate": 250
              },
              "processingTime": 1.381,
              "timestamp": "2026-11-01T15:22:59.161692"
            }
          },
          {
            "event": "offers_chunk",
            "description": "Up to 50 offers of the result set opened by the last `supplier_start` of the same supplier.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamOffersChunk"
            },
            "example": {
              "type": "offers_chunk",
              "supplierId": "duffel_1",
              "chunkNumber": 1,
              "totalChunks": 2,
              "offersInChunk": 50,
              "offers": [
                {
                  "id": "fx_offer_9ab6d940",
                  "type": "flight_offer",
                  "supplier": {
                    "id": "duffel_1",
                    "name": "Duffel",
                    "originalId": "off_034q6JKdiQDZ8HIDMFus64"
                  },
                  "bookableSeats": null,
                  "supportHold": false,
                  "supportVoid": false,
                  "voidDeadline": null,
                  "filtersApplied": {},
                  "warnings": [],
                  "instantTicketingRequired": false,
                  "lastTicketingDate": "2026-11-01",
                  "lastTicketingDateTime": "2026-11-01T15:52:40.830072Z",
                  "pricing": {
                    "supplierCurrency": "USD",
                    "agencyCurrency": "DZD",
                    "basePrice": 16385,
                    "taxesAndFees": 5226.2,
                    "agencyCommission": 21.62,
                    "finalPrice": 21632.82,
                    "b2bPrice": 21611.2,
                    "additionalServices": []
                  },
                  "itinerary": [
                    {
                      "direction": "outbound",
                      "duration": "PT2H24M",
                      "stops": 0,
                      "segments": [
                        {
                          "id": "seg_0000BC0a5iUChSV6Qrz0Co",
                          "departure": {
                            "airportCode": "ALG",
                            "airportName": "Houari Boumediene Airport",
                            "cityName": "Algiers",
                            "countryCode": "DZ",
                            "terminal": "2",
                            "datetime": "2026-12-15T10:50:00",
                            "timeZone": "Africa/Algiers"
                          },
                          "arrival": {
                            "airportCode": "CDG",
                            "airportName": "Paris Charles de Gaulle Airport",
                            "cityName": "Paris",
                            "countryCode": "FR",
                            "terminal": "1",
                            "datetime": "2026-12-15T13:14:00",
                            "timeZone": "Europe/Paris"
                          },
                          "flight": {
                            "carrierCode": "ZZ",
                            "carrierName": "Duffel Airways",
                            "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                            "flightNumber": "7426",
                            "aircraftCode": null,
                            "aircraftName": null,
                            "operatingCarrier": "ZZ",
                            "operatingCarrierName": "Duffel Airways"
                          },
                          "duration": "PT2H24M",
                          "cabin": "ECONOMY",
                          "bookingClass": null,
                          "fareBasis": "Y20LGTN2",
                          "brandedFare": "BASIC",
                          "brandedFareLabel": "Basic"
                        }
                      ],
                      "conditions": {
                        "changeBeforeDeparture": {
                          "allowed": false
                        },
                        "refundBeforeDeparture": {
                          "allowed": false
                        }
                      }
                    }
                  ],
                  "travelerPricing": [
                    {
                      "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                      "travelerType": "ADULT",
                      "fareOption": "STANDARD",
                      "price": {
                        "supplierCurrency": "USD",
                        "agencyCurrency": "DZD",
                        "base": 16385,
                        "total": 21632.82,
                        "b2bPrice": 21611.2
                      },
                      "fareDetailsBySegment": [
                        {
                          "segmentId": "seg_0000BC0a5iUChSV6Qrz0Co",
                          "cabin": "ECONOMY",
                          "fareBasis": "Y20LGTN2",
                          "brandedFare": "BASIC",
                          "brandedFareLabel": "Basic",
                          "bookingClass": null,
                          "includedCheckedBags": {
                            "quantity": 1
                          },
                          "includedCabinBags": {
                            "quantity": 1
                          },
                          "amenities": [
                            {
                              "description": "WIFI",
                              "isChargeable": true,
                              "amenityType": "WIFI",
                              "amenityProvider": {
                                "name": "BrandedFare"
                              }
                            },
                            {
                              "description": "POWER SUPPLY",
                              "isChargeable": false,
                              "amenityType": "ENTERTAINMENT",
                              "amenityProvider": {
                                "name": "BrandedFare"
                              }
                            }
                          ]
                        }
                      ]
                    }
                  ],
                  "conditions": {
                    "refundBeforeDeparture": {
                      "allowed": false
                    },
                    "changeBeforeDeparture": {
                      "allowed": false
                    }
                  },
                  "fareRules": {
                    "exchange": {
                      "allowed": false
                    },
                    "refund": {
                      "allowed": false
                    },
                    "revalidation": {
                      "allowed": false
                    },
                    "supplierCurrency": "USD",
                    "agencyCurrency": "DZD"
                  },
                  "validatingAirline": [
                    {
                      "code": "ZZ",
                      "name": "Duffel Airways"
                    }
                  ]
                }
              ],
              "timestamp": "2026-11-01T15:22:59.161771"
            }
          },
          {
            "event": "flex_prices",
            "description": "The date matrix of a chunked result set, when `flexible: true`. Not seen in the sandbox: treat it as optional.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamFlexPrices"
            }
          },
          {
            "event": "supplier_complete",
            "description": "After the last chunk of a result set. `complete: false` means that another batch of this supplier follows.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamSupplierComplete"
            },
            "example": {
              "type": "supplier_complete",
              "supplierId": "amadeus_aqc_dz_1",
              "totalOffers": 68,
              "hasFlexPrices": false,
              "flexPricesCount": 0,
              "complete": false,
              "timestamp": "2026-11-01T15:22:59.819079"
            }
          },
          {
            "event": "supplier_result",
            "description": "A supplier, or a later batch of it, answered with **50 offers or fewer**: the whole result set in `data.results.offers`. `complete: false` means that another batch follows.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamSupplierResult"
            },
            "example": {
              "type": "supplier_result",
              "supplierId": "amadeus_aqc_dz_1",
              "success": true,
              "data": {
                "success": true,
                "requestId": "d36b6918",
                "searchId": "fs_35418d61-6092-44e4-81b7-a6fe985feb79",
                "searchType": "ONE_WAY",
                "processingTime": 2.516,
                "supplierCurrency": "DZD",
                "agencyCurrency": "DZD",
                "results": {
                  "totalOffers": 2,
                  "supplierCurrency": "DZD",
                  "agencyCurrency": "DZD",
                  "offers": [
                    {
                      "id": "fx_offer_b1c2a850",
                      "type": "flight_offer",
                      "supplier": {
                        "id": "amadeus_aqc_dz_1",
                        "name": "Amadeus Algeria",
                        "originalId": "6"
                      },
                      "bookableSeats": 9,
                      "supportHold": true,
                      "supportVoid": true,
                      "voidDeadline": "2026-11-01T23:00:00Z",
                      "filtersApplied": {},
                      "warnings": [],
                      "instantTicketingRequired": false,
                      "pricing": {
                        "supplierCurrency": "DZD",
                        "agencyCurrency": "DZD",
                        "basePrice": 18520,
                        "taxesAndFees": 11306.17,
                        "agencyCommission": 29.83,
                        "finalPrice": 29856,
                        "b2bPrice": 29826.17,
                        "acd": {
                          "applied": false
                        },
                        "additionalServices": []
                      },
                      "itinerary": [
                        {
                          "direction": "outbound",
                          "duration": "PT2H30M",
                          "stops": 0,
                          "segments": [
                            {
                              "id": "27",
                              "departure": {
                                "airportCode": "ALG",
                                "airportName": "Houari Boumediene Airport",
                                "cityName": "Algiers",
                                "countryCode": "DZ",
                                "terminal": "4",
                                "datetime": "2026-12-15T07:15:00",
                                "timeZone": "Africa/Algiers"
                              },
                              "arrival": {
                                "airportCode": "CDG",
                                "airportName": "Paris Charles de Gaulle Airport",
                                "cityName": "Paris",
                                "countryCode": "FR",
                                "terminal": "2D",
                                "datetime": "2026-12-15T09:45:00",
                                "timeZone": "Europe/Paris"
                              },
                              "flight": {
                                "carrierCode": "AH",
                                "carrierName": "AIR ALGERIE",
                                "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                                "flightNumber": "1002",
                                "aircraftCode": "7M8",
                                "aircraftName": "BOEING 737 MAX 8",
                                "operatingCarrier": "AH",
                                "operatingCarrierName": "AIR ALGERIE"
                              },
                              "duration": "PT2H30M",
                              "cabin": "ECONOMY",
                              "bookingClass": "Q",
                              "fareBasis": "QDZOO",
                              "brandedFare": "STANDARD",
                              "brandedFareLabel": ""
                            }
                          ],
                          "conditions": {
                            "refundBeforeDeparture": {
                              "allowed": false
                            },
                            "changeBeforeDeparture": {
                              "allowed": true
                            }
                          }
                        }
                      ],
                      "travelerPricing": [
                        {
                          "travelerId": "1",
                          "travelerType": "ADULT",
                          "fareOption": "STANDARD",
                          "price": {
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD",
                            "base": 18520,
                            "total": 29856,
                            "b2bPrice": 29826.17
                          },
                          "fareDetailsBySegment": [
                            {
                              "segmentId": "27",
                              "cabin": "ECONOMY",
                              "fareBasis": "QDZOO",
                              "brandedFare": "STANDARD",
                              "brandedFareLabel": "",
                              "bookingClass": "Q",
                              "includedCheckedBags": {
                                "quantity": 1
                              },
                              "includedCabinBags": {
                                "quantity": 1
                              },
                              "amenities": []
                            }
                          ]
                        }
                      ],
                      "conditions": {
                        "refundBeforeDeparture": {
                          "allowed": false
                        },
                        "changeBeforeDeparture": {
                          "allowed": true
                        }
                      },
                      "fareRules": {
                        "exchange": {
                          "allowed": true
                        },
                        "refund": {
                          "allowed": false
                        },
                        "revalidation": {
                          "allowed": false
                        },
                        "supplierCurrency": "DZD",
                        "agencyCurrency": "DZD"
                      },
                      "validatingAirline": [
                        {
                          "code": "AH",
                          "name": "AIR ALGERIE"
                        }
                      ],
                      "additionalServices": []
                    }
                  ]
                },
                "meta": {
                  "filtersApplied": {},
                  "warnings": [],
                  "supplierId": "amadeus_aqc_dz_1",
                  "technologyType": "amadeus_aqc",
                  "supportHold": true,
                  "supportVoid": true,
                  "voidDeadline": "2026-11-01T23:00:00Z"
                }
              },
              "complete": true,
              "processingTime": 2.516,
              "timestamp": "2026-11-01T15:23:00.284737"
            }
          },
          {
            "event": "error",
            "description": "A supplier failed. The stream goes on without it. `cause` is a stable code.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamError"
            },
            "example": {
              "type": "error",
              "supplierId": "duffel_1",
              "success": false,
              "error": "The supplier rejected the search request.",
              "cause": "supplier_error",
              "processingTime": 0,
              "timestamp": "2026-11-01T15:23:32.838533"
            }
          },
          {
            "event": "summary",
            "description": "After every supplier has answered or failed. `truncated: true` when the stream reached its 300 s limit.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamSummary"
            },
            "example": {
              "type": "summary",
              "requestId": "d36b6918",
              "success": true,
              "processingTime": 2.581,
              "summary": {
                "totalSuppliers": 2,
                "successfulSuppliers": 2,
                "failedSuppliers": 0,
                "totalOffers": 162
              },
              "successfulSuppliers": [
                {
                  "supplierId": "duffel_1",
                  "offersCount": 92,
                  "processingTime": 1.381
                },
                {
                  "supplierId": "amadeus_aqc_dz_1",
                  "offersCount": 70,
                  "processingTime": 2.516
                }
              ],
              "failures": null,
              "timestamp": "2026-11-01T15:23:00.286463"
            }
          },
          {
            "event": "done",
            "description": "Last, always (also after a crash). The stream is over only here.",
            "schema": {
              "$ref": "#/components/schemas/FlightStreamDone"
            },
            "example": {
              "type": "done",
              "requestId": "d36b6918",
              "timestamp": "2026-11-01T15:23:00.286605"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/flexible_search": {
      "post": {
        "operationId": "flexibleSearch",
        "tags": [
          "Flights"
        ],
        "summary": "Search flexible dates",
        "description": "Get a price calendar instead of offers: the cheapest fare of each day from three days before to three days after your travel dates.\n\nThe body is the one of `get_flights`; `flexible` and `flexible_only` are set for you. A one-way search returns 7 dates, a round trip or an open-jaw up to 49 date pairs (7 by 7). Each supplier entry has `flexPrices.matrix[]` and no `results`; `summary.totalOffers` is `0` and `flexibleOnly` is `true`. An entry has no offer id: to book a date, search for that exact date, then price and book as usual. The price of the matrix is an indication.\n\nOnly suppliers with the flexible-dates capability are called: today Amadeus Algeria (`amadeus_aqc_dz_1`, routes that touch Algeria). Duffel is skipped, and `supplier_id: \"duffel_1\"` is a `400 invalid_supplier_selection`. A route nobody serves is `422 no_supplier_supports_flexible_search`. To get the offers and the matrix together, send `flexible: true` to `get_flights`. Identical flexible searches that arrive at the same moment from the same agency, environment and currency are merged into one supplier fan-out; nothing is kept afterwards, and every caller still gets its own `requestId` and headers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightSearchRequest"
              },
              "examples": {
                "one_way": {
                  "value": {
                    "origin": "ALG",
                    "destination": "CDG",
                    "departure_date": "2026-12-15",
                    "cabin_class": "economy",
                    "passengers": {
                      "adults": 1
                    }
                  }
                },
                "round_trip": {
                  "value": {
                    "origin": "ALG",
                    "destination": "CDG",
                    "departure_date": "2026-12-15",
                    "return_date": "2026-12-22",
                    "cabin_class": "economy",
                    "passengers": {
                      "adults": 1
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The date matrix of each flexible-capable supplier. Check that `flexPrices` is present before you read it; entries with `status: no_flights_available` are expected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightSearchResponse"
                },
                "examples": {
                  "one_way_7_dates": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "9c844468",
                      "searchType": "ONE_WAY",
                      "flexible": true,
                      "processingTime": 4.092,
                      "suppliers": [
                        {
                          "success": true,
                          "searchId": "fs_4f72482f-0d24-4aba-882d-d4eb49f6d034",
                          "supportHold": true,
                          "supportVoid": true,
                          "voidDeadline": "2026-11-01T23:00:00Z",
                          "filtersApplied": {
                            "flexible": true,
                            "flexibleOnly": true
                          },
                          "warnings": [],
                          "meta": {
                            "supplierId": "amadeus_aqc_dz_1",
                            "technologyType": "amadeus_aqc",
                            "supplierName": "Amadeus Algeria",
                            "processingTime": 4.029,
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD"
                          },
                          "flexPrices": {
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD",
                            "matrix": [
                              {
                                "departure": "2026-12-12",
                                "status": "available",
                                "finalPrice": 26925.47,
                                "basePrice": 18360,
                                "taxesAndFees": 8538.57,
                                "agencyCommission": 26.9,
                                "flight": {
                                  "carrierCode": "IB",
                                  "flightNumber": "IB1378",
                                  "operatingCarrier": "IB",
                                  "validatingAirline": "IB",
                                  "duration": "PT9H45M",
                                  "stops": 1,
                                  "departureTime": "14:05",
                                  "arrivalTime": "23:50",
                                  "departureAirport": "ALG",
                                  "arrivalAirport": "CDG",
                                  "cabinClass": "ECONOMY",
                                  "brandedFare": null,
                                  "brandedFareLabel": null,
                                  "includedCheckedBags": {
                                    "quantity": 1
                                  }
                                },
                                "b2bPrice": 26898.57,
                                "agencyCurrency": "DZD"
                              },
                              {
                                "departure": "2026-12-13",
                                "status": "available",
                                "finalPrice": 29638.41,
                                "basePrice": 11810,
                                "taxesAndFees": 17798.8,
                                "agencyCommission": 29.61,
                                "flight": {
                                  "carrierCode": "AZ",
                                  "flightNumber": "AZ801",
                                  "operatingCarrier": "AZ",
                                  "validatingAirline": "AZ",
                                  "duration": "PT5H10M",
                                  "stops": 1,
                                  "departureTime": "12:15",
                                  "arrivalTime": "17:25",
                                  "departureAirport": "ALG",
                                  "arrivalAirport": "CDG",
                                  "cabinClass": "ECONOMY",
                                  "brandedFare": null,
                                  "brandedFareLabel": null,
                                  "includedCheckedBags": {
                                    "quantity": 1
                                  }
                                },
                                "b2bPrice": 29608.8,
                                "agencyCurrency": "DZD"
                              }
                            ]
                          }
                        }
                      ],
                      "flexibleOnly": true,
                      "summary": {
                        "totalSuppliers": 1,
                        "successfulSuppliers": 1,
                        "failedSuppliers": 0,
                        "totalOffers": 0
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused **before any supplier was called**, so it cost nothing. `errors[0].code` says what is wrong and `errors[0].field` names the field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "missing_search_params": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "missing_search_params",
                        "message": "Provide either 'url' or structured params (origin, destination, departure_date, cabin_class).",
                        "example_url": "flights/ALG-CDG/2026-12-15/economy/1adults",
                        "example_params": {
                          "origin": "ALG",
                          "destination": "CDG",
                          "departure_date": "2026-12-15",
                          "cabin_class": "economy",
                          "passengers": {
                            "adults": 1
                          }
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_search_params",
                          "type": "validation_error",
                          "title": "Missing search params",
                          "message": "Provide either 'url' or structured params (origin, destination, departure_date, cabin_class).",
                          "field": null,
                          "details": {
                            "example_url": "flights/ALG-CDG/2026-12-15/economy/1adults",
                            "example_params": {
                              "origin": "ALG",
                              "destination": "CDG",
                              "departure_date": "2026-12-15",
                              "cabin_class": "economy",
                              "passengers": {
                                "adults": 1
                              }
                            }
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#missing_search_params"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_airport_code": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_airport_code",
                        "message": "`origin` must be a 3-letter IATA code such as ALG.",
                        "field": "origin",
                        "received": "ALGIERS"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_airport_code",
                          "type": "validation_error",
                          "title": "Invalid airport code",
                          "message": "`origin` must be a 3-letter IATA code such as ALG.",
                          "field": "origin",
                          "details": {
                            "received": "ALGIERS"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_airport_code"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "date_in_past": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "date_in_past",
                        "message": "`departure_date` is in the past; use today or a later date.",
                        "field": "departure_date",
                        "received": "2025-01-10"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "date_in_past",
                          "type": "validation_error",
                          "title": "Date in past",
                          "message": "`departure_date` is in the past; use today or a later date.",
                          "field": "departure_date",
                          "details": {
                            "received": "2025-01-10"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#date_in_past"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_cabin_class": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_cabin_class",
                        "message": "`cabin_class` must be one of: economy, premium_economy, business, first, any.",
                        "field": "cabin_class",
                        "allowed": [
                          "economy",
                          "premium_economy",
                          "business",
                          "first",
                          "any"
                        ],
                        "received": "luxury"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_cabin_class",
                          "type": "validation_error",
                          "title": "Invalid cabin class",
                          "message": "`cabin_class` must be one of: economy, premium_economy, business, first, any.",
                          "field": "cabin_class",
                          "details": {
                            "allowed": [
                              "economy",
                              "premium_economy",
                              "business",
                              "first",
                              "any"
                            ],
                            "received": "luxury"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_cabin_class"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_supplier_selection": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_supplier_selection",
                        "message": "One or more requested suppliers cannot be used for this flight search.",
                        "invalidSuppliers": [
                          "nope_1"
                        ],
                        "disabledSuppliers": [],
                        "unsupportedFlexibleSuppliers": [],
                        "availableSuppliers": [
                          "amadeus_aqc_dz_1",
                          "duffel_1"
                        ]
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_supplier_selection",
                          "type": "validation_error",
                          "title": "Invalid supplier selection",
                          "message": "One or more requested suppliers cannot be used for this flight search.",
                          "field": null,
                          "details": {
                            "invalidSuppliers": [
                              "nope_1"
                            ],
                            "disabledSuppliers": [],
                            "unsupportedFlexibleSuppliers": [],
                            "availableSuppliers": [
                              "amadeus_aqc_dz_1",
                              "duffel_1"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_supplier_selection"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "traveler_rule_violation": {
                    "value": {
                      "environment": "sandbox",
                      "error": "TRAVELER_RULE_VIOLATION",
                      "message": "Total seated passengers cannot exceed 9",
                      "requestId": "2b773b87",
                      "violations": [
                        {
                          "code": "MAX_SEATS_EXCEEDED",
                          "message": "Total seated passengers cannot exceed 9",
                          "max_seats": 9,
                          "seated_passengers": 10
                        }
                      ],
                      "success": false,
                      "errors": [
                        {
                          "code": "traveler_rule_violation",
                          "type": "validation_error",
                          "title": "Traveler rule violation",
                          "message": "Total seated passengers cannot exceed 9",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#traveler_rule_violation"
                        }
                      ],
                      "meta": {
                        "request_id": "2b773b87",
                        "status": 400
                      },
                      "request_id": "2b773b87"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The IP address or the agency is not allowed, or `acd_exempt_company_id` is not linked to your agency or is no longer approved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "acd_exemption_invalid": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "acd_exemption_invalid",
                        "message": "The selected ACD exempt company is not linked to this agency."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "acd_exemption_invalid",
                          "type": "permission_error",
                          "title": "Acd exemption invalid",
                          "message": "The selected ACD exempt company is not linked to this agency.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#acd_exemption_invalid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "The body is larger than 64 KB or holds more than 5,000 values.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "request_too_large": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "request_too_large",
                        "message": "The request body carries too many values."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "request_too_large",
                          "type": "validation_error",
                          "title": "Request too large",
                          "message": "The request body carries too many values.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#request_too_large"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 413
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`flexible_only` on a route that no flexible-capable supplier serves (flexible dates exist on routes that touch Algeria).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "no_supplier_supports_flexible_search": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "no_supplier_supports_flexible_search",
                        "message": "No flight supplier supports flexible-date search for this route. Flexible search is offered on routes that touch Algeria; use get_flights for fixed dates.",
                        "field": "flexible_only"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "no_supplier_supports_flexible_search",
                          "type": "validation_error",
                          "title": "No supplier supports flexible search",
                          "message": "No flight supplier supports flexible-date search for this route. Flexible search is offered on routes that touch Algeria; use get_flights for fixed dates.",
                          "field": "flexible_only",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#no_supplier_supports_flexible_search"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 422
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "No supplier answered. `all_suppliers_failed`: `supplierErrors[]` says why for each one, with a stable `cause` (`supplier_busy` or `service_overloaded` for a supplier that was refused for lack of capacity). A well-formed code of an airport that does not exist, a date with no schedule and a supplier outage all end here: wait, then retry with backoff or search another route. `supplier_busy`: **every** supplier refused because FX-Port's capacity towards it is full for a moment; the suppliers were not called, nothing was booked, and `Retry-After` says how many seconds to wait before you send the same request again (when only some suppliers are busy the answer is a `200` and the busy ones are in `failures[]`). `service_overloaded`: FX-Port was too busy preparing other large search answers to prepare this one; nothing was booked and nothing was charged. Retry with backoff, after the seconds of `Retry-After` (when only one supplier's results were affected the answer is a `200` and that supplier is in `failures[]` with the cause `service_overloaded`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "all_suppliers_failed": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "all_suppliers_failed",
                        "message": "No supplier returned results. See supplierErrors for details.",
                        "supplierErrors": [
                          {
                            "supplierId": "duffel_1",
                            "cause": "supplier_timeout",
                            "message": "The supplier did not respond in time. Retry or try a different supplier."
                          }
                        ]
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "all_suppliers_failed",
                          "type": "supplier_error",
                          "title": "All suppliers failed",
                          "message": "No supplier returned results. See supplierErrors for details.",
                          "field": null,
                          "details": {
                            "supplierErrors": [
                              {
                                "supplierId": "duffel_1",
                                "cause": "supplier_timeout",
                                "message": "The supplier did not respond in time. Retry or try a different supplier."
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#all_suppliers_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                        "retry_after": 5,
                        "errors": [
                          {
                            "code": "supplier_busy",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                            "title": "Supplier temporarily at capacity",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_8f3a61c2d9e4",
                          "status": 503,
                          "retry_after": 5
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 5
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                        "retry_after": 5,
                        "errors": [
                          {
                            "code": "service_overloaded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                            "title": "Service temporarily overloaded",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_8f3a61c2d9e4",
                          "status": 503,
                          "retry_after": 5
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 5
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Flexible-date search",
            "path": "/docs/flights/flexible-search"
          },
          {
            "label": "Search flights",
            "path": "/docs/flights/search"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/price_flight": {
      "post": {
        "operationId": "priceFlight",
        "tags": [
          "Flights"
        ],
        "summary": "Price a flight offer",
        "description": "Confirm the current price and conditions of one offer of a search with its supplier, just before you book it.\n\nThis call is required before `book_flight`: an offer that was not priced is refused with `404 offer_not_priced`. Send the `searchId` of the supplier entry the offer is in (`suppliers[n].searchId`, never the top-level `requestId`) and the `id` of the offer. The answer is the current version of the offer: its price can differ from the search, `supportHold` is final, and an Amadeus offer lists the extra bags that can be bought. Pricing reserves nothing.\n\nA `searchId` is valid for 24 hours, but a supplier withdraws offers sooner (a Duffel offer lives 15 to 60 minutes): on `410 offer_expired`, search again. A priced offer can be booked for 12 hours.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightPriceRequest"
              },
              "examples": {
                "duffel": {
                  "value": {
                    "search_id": "fs_dfx_a3aeaa36-74ad-424b-b88d-e6bdc9a4ba94",
                    "offer_id": "fx_offer_89d6592c"
                  }
                },
                "amadeus": {
                  "value": {
                    "search_id": "fs_e1853430-16e2-4bcb-8f11-4f7bfbc33602",
                    "offer_id": "fx_offer_5d3add32"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The priced offer in `results.offers[0]`: the price to book, `supportHold` and, for Amadeus, the bags that can be added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightPriceResponse"
                },
                "examples": {
                  "duffel": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "76f8fc58",
                      "offerId": "fx_offer_89d6592c",
                      "offerType": "priced",
                      "processingTime": 0.177,
                      "supplierCurrency": "USD",
                      "agencyCurrency": "DZD",
                      "results": {
                        "totalOffers": 1,
                        "supplierCurrency": "USD",
                        "agencyCurrency": "DZD",
                        "offers": [
                          {
                            "id": "fx_offer_89d6592c",
                            "type": "flight_offer",
                            "supplier": {
                              "id": "duffel_1",
                              "name": "Duffel",
                              "originalId": "off_034q6JKduVe2mSW1fH8lge"
                            },
                            "source": "duffel",
                            "bookableSeats": null,
                            "supportHold": false,
                            "supportVoid": false,
                            "voidDeadline": null,
                            "filtersApplied": {},
                            "warnings": [],
                            "instantTicketingRequired": false,
                            "pricing": {
                              "supplierCurrency": "USD",
                              "agencyCurrency": "DZD",
                              "basePrice": 16975,
                              "taxesAndFees": 5366.8,
                              "agencyCommission": 22.35,
                              "finalPrice": 22364.15,
                              "b2bPrice": 22341.8,
                              "additionalServices": []
                            },
                            "itinerary": [
                              {
                                "direction": "outbound",
                                "duration": "PT2H24M",
                                "stops": 0,
                                "segments": [
                                  {
                                    "id": "seg_0000BC0a5iVGdVLqUATqqr",
                                    "departure": {
                                      "airportCode": "ALG",
                                      "airportName": "Houari Boumediene Airport",
                                      "cityName": "Algiers",
                                      "countryCode": "DZ",
                                      "terminal": "2",
                                      "datetime": "2026-12-15T10:50:00",
                                      "timeZone": "Africa/Algiers"
                                    },
                                    "arrival": {
                                      "airportCode": "CDG",
                                      "airportName": "Paris Charles de Gaulle Airport",
                                      "cityName": "Paris",
                                      "countryCode": "FR",
                                      "terminal": "1",
                                      "datetime": "2026-12-15T13:14:00",
                                      "timeZone": "Europe/Paris"
                                    },
                                    "flight": {
                                      "carrierCode": "BA",
                                      "carrierName": "British Airways",
                                      "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/BA.svg",
                                      "flightNumber": "0107",
                                      "aircraftCode": null,
                                      "aircraftName": null,
                                      "operatingCarrier": "BA",
                                      "operatingCarrierName": "British Airways"
                                    },
                                    "duration": "PT2H24M",
                                    "cabin": "ECONOMY",
                                    "bookingClass": null,
                                    "fareBasis": "Y20LGTN2",
                                    "brandedFare": "Basic",
                                    "brandedFareLabel": "Basic"
                                  }
                                ],
                                "conditions": {
                                  "changeBeforeDeparture": {
                                    "allowed": true
                                  },
                                  "refundBeforeDeparture": {
                                    "allowed": true
                                  }
                                }
                              }
                            ],
                            "travelerPricing": [
                              {
                                "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                                "travelerType": "ADULT",
                                "fareOption": "STANDARD",
                                "price": {
                                  "supplierCurrency": "USD",
                                  "agencyCurrency": "DZD",
                                  "base": 16975,
                                  "total": 22364.15,
                                  "b2bPrice": 22341.8
                                },
                                "fareDetailsBySegment": [
                                  {
                                    "segmentId": "seg_0000BC0a5iVGdVLqUATqqr",
                                    "cabin": "ECONOMY",
                                    "fareBasis": "Y20LGTN2",
                                    "brandedFare": "Basic",
                                    "brandedFareLabel": "Basic",
                                    "bookingClass": "Economy",
                                    "includedCheckedBags": {
                                      "quantity": 1
                                    },
                                    "includedCabinBags": {
                                      "quantity": 1
                                    },
                                    "amenities": []
                                  }
                                ]
                              }
                            ],
                            "conditions": {
                              "refundBeforeDeparture": {
                                "allowed": true
                              },
                              "changeBeforeDeparture": {
                                "allowed": true
                              }
                            },
                            "fareRules": {
                              "refund": {
                                "allowed": true
                              },
                              "exchange": {
                                "allowed": true
                              },
                              "supplierCurrency": "USD",
                              "agencyCurrency": "DZD"
                            },
                            "validatingAirline": [
                              {
                                "code": "BA",
                                "name": "British Airways"
                              }
                            ],
                            "additionalServices": {
                              "baggage": [
                                {
                                  "id": "ase_0000BC0aGG1cCZZ8LCieTb",
                                  "type": "checked",
                                  "quantity": 1,
                                  "maxQuantity": 1,
                                  "maxWeightKg": 23,
                                  "maxWeightUnit": "kg",
                                  "priceAmount": 20,
                                  "convertedPriceAmount": 5000,
                                  "priceCurrency": "USD",
                                  "applicablePassengers": [
                                    "pas_0000BC0a5iH5UE0bmBqWkl"
                                  ],
                                  "applicableSegments": [
                                    "seg_0000BC0a5iVGdVLqUATqqr"
                                  ],
                                  "bookableByItinerary": false,
                                  "metadata": {
                                    "name": "CHECKED",
                                    "description": "Additional checked"
                                  }
                                }
                              ]
                            }
                          }
                        ]
                      },
                      "exchangeRate": 250
                    }
                  },
                  "amadeus": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "6d05c5ff",
                      "offerId": "fx_offer_5d3add32",
                      "offerType": "priced",
                      "processingTime": 1.441,
                      "supplierCurrency": "DZD",
                      "agencyCurrency": "DZD",
                      "results": {
                        "totalOffers": 1,
                        "supplierCurrency": "DZD",
                        "agencyCurrency": "DZD",
                        "offers": [
                          {
                            "id": "fx_offer_5d3add32",
                            "type": "flight_offer",
                            "supplier": {
                              "id": "amadeus_aqc_dz_1",
                              "name": "Amadeus Algeria",
                              "originalId": "5"
                            },
                            "source": "GDS",
                            "bookableSeats": 9,
                            "supportHold": true,
                            "supportVoid": true,
                            "voidDeadline": "2026-11-01T23:00:00Z",
                            "filtersApplied": {},
                            "warnings": [],
                            "instantTicketingRequired": false,
                            "pricing": {
                              "supplierCurrency": "DZD",
                              "agencyCurrency": "DZD",
                              "basePrice": 19470,
                              "taxesAndFees": 10376.96,
                              "agencyCommission": 29.85,
                              "finalPrice": 29876.81,
                              "b2bPrice": 29846.96,
                              "additionalServices": [],
                              "acd": {
                                "applied": false
                              }
                            },
                            "itinerary": [
                              {
                                "direction": "outbound",
                                "duration": "PT2H35M",
                                "stops": 0,
                                "segments": [
                                  {
                                    "id": "43",
                                    "departure": {
                                      "airportCode": "ALG",
                                      "airportName": "Houari Boumediene Airport",
                                      "cityName": "Algiers",
                                      "countryCode": "DZ",
                                      "terminal": "4",
                                      "datetime": "2026-12-15T18:40:00",
                                      "timeZone": "Africa/Algiers"
                                    },
                                    "arrival": {
                                      "airportCode": "CDG",
                                      "airportName": "Paris Charles de Gaulle Airport",
                                      "cityName": "Paris",
                                      "countryCode": "FR",
                                      "terminal": "2E",
                                      "datetime": "2026-12-15T21:15:00",
                                      "timeZone": "Europe/Paris"
                                    },
                                    "flight": {
                                      "carrierCode": "AF",
                                      "carrierName": "Air France",
                                      "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AF.svg",
                                      "flightNumber": "1655",
                                      "aircraftCode": "223",
                                      "aircraftName": "223",
                                      "operatingCarrier": "AF",
                                      "operatingCarrierName": "Air France"
                                    },
                                    "duration": "PT2H35M",
                                    "cabin": "ECONOMY",
                                    "bookingClass": "X",
                                    "fareBasis": "XYS0BCSA",
                                    "brandedFare": "STANDARD",
                                    "brandedFareLabel": "STANDARD"
                                  }
                                ],
                                "conditions": {
                                  "changeBeforeDeparture": {
                                    "allowed": true
                                  },
                                  "refundBeforeDeparture": {
                                    "allowed": false
                                  }
                                }
                              }
                            ],
                            "travelerPricing": [
                              {
                                "travelerId": "1",
                                "travelerType": "ADULT",
                                "fareOption": "STANDARD",
                                "price": {
                                  "supplierCurrency": "DZD",
                                  "agencyCurrency": "DZD",
                                  "base": 19470,
                                  "total": 29876.81,
                                  "b2bPrice": 29846.96
                                },
                                "fareDetailsBySegment": [
                                  {
                                    "segmentId": "43",
                                    "cabin": "ECONOMY",
                                    "fareBasis": "XYS0BCSA",
                                    "brandedFare": "STANDARD",
                                    "brandedFareLabel": "STANDARD",
                                    "bookingClass": "X",
                                    "includedCheckedBags": {
                                      "quantity": 1
                                    },
                                    "includedCabinBags": {
                                      "quantity": 1
                                    },
                                    "amenities": []
                                  }
                                ]
                              }
                            ],
                            "conditions": {
                              "refundBeforeDeparture": {
                                "allowed": false
                              },
                              "changeBeforeDeparture": {
                                "allowed": true
                              }
                            },
                            "fareRules": {
                              "exchange": {
                                "allowed": true
                              },
                              "refund": {
                                "allowed": false
                              },
                              "revalidation": {
                                "allowed": false
                              },
                              "supplierCurrency": "DZD",
                              "agencyCurrency": "DZD"
                            },
                            "validatingAirline": [
                              {
                                "code": "AF",
                                "name": "Air France"
                              }
                            ],
                            "additionalServices": {
                              "baggage": [
                                {
                                  "id": "bag_service_1",
                                  "type": "checked",
                                  "quantity": 1,
                                  "maxQuantity": 1,
                                  "maxWeightKg": null,
                                  "maxWeightUnit": null,
                                  "priceAmount": 8347,
                                  "convertedPriceAmount": 8347,
                                  "priceCurrency": "DZD",
                                  "applicablePassengers": [
                                    "1"
                                  ],
                                  "applicableSegments": [
                                    "43"
                                  ],
                                  "bookableByItinerary": true,
                                  "metadata": {
                                    "name": "CHECKED_BAG",
                                    "description": "1 additional checked bag(s)"
                                  }
                                }
                              ]
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`search_id` or `offer_id` is missing, or an id is not a valid token (letters, digits and `- _ . : = ~`, up to 128 characters).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameters: search_id and offer_id are required",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameters: search_id and offer_id are required",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_identifier": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_identifier",
                        "message": "`search_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "search_id",
                        "received": "fs bad/id"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_identifier",
                          "type": "validation_error",
                          "title": "Invalid identifier",
                          "message": "`search_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "search_id",
                          "details": {
                            "received": "fs bad/id"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_identifier"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The `search_id` is unknown, older than 24 hours, a `requestId`, or belongs to another agency (the answer is the same); or the offer is not in that search.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "search_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "search_not_found",
                        "message": "Search ID 'fs_does_not_exist' not found or has expired",
                        "code": "SEARCH_EXPIRED_OR_NOT_FOUND"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "search_not_found",
                          "type": "not_found",
                          "title": "Search not found",
                          "message": "Search ID 'fs_does_not_exist' not found or has expired",
                          "field": null,
                          "details": {
                            "code": "SEARCH_EXPIRED_OR_NOT_FOUND"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#search_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "offer_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "offer_not_found",
                        "message": "Offer ID 'fx_offer_00000000' not found in search results",
                        "code": "OFFER_NOT_FOUND_IN_SEARCH",
                        "available_offers": [
                          "fx_offer_b7a95439",
                          "fx_offer_250b4eb7"
                        ]
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "offer_not_found",
                          "type": "not_found",
                          "title": "Offer not found",
                          "message": "Offer ID 'fx_offer_00000000' not found in search results",
                          "field": null,
                          "details": {
                            "code": "OFFER_NOT_FOUND_IN_SEARCH",
                            "available_offers": [
                              "fx_offer_b7a95439",
                              "fx_offer_250b4eb7"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#offer_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "The supplier says the offer is gone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "offer_expired": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "offer_expired",
                        "message": "This offer has expired or is no longer available",
                        "code": "OFFER_EXPIRED",
                        "supplier_offer_id": "off_034q4dw6EAmaA6coqOjoLA"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "offer_expired",
                          "type": "not_found",
                          "title": "Offer expired",
                          "message": "This offer has expired or is no longer available",
                          "field": null,
                          "details": {
                            "code": "OFFER_EXPIRED",
                            "supplier_offer_id": "off_034q4dw6EAmaA6coqOjoLA"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#offer_expired"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 410
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The supplier refused or could not answer, or FX-Port's capacity towards it is full for a moment (`supplier_busy`: the supplier was not called and nothing was priced; wait for the seconds in `Retry-After`, then send the same request again). Otherwise retry once, then search again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "supplier_temporarily_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "supplier_temporarily_unavailable",
                        "message": "Supplier 'duffel_1' circuit breaker is open: retry in a few moments."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_temporarily_unavailable",
                          "type": "supplier_error",
                          "title": "Supplier temporarily unavailable",
                          "message": "Supplier 'duffel_1' circuit breaker is open: retry in a few moments.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_temporarily_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                        "retry_after": 5,
                        "errors": [
                          {
                            "code": "supplier_busy",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                            "title": "Supplier temporarily at capacity",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_8f3a61c2d9e4",
                          "status": 503,
                          "retry_after": 5
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 5
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Price a flight offer",
            "path": "/docs/flights/price"
          },
          {
            "label": "Flight pricing",
            "path": "/docs/flights/pricing"
          },
          {
            "label": "Search flights",
            "path": "/docs/flights/search"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/book_flight": {
      "post": {
        "operationId": "bookFlight",
        "tags": [
          "Flights"
        ],
        "summary": "Book a flight offer",
        "description": "Book an offer you have priced: ticketed at once (`booking_type: issue`) or held without payment (`hold`).\n\nPrice 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.\n\nThe 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.\n\n**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`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookFlightRequest"
              },
              "examples": {
                "hold_amadeus_algeria": {
                  "value": {
                    "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"
                    }
                  }
                },
                "instant_duffel": {
                  "value": {
                    "search_id": "fs_dfx_88eb64c9-6c36-47e6-bdaa-9ad3c1377ac4",
                    "offer_id": "fx_offer_b7a95439",
                    "booking_type": "issue",
                    "send_email": false,
                    "passengers": [
                      {
                        "type": "adult",
                        "gender": "MALE",
                        "first_name": "James",
                        "last_name": "Smith",
                        "date_of_birth": "1990-01-15",
                        "identity_documents": [
                          {
                            "type": "passport",
                            "number": "A12345678",
                            "issuing_country": "DZ",
                            "expiry_date": "2031-04-18"
                          }
                        ]
                      }
                    ],
                    "contact_details": {
                      "email": "traveler@example.com",
                      "phone_number": "+15555550123"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order. Keep `booking.reference` (the supplier's order id), `booking.pnr` and `postProcessing.bookingDbId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightOrder"
                },
                "examples": {
                  "hold_amadeus_algeria": {
                    "value": {
                      "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
                    }
                  },
                  "instant_duffel": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "7b166b31",
                      "searchId": "fs_dfx_88eb64c9-6c36-47e6-bdaa-9ad3c1377ac4",
                      "offerId": "fx_offer_b7a95439",
                      "processingTime": 0.40790867805480957,
                      "booking": {
                        "reference": "ord_0000BC0UuqWJKkrDO331Xc",
                        "status": "confirmed",
                        "pnr": "BOKT52",
                        "type": "instant",
                        "createdAt": "2026-11-01T14:24:41.453265Z",
                        "cancelledAt": null,
                        "content": "managed",
                        "syncedAt": "2026-11-01T14:24:41Z",
                        "availableActions": [
                          "cancel",
                          "change"
                        ],
                        "voidableUntil": null,
                        "cancellation": null
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel",
                        "offerId": "off_034q4rP6NMvbRSHMkUPbTE"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "pricing": {
                        "supplierCurrency": "USD",
                        "agencyCurrency": "DZD",
                        "basePrice": 16342.51,
                        "taxesAndFees": 5216.69,
                        "agencyCommission": 21.56,
                        "finalPrice": 21580.76,
                        "b2bPrice": 21559.2,
                        "additionalServices": []
                      },
                      "payment": {
                        "status": "paid",
                        "paidAt": "2026-11-01T14:24:41Z",
                        "paymentRequiredBy": null,
                        "priceGuaranteeExpiresAt": null,
                        "awaitingPayment": false
                      },
                      "documents": [
                        {
                          "type": "electronic_ticket",
                          "uniqueIdentifier": "1",
                          "passengerIds": [
                            "pas_0000BC0UogJGYlTqCNHhye"
                          ]
                        }
                      ],
                      "bookingReferences": [
                        {
                          "pnr": "BOKT52",
                          "carrier": {
                            "iataCode": "ZZ",
                            "name": "Duffel Airways",
                            "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                            "logoLockupUrl": null
                          }
                        }
                      ],
                      "passengers": [
                        {
                          "id": "pas_0000BC0UogJGYlTqCNHhye",
                          "type": "ADULT",
                          "title": "mr",
                          "givenName": "James",
                          "familyName": "Smith",
                          "gender": "m",
                          "bornOn": "1990-01-15",
                          "email": "traveler@example.com",
                          "phoneNumber": "+15555550123",
                          "infantPassengerId": null,
                          "loyaltyProgrammeAccounts": [],
                          "ticketNumber": "1"
                        }
                      ],
                      "travelerPricing": [
                        {
                          "travelerId": "pas_0000BC0UogJGYlTqCNHhye",
                          "travelerType": "ADULT",
                          "fareOption": "STANDARD",
                          "price": {
                            "supplierCurrency": "USD",
                            "agencyCurrency": "DZD",
                            "base": 16342.51,
                            "total": 21580.76,
                            "b2bPrice": 21559.2
                          },
                          "fareDetailsBySegment": [
                            {
                              "segmentId": "seg_0000BC0UogVJpx7ankvKl6",
                              "cabin": "ECONOMY",
                              "fareBasis": "Y20LGTN2",
                              "brandedFare": "Basic",
                              "brandedFareLabel": "Basic",
                              "bookingClass": "Economy",
                              "includedCheckedBags": {
                                "quantity": 1
                              },
                              "includedCabinBags": {
                                "quantity": 1
                              },
                              "amenities": []
                            }
                          ]
                        }
                      ],
                      "itinerary": [
                        {
                          "direction": "outbound",
                          "duration": "PT2H24M",
                          "stops": 0,
                          "fareBrandName": "Basic",
                          "origin": {
                            "airportCode": "ALG",
                            "airportName": "Houari Boumediene Airport",
                            "cityName": "Algiers",
                            "countryCode": "DZ",
                            "timeZone": "Africa/Algiers"
                          },
                          "destination": {
                            "airportCode": "CDG",
                            "airportName": "Paris Charles de Gaulle Airport",
                            "cityName": "Paris",
                            "countryCode": "FR",
                            "timeZone": "Europe/Paris"
                          },
                          "segments": [
                            {
                              "id": "seg_0000BC0UogVJpx7ankvKl6",
                              "departure": {
                                "airportCode": "ALG",
                                "airportName": "Houari Boumediene Airport",
                                "cityName": "Algiers",
                                "countryCode": "DZ",
                                "terminal": "2",
                                "datetime": "2026-12-15T10:50:00",
                                "timeZone": "Africa/Algiers"
                              },
                              "arrival": {
                                "airportCode": "CDG",
                                "airportName": "Paris Charles de Gaulle Airport",
                                "cityName": "Paris",
                                "countryCode": "FR",
                                "terminal": "1",
                                "datetime": "2026-12-15T13:14:00",
                                "timeZone": "Europe/Paris"
                              },
                              "flight": {
                                "carrierCode": "ZZ",
                                "carrierName": "Duffel Airways",
                                "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                                "flightNumber": "7426",
                                "aircraftCode": "773",
                                "aircraftName": "Boeing 777-300",
                                "operatingCarrier": "ZZ",
                                "operatingCarrierName": "Duffel Airways",
                                "operatingFlightNumber": "7426"
                              },
                              "duration": "PT2H24M",
                              "distance": "1370.3019844730272",
                              "stops": [],
                              "cabin": "ECONOMY",
                              "cabinMarketingName": "Economy",
                              "fareBasis": "",
                              "brandedFare": "BASIC",
                              "brandedFareLabel": "Basic",
                              "passengerDetails": [
                                {
                                  "passengerId": "pas_0000BC0UogJGYlTqCNHhye",
                                  "cabin": "ECONOMY",
                                  "cabinMarketingName": "Economy",
                                  "seat": null,
                                  "checkedBags": {
                                    "quantity": 1
                                  },
                                  "cabinBags": {
                                    "quantity": 1
                                  }
                                }
                              ]
                            }
                          ],
                          "conditions": {
                            "changeBeforeDeparture": {
                              "allowed": true
                            }
                          }
                        }
                      ],
                      "conditions": {
                        "refundBeforeDeparture": {
                          "allowed": false
                        },
                        "changeBeforeDeparture": {
                          "allowed": true
                        }
                      },
                      "owner": {
                        "id": "arl_00009VME7D6ivUu8dn35WK",
                        "iataCode": "ZZ",
                        "name": "Duffel Airways",
                        "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                        "logoLockupUrl": null,
                        "conditionsOfCarriageUrl": "https://duffelairways.com/dummy-url/conditions-of-carriage"
                      },
                      "services": [],
                      "exchangeRate": 250,
                      "meta": {
                        "supportHold": false,
                        "supportVoid": false,
                        "voidDeadline": null,
                        "voidWindowEndsAt": null,
                        "airlineInitiatedChanges": [],
                        "changes": [],
                        "users": [],
                        "orderMetadata": null
                      },
                      "postProcessing": {
                        "bookingDbId": "d47fc805-23b6-44f0-a892-99c65fbbc363",
                        "bucketLink": null,
                        "emailSent": false
                      },
                      "balanceDebited": false,
                      "transactionCreated": false,
                      "payoutCreated": false
                    }
                  },
                  "acd_fare_amadeus_algeria": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "c501d0d3",
                      "searchId": "fs_92df8ed2-bd63-4dfc-bede-e27f72b04f2f",
                      "offerId": "fx_offer_40738cc9",
                      "processingTime": 9.402973413467407,
                      "booking": {
                        "reference": "eJzTd9ePNPcI93UBAAtdAmY",
                        "status": "confirmed",
                        "pnr": "Y7HWMD",
                        "type": "instant",
                        "createdAt": "2026-11-01T14:18:00.000",
                        "cancelledAt": null,
                        "content": "managed",
                        "syncedAt": "2026-11-01T14:18:00.000",
                        "availableActions": [],
                        "voidableUntil": "2026-11-01T23:00:00Z",
                        "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": 16390,
                        "taxesAndFees": 62312.01,
                        "agencyCommission": 78.71,
                        "finalPrice": 78780.72,
                        "b2bPrice": 78702.01,
                        "additionalServices": [],
                        "acd": {
                          "applied": true,
                          "scenario": "B"
                        }
                      },
                      "requiresACD": true,
                      "payment": {
                        "status": "paid",
                        "paidAt": "2026-11-01T14:18:00.000",
                        "paymentRequiredBy": null,
                        "priceGuaranteeExpiresAt": null,
                        "awaitingPayment": false
                      },
                      "documents": [],
                      "bookingReferences": [
                        {
                          "pnr": "Y7HWMD",
                          "carrier": {
                            "iataCode": "AZ",
                            "name": "ITA",
                            "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AZ.svg",
                            "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AZ.svg"
                          }
                        }
                      ],
                      "passengers": [
                        {
                          "id": "1",
                          "type": "ADULT",
                          "title": "mr",
                          "givenName": "JOHN",
                          "familyName": "DOE",
                          "gender": "male",
                          "bornOn": "1985-04-10",
                          "email": "traveler@example.com",
                          "phoneNumber": "15555550123",
                          "infantPassengerId": null,
                          "loyaltyProgrammeAccounts": [],
                          "ticketNumber": null
                        }
                      ],
                      "travelerPricing": [
                        {
                          "travelerId": "1",
                          "travelerType": "ADULT",
                          "fareOption": "STANDARD",
                          "price": {
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD",
                            "base": 16390,
                            "total": 78780.72,
                            "b2bPrice": 78702
                          },
                          "fareDetailsBySegment": [
                            {
                              "segmentId": "31",
                              "cabin": "ECONOMY",
                              "fareBasis": "SETCLSE9",
                              "brandedFare": "COMFORT",
                              "brandedFareLabel": "COMFORT",
                              "bookingClass": "S",
                              "includedCheckedBags": {
                                "quantity": 1
                              },
                              "includedCabinBags": {
                                "quantity": 1
                              },
                              "amenities": []
                            },
                            {
                              "segmentId": "32",
                              "cabin": "ECONOMY",
                              "fareBasis": "SETCLSE9",
                              "brandedFare": "COMFORT",
                              "brandedFareLabel": "COMFORT",
                              "bookingClass": "S",
                              "includedCheckedBags": {
                                "quantity": 1
                              },
                              "includedCabinBags": {
                                "quantity": 1
                              },
                              "amenities": []
                            }
                          ]
                        }
                      ],
                      "itinerary": [
                        {
                          "direction": "outbound",
                          "duration": "PT10H45M",
                          "stops": 1,
                          "fareBrandName": "COMFORT",
                          "origin": {
                            "airportCode": "CDG",
                            "airportName": "Paris Charles de Gaulle Airport",
                            "cityName": "Paris",
                            "countryCode": "FR",
                            "timeZone": "Europe/Paris"
                          },
                          "destination": {
                            "airportCode": "ALG",
                            "airportName": "Houari Boumediene Airport",
                            "cityName": "Algiers",
                            "countryCode": "DZ",
                            "timeZone": "Africa/Algiers"
                          },
                          "segments": [
                            {
                              "id": "31",
                              "departure": {
                                "airportCode": "CDG",
                                "airportName": "Paris Charles de Gaulle Airport",
                                "cityName": "Paris",
                                "countryCode": "FR",
                                "terminal": "2B",
                                "datetime": "2026-12-16T06:10:00",
                                "timeZone": "Europe/Paris"
                              },
                              "arrival": {
                                "airportCode": "FCO",
                                "airportName": "Leonardo da Vinci-Fiumicino Airport",
                                "cityName": "Rome",
                                "countryCode": "IT",
                                "terminal": "1",
                                "datetime": "2026-12-16T08:15:00",
                                "timeZone": "Europe/Rome"
                              },
                              "flight": {
                                "carrierCode": "AZ",
                                "carrierName": "ITA",
                                "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AZ.svg",
                                "flightNumber": "317",
                                "aircraftCode": "32N",
                                "aircraftName": null,
                                "operatingCarrier": "AZ",
                                "operatingCarrierName": "ITA",
                                "operatingFlightNumber": "317"
                              },
                              "duration": "PT2H5M",
                              "distance": null,
                              "stops": [],
                              "cabin": "ECONOMY",
                              "cabinMarketingName": "Economy",
                              "fareBasis": "SETCLSE9",
                              "brandedFare": "COMFORT",
                              "brandedFareLabel": "COMFORT",
                              "passengerDetails": [
                                {
                                  "passengerId": "1",
                                  "cabin": "ECONOMY",
                                  "cabinMarketingName": "Economy",
                                  "seat": null,
                                  "checkedBags": {
                                    "quantity": 1
                                  },
                                  "cabinBags": {
                                    "quantity": 1
                                  }
                                }
                              ]
                            },
                            {
                              "id": "32",
                              "departure": {
                                "airportCode": "FCO",
                                "airportName": "Leonardo da Vinci-Fiumicino Airport",
                                "cityName": "Rome",
                                "countryCode": "IT",
                                "terminal": "1",
                                "datetime": "2026-12-16T15:00:00",
                                "timeZone": "Europe/Rome"
                              },
                              "arrival": {
                                "airportCode": "ALG",
                                "airportName": "Houari Boumediene Airport",
                                "cityName": "Algiers",
                                "countryCode": "DZ",
                                "terminal": "I",
                                "datetime": "2026-12-16T16:55:00",
                                "timeZone": "Africa/Algiers"
                              },
                              "flight": {
                                "carrierCode": "AZ",
                                "carrierName": "ITA",
                                "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AZ.svg",
                                "flightNumber": "800",
                                "aircraftCode": "319",
                                "aircraftName": null,
                                "operatingCarrier": "AZ",
                                "operatingCarrierName": "ITA",
                                "operatingFlightNumber": "800"
                              },
                              "duration": "PT1H55M",
                              "distance": null,
                              "stops": [],
                              "cabin": "ECONOMY",
                              "cabinMarketingName": "Economy",
                              "fareBasis": "SETCLSE9",
                              "brandedFare": "COMFORT",
                              "brandedFareLabel": "COMFORT",
                              "passengerDetails": [
                                {
                                  "passengerId": "1",
                                  "cabin": "ECONOMY",
                                  "cabinMarketingName": "Economy",
                                  "seat": null,
                                  "checkedBags": {
                                    "quantity": 1
                                  },
                                  "cabinBags": {
                                    "quantity": 1
                                  }
                                }
                              ]
                            }
                          ],
                          "conditions": {
                            "changeBeforeDeparture": {
                              "allowed": true
                            }
                          }
                        }
                      ],
                      "conditions": {
                        "refundBeforeDeparture": {
                          "allowed": false
                        },
                        "changeBeforeDeparture": {
                          "allowed": true
                        }
                      },
                      "owner": {
                        "id": null,
                        "iataCode": "AZ",
                        "name": "ITA",
                        "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AZ.svg",
                        "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AZ.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": false
                        },
                        "exchange": {
                          "allowed": true
                        },
                        "agencyCurrency": "DZD"
                      },
                      "postProcessing": {
                        "bookingDbId": "2acf8907-f5c5-4d5d-93b5-3e4a038ccdf1",
                        "bucketLink": null,
                        "emailSent": false
                      },
                      "balanceDebited": false,
                      "transactionCreated": false,
                      "payoutCreated": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is refused before the supplier is called. `errors[0].field` names the field: `passengers[0].first_name`, `contact_details.email` ...",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameters: search_id and offer_id are required",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameters: search_id and offer_id are required",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "missing_field",
                        "message": "`passengers[0].first_name` is required.",
                        "field": "passengers[0].first_name"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "`passengers[0].first_name` is required.",
                          "field": "passengers[0].first_name",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_type": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_type",
                        "message": "`booking_type` must be \"issue\" (instant ticket) or \"hold\".",
                        "field": "booking_type",
                        "allowed": [
                          "issue",
                          "hold"
                        ],
                        "received": "later"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_type",
                          "type": "validation_error",
                          "title": "Invalid booking type",
                          "message": "`booking_type` must be \"issue\" (instant ticket) or \"hold\".",
                          "field": "booking_type",
                          "details": {
                            "allowed": [
                              "issue",
                              "hold"
                            ],
                            "received": "later"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_type"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "hold_not_supported": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "hold_not_supported",
                        "message": "This offer cannot be held; it must be booked instantly.",
                        "offer_id": "fx_offer_b7a95439"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "hold_not_supported",
                          "type": "validation_error",
                          "title": "Hold not supported",
                          "message": "This offer cannot be held; it must be booked instantly.",
                          "field": null,
                          "details": {
                            "offer_id": "fx_offer_b7a95439"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#hold_not_supported"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "too_many_passengers": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "too_many_passengers",
                        "message": "A booking takes at most 18 passengers.",
                        "field": "passengers",
                        "received": 19
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "too_many_passengers",
                          "type": "validation_error",
                          "title": "Too many passengers",
                          "message": "A booking takes at most 18 passengers.",
                          "field": "passengers",
                          "details": {
                            "received": 19
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#too_many_passengers"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Live only: the balance of your agency is lower than what the booking debits (`pricing.b2bPrice`). The sandbox debits nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "insufficient_balance": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "insufficient_balance",
                        "message": "Agency balance is insufficient. Available: 120.0 DZD, Required: 29876.81 DZD.",
                        "details": {
                          "available": 120,
                          "required": 29876.81,
                          "currency": "DZD"
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "insufficient_balance",
                          "type": "payment_error",
                          "title": "Insufficient balance",
                          "message": "Agency balance is insufficient. Available: 120.0 DZD, Required: 29876.81 DZD.",
                          "field": null,
                          "details": {
                            "available": 120,
                            "required": 29876.81,
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#insufficient_balance"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 402
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency or the IP address is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The `search_id` is unknown, older than 24 hours or belongs to another agency (the answer is the same); the offer is not in that search; or the offer was never priced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "search_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "search_not_found",
                        "message": "Search ID 'fs_does_not_exist' not found or has expired",
                        "code": "SEARCH_EXPIRED_OR_NOT_FOUND"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "search_not_found",
                          "type": "not_found",
                          "title": "Search not found",
                          "message": "Search ID 'fs_does_not_exist' not found or has expired",
                          "field": null,
                          "details": {
                            "code": "SEARCH_EXPIRED_OR_NOT_FOUND"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#search_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "offer_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "offer_not_found",
                        "message": "Offer ID 'fx_offer_00000000' not found in search results",
                        "code": "OFFER_NOT_FOUND_IN_SEARCH"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "offer_not_found",
                          "type": "not_found",
                          "title": "Offer not found",
                          "message": "Offer ID 'fx_offer_00000000' not found in search results",
                          "field": null,
                          "details": {
                            "code": "OFFER_NOT_FOUND_IN_SEARCH"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#offer_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "offer_not_priced": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "offer_not_priced",
                        "message": "Priced offer 'fx_offer_250b4eb7' not found in cache. Please price the offer first before booking.",
                        "code": "OFFER_NOT_PRICED"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "offer_not_priced",
                          "type": "not_found",
                          "title": "Offer not priced",
                          "message": "Priced offer 'fx_offer_250b4eb7' not found in cache. Please price the offer first before booking.",
                          "field": null,
                          "details": {
                            "code": "OFFER_NOT_PRICED"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#offer_not_priced"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The offer, or the search it belongs to, was already booked (Duffel allows one booking per search), or another request is booking this offer. Read the booking before you try again; do not repeat the call blindly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "duplicate_booking": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "duplicate_booking",
                        "message": "This offer has already been booked successfully (PNR: BOKT52). Duplicate submissions are not allowed.",
                        "code": "OFFER_ALREADY_BOOKED",
                        "pnr": "BOKT52"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "duplicate_booking",
                          "type": "conflict",
                          "title": "Duplicate booking",
                          "message": "This offer has already been booked successfully (PNR: BOKT52). Duplicate submissions are not allowed.",
                          "field": null,
                          "details": {
                            "code": "OFFER_ALREADY_BOOKED",
                            "pnr": "BOKT52"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#duplicate_booking"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 409
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "offer_request_already_booked": {
                    "value": {
                      "environment": "sandbox",
                      "requestId": "0a9fa143",
                      "searchId": "fs_dfx_88eb64c9-6c36-47e6-bdaa-9ad3c1377ac4",
                      "offerId": "fx_offer_2dc0bf77",
                      "processingTime": 0.219,
                      "error": {
                        "message": "Field 'selected_offers' has offers included in a offer request that has already been booked; please perform a new search",
                        "code": "offer_request_already_booked",
                        "type": "validation_error",
                        "details": [
                          {
                            "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                            "source": {
                              "field": "selected_offers",
                              "pointer": "/selected_offers"
                            },
                            "title": "Can't book multiple offers from the same offer request",
                            "type": "validation_error",
                            "message": "Field 'selected_offers' has offers included in a offer request that has already been booked; please perform a new search",
                            "code": "offer_request_already_booked"
                          }
                        ]
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "meta": {
                        "supportHold": false,
                        "supportVoid": false,
                        "voidDeadline": null,
                        "request_id": "0a9fa143",
                        "status": 409
                      },
                      "balanceDebited": false,
                      "transactionCreated": false,
                      "payoutCreated": false,
                      "success": false,
                      "errors": [
                        {
                          "code": "offer_request_already_booked",
                          "type": "supplier_error",
                          "title": "Offer request already booked",
                          "message": "Field 'selected_offers' has offers included in a offer request that has already been booked; please perform a new search",
                          "field": "selected_offers",
                          "details": {
                            "supplier_type": "validation_error",
                            "supplier_details": [
                              {
                                "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                                "source": {
                                  "field": "selected_offers",
                                  "pointer": "/selected_offers"
                                },
                                "title": "Can't book multiple offers from the same offer request",
                                "type": "validation_error",
                                "message": "Field 'selected_offers' has offers included in a offer request that has already been booked; please perform a new search",
                                "code": "offer_request_already_booked"
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#offer_request_already_booked"
                        }
                      ],
                      "request_id": "0a9fa143"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The supplier refused a detail of the booking. The body keeps the supplier's own `error` (`message`, `code`, `type`, `details[]` with `source.field`); `balanceDebited` is `false`. Amadeus errors have a numeric `error.code` as text, Duffel errors a name such as `validation_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "validation_type": {
                    "value": {
                      "environment": "sandbox",
                      "requestId": "1bddbdb6",
                      "searchId": "fs_dfx_88eb64c9-6c36-47e6-bdaa-9ad3c1377ac4",
                      "offerId": "fx_offer_2dc0bf77",
                      "processingTime": 0.219,
                      "error": {
                        "message": "Field 'born_on' is invalid. Expected a date in ISO 8601 format (YYYY-MM-DD)",
                        "code": "validation_type",
                        "type": "validation_error",
                        "details": [
                          {
                            "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                            "source": {
                              "field": "born_on",
                              "pointer": "/passengers/0/born_on"
                            },
                            "title": "Invalid type",
                            "type": "validation_error",
                            "message": "Field 'born_on' is invalid. Expected a date in ISO 8601 format (YYYY-MM-DD)",
                            "code": "validation_type"
                          }
                        ]
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "meta": {
                        "supportHold": false,
                        "supportVoid": false,
                        "voidDeadline": null,
                        "request_id": "1bddbdb6",
                        "status": 422
                      },
                      "balanceDebited": false,
                      "transactionCreated": false,
                      "payoutCreated": false,
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_type",
                          "type": "supplier_error",
                          "title": "Validation type",
                          "message": "Field 'born_on' is invalid. Expected a date in ISO 8601 format (YYYY-MM-DD)",
                          "field": "born_on",
                          "details": {
                            "supplier_type": "validation_error",
                            "supplier_details": [
                              {
                                "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                                "source": {
                                  "field": "born_on",
                                  "pointer": "/passengers/0/born_on"
                                },
                                "title": "Invalid type",
                                "type": "validation_error",
                                "message": "Field 'born_on' is invalid. Expected a date in ISO 8601 format (YYYY-MM-DD)",
                                "code": "validation_type"
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#validation_type"
                        }
                      ],
                      "request_id": "1bddbdb6"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The booking was created at the supplier but FX-Port could not finish storing it. **Do not book again**: look for the booking in your history, or contact support with the `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "post_booking_failed": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "post_booking_failed",
                        "message": "The booking was placed but could not be recorded completely. Contact support with this request id before retrying.",
                        "booking_type": "hold",
                        "requestId": "7f1c2d3e"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "post_booking_failed",
                          "type": "internal_error",
                          "title": "Post booking failed",
                          "message": "The booking was placed but could not be recorded completely. Contact support with this request id before retrying.",
                          "field": null,
                          "details": {
                            "booking_type": "hold",
                            "requestId": "7f1c2d3e"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#post_booking_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 500
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Two causes. The supplier itself failed (`supplier_temporarily_unavailable` or the supplier's own code: it answered with a status of 500 or more, `meta.supplierStatusCode`): a booking can exist at the airline, so read your bookings before you retry. Or, live only for an `issue` booking, another operation of your agency holds the balance in the same currency for a moment (`balance_lock_timeout`): nothing was booked and nothing was charged, and the offer is released, so send the same request again after a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "supplier_temporarily_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "supplier_temporarily_unavailable",
                        "message": "Supplier 'duffel_1' circuit breaker is open: retry in a few moments."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_temporarily_unavailable",
                          "type": "supplier_error",
                          "title": "Supplier temporarily unavailable",
                          "message": "Supplier 'duffel_1' circuit breaker is open: retry in a few moments.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_temporarily_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "balance_lock_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "balance_lock_timeout",
                        "message": "The agency balance is busy with another operation. Retry in a few seconds.",
                        "currency": "DZD"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "balance_lock_timeout",
                          "type": "internal_error",
                          "title": "Balance lock timeout",
                          "message": "The agency balance is busy with another operation. Retry in a few seconds.",
                          "field": null,
                          "details": {
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#balance_lock_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Book a flight",
            "path": "/docs/flights/book"
          },
          {
            "label": "Flight passengers & ages",
            "path": "/docs/flights/passengers"
          },
          {
            "label": "Booking flows",
            "path": "/docs/flights/booking-flows"
          },
          {
            "label": "Upload ACD documents",
            "path": "/docs/flights/acd-documents"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/get_booking": {
      "post": {
        "operationId": "getBooking",
        "tags": [
          "Flights"
        ],
        "summary": "Get a flight booking",
        "description": "Read a booking of your agency by its PNR, its supplier order id or its FX-Port id: status, prices, ticket number, the links of the PDF tickets and the ACD state.\n\n`booking_id` takes any of the three values (`order_id` and `pnr` are other names of the same field; when they are sent with a different value, `booking_id` wins and an `ignored_field` warning names the dropped one). Prefer the FX-Port `id` (a UUID) or the order id: **PNRs are not unique** across suppliers and airlines. A booking that is not yours, or that belongs to the other environment, is answered `404 booking_not_found`, the same as one that does not exist.\n\n`booking.status` is `confirmed`, `awaiting_payment` (a hold), `cancelled`, `partial_refund` or `failed`. `ticket_number` is `null` until the airline returns it: ask `get_ticket_number`. `acd_required` says whether the booking needs ACD passports and `acd_submitted` whether they are all uploaded; `acd_documents[]` and `acd_links` list the files that were uploaded (public links, without FX-Port's storage keys), but `get_acd_documents` is the better source for the state of each traveler. The links `pdf_url` and `pdf_url_no_price` (`bucket_link` and `bucket_link_no_price`) are **public**: anybody who has them can open the PDF, so treat them as sensitive. The booking is a database row: only the keys listed below are part of the contract.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightBookingLookupRequest"
              },
              "examples": {
                "by_pnr": {
                  "value": {
                    "booking_id": "Y7PAEU"
                  }
                },
                "by_fx_port_id": {
                  "value": {
                    "booking_id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7"
                  }
                },
                "with_stored_answer": {
                  "value": {
                    "booking_id": "Y7PAEU",
                    "include_api_response": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightBookingResponse"
                },
                "examples": {
                  "confirmed_after_payment": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "0c2b315c",
                      "processingTime": 0.272,
                      "booking": {
                        "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
                        "booking_id": "Y7PAEU",
                        "amadeus_id": "eJzTd9ePNA9wdA0FAAs8AmE",
                        "status": "confirmed",
                        "created_at": "2026-11-01T15:26:14.871035+00:00",
                        "paid_at": "2026-11-01T15:26:21.961664+00:00",
                        "updated_at": "2026-11-01T15:26:21.998944+00:00",
                        "total_price": 4930.37,
                        "agency_commission": 4.93,
                        "currency": "DZD",
                        "departure_airport": "ALG",
                        "arrival_airport": "ORN",
                        "departure_date": "2026-12-22T19:45:00",
                        "arrival_date": "2026-12-22T20:45:00",
                        "return_departure_date": null,
                        "return_arrival_date": null,
                        "included_checked_bags": "0",
                        "ticketing_agreement": "DELAY_TO_CANCEL",
                        "supplier_name": "Amadeus Algeria",
                        "supplier_id": "amadeus_aqc_dz_1",
                        "ticket_number": "124-2446977397",
                        "bucket_link": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
                        "bucket_link_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
                        "email_sent": false,
                        "require_acd": false,
                        "acd_submitted": false,
                        "acd_links": null,
                        "acd_amount": null,
                        "acd_paid": false,
                        "acd_documents_revision": 0,
                        "acd_documents": null,
                        "has_changed": false,
                        "new_flight_details": null,
                        "voided_at": null,
                        "refund_amount": null,
                        "refund_currency": null,
                        "refund_method": null,
                        "agent_id": null,
                        "environment": "sandbox",
                        "pricingBreakdown": {
                          "supplier_currency": "DZD",
                          "agency_currency": "DZD",
                          "base_price": 3160,
                          "taxes_and_fees": 1765.44,
                          "agency_commission": 4.93,
                          "final_price": 4930.37,
                          "b2b_price": 4925.44,
                          "created_at": "2026-11-01T15:26:22.176516+00:00",
                          "environment": "sandbox"
                        },
                        "travelerPricing": [
                          {
                            "traveler_id": "1",
                            "traveler_type": "ADULT",
                            "fare_option": "STANDARD",
                            "supplier_currency": "DZD",
                            "agency_currency": "DZD",
                            "base": 3160,
                            "total": 4930.37,
                            "b2b_price": 4925.44,
                            "created_at": "2026-11-01T15:26:15.133066+00:00",
                            "environment": "sandbox"
                          }
                        ],
                        "balanceOperations": [],
                        "order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
                        "pnr": "Y7PAEU",
                        "pdf_url": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
                        "pdf_url_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
                        "acd_required": false,
                        "requires_acd": false
                      }
                    }
                  },
                  "acd_booking_with_a_passport": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "251723b0",
                      "processingTime": 0.292,
                      "booking": {
                        "id": "2acf8907-f5c5-4d5d-93b5-3e4a038ccdf1",
                        "booking_id": "Y7HWMD",
                        "amadeus_id": "eJzTd9ePNPcI93UBAAtdAmY",
                        "status": "confirmed",
                        "created_at": "2026-11-01T14:19:02.015222+00:00",
                        "paid_at": "2026-11-01T14:19:01.979761+00:00",
                        "updated_at": "2026-11-01T14:24:33.08805+00:00",
                        "total_price": 78780.71,
                        "agency_commission": 78.71,
                        "currency": "DZD",
                        "departure_airport": "CDG",
                        "arrival_airport": "ALG",
                        "departure_date": "2026-12-16T06:10:00",
                        "arrival_date": "2026-12-16T16:55:00",
                        "return_departure_date": null,
                        "return_arrival_date": null,
                        "included_checked_bags": "0",
                        "ticketing_agreement": "CONFIRM",
                        "supplier_name": "Amadeus Algeria",
                        "supplier_id": "amadeus_aqc_dz_1",
                        "ticket_number": null,
                        "bucket_link": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE.pdf",
                        "bucket_link_no_price": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE-no-price.pdf",
                        "email_sent": false,
                        "require_acd": true,
                        "acd_submitted": true,
                        "acd_links": [
                          "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142432_087a6dc8.png"
                        ],
                        "acd_amount": 235,
                        "acd_paid": false,
                        "acd_documents_revision": 2,
                        "acd_documents": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "link": "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142432_087a6dc8.png",
                            "filename": "rescan.png",
                            "content_type": "image/png",
                            "size": 69,
                            "format": "png",
                            "uploaded_at": "2026-11-01T14:24:33.057044+00:00",
                            "replaced_count": 1,
                            "previous_links": [
                              {
                                "link": "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142430_9b354245.pdf",
                                "uploaded_at": "2026-11-01T14:24:31.256905+00:00",
                                "replaced_at": "2026-11-01T14:24:33.057044+00:00"
                              }
                            ],
                            "source": "api"
                          }
                        ],
                        "has_changed": false,
                        "new_flight_details": null,
                        "voided_at": null,
                        "refund_amount": null,
                        "refund_currency": null,
                        "refund_method": null,
                        "agent_id": null,
                        "environment": "sandbox",
                        "pricingBreakdown": {
                          "supplier_currency": "DZD",
                          "agency_currency": "DZD",
                          "base_price": 16390,
                          "taxes_and_fees": 62312,
                          "agency_commission": 78.71,
                          "final_price": 78780.71,
                          "b2b_price": 78702,
                          "created_at": "2026-11-01T14:19:02.205874+00:00",
                          "environment": "sandbox"
                        },
                        "travelerPricing": [
                          {
                            "traveler_id": "1",
                            "traveler_type": "ADULT",
                            "fare_option": "STANDARD",
                            "supplier_currency": "DZD",
                            "agency_currency": "DZD",
                            "base": 16390,
                            "total": 78780.72,
                            "b2b_price": 78702.01,
                            "created_at": "2026-11-01T14:19:02.115601+00:00",
                            "environment": "sandbox"
                          }
                        ],
                        "balanceOperations": [],
                        "order_id": "eJzTd9ePNPcI93UBAAtdAmY",
                        "pnr": "Y7HWMD",
                        "pdf_url": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE.pdf",
                        "pdf_url_no_price": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE-no-price.pdf",
                        "acd_required": true,
                        "requires_acd": true
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`booking_id` is missing (or none of `order_id` and `pnr`), or the id is not a valid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "booking_id is required",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "booking_id is required",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "`booking_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "booking_id",
                        "received": "Y7 PAEU/1"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`booking_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "booking_id",
                          "details": {
                            "received": "Y7 PAEU/1"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No booking of your agency in the environment of your key has this id. A booking of another agency is answered the same way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "No booking found for 'ZZZZZZ'"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No booking found for 'ZZZZZZ'",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Get a booking",
            "path": "/docs/flights/get-booking"
          },
          {
            "label": "Post-booking operations",
            "path": "/docs/flights/post-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/get_ticket": {
      "post": {
        "operationId": "getTicket",
        "tags": [
          "Flights"
        ],
        "summary": "Get the ticket PDF of a booking",
        "description": "Get the download links of the ticket PDF of a booking (the hold voucher for a hold). The PDF is generated after the booking: call again until `ticket_ready` is `true`.\n\n`ticket_ready` is `true` as soon as the booking has a PDF link, whatever its status: it does not mean that an e-ticket exists, ask `get_ticket_number` for that. `get_booking` returns the same links as `booking.pdf_url` and `booking.pdf_url_no_price` together with the rest of the booking.\n\n**The links are public.** Anybody who has a `pdf_url` can open the PDF with no key and no login; it holds the traveler's name, the itinerary and the fare. Do not log them, do not send them to third parties, and prefer to download the PDF on your server and serve it yourself.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightBookingLookupRequest"
              },
              "examples": {
                "by_pnr": {
                  "value": {
                    "booking_id": "Y7PAEU"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The links, or `ticket_ready: false` with a message while the PDF is being generated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightTicketResponse"
                },
                "examples": {
                  "ready": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "71fdaeb7",
                      "ticket_ready": true,
                      "booking_id": "Y7PAEU",
                      "download_url": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
                      "download_url_with_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
                      "download_url_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf"
                    }
                  },
                  "not_ready_yet": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "76be69dc",
                      "ticket_ready": false,
                      "booking_id": "Y7PAEU",
                      "message": "Ticket is not ready yet. Please retry in a moment."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`booking_id` is missing (or none of `order_id` and `pnr`), or the id is not a valid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "booking_id is required",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "booking_id is required",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "`booking_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "booking_id",
                        "received": "Y7 PAEU/1"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`booking_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "booking_id",
                          "details": {
                            "received": "Y7 PAEU/1"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such booking in your agency and environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "No booking found for 'ZZZZZZ'"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No booking found for 'ZZZZZZ'",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Get the ticket PDF",
            "path": "/docs/flights/get-ticket"
          },
          {
            "label": "Get a booking",
            "path": "/docs/flights/get-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/get_ticket_number": {
      "post": {
        "operationId": "getTicketNumber",
        "tags": [
          "Flights"
        ],
        "summary": "Get the e-ticket number of a booking",
        "description": "Get the e-ticket number of a booking, asking the airline for it when it is not stored yet. Use it after an instant booking whose number has not arrived, or to find out whether the tickets of a booking were issued.\n\nThis is a read endpoint that can **do work**: when the number is not stored, FX-Port asks the supplier (Amadeus, Travelport or Duffel) for the order, stores the number on the booking and can send the confirmation e-mail that was held back until the ticket exists. Calling it twice is harmless. Wait 5 to 10 seconds between calls instead of looping. `ticket_available: false` with a message means the airline has not returned a number yet. A hold has no ticket number until it is paid.\n\n`booking_response` is the stored answer of the booking, without the supplier's costs and commissions. Only bookings of your agency in the environment of your key are answered.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightBookingLookupRequest"
              },
              "examples": {
                "by_pnr": {
                  "value": {
                    "booking_id": "Y7PAEU"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The ticket number, or `ticket_available: false` with a message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightTicketNumberResponse"
                },
                "examples": {
                  "issued": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "a8e2164f",
                      "ticket_available": true,
                      "ticket_number": "124-2446977397",
                      "booking_id": "Y7PAEU",
                      "supplier_order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
                      "booking_response": {
                        "booking": {
                          "pnr": "Y7PAEU",
                          "type": "instant",
                          "status": "confirmed",
                          "content": "managed",
                          "syncedAt": "2026-11-01T15:26:21.896668",
                          "createdAt": "2026-11-01T15:26:21.896668",
                          "reference": "eJzTd9ePNA9wdA0FAAs8AmE",
                          "cancelledAt": null,
                          "cancellation": null,
                          "voidableUntil": "2026-11-01T23:00:00Z",
                          "availableActions": []
                        },
                        "supplier": {
                          "id": "amadeus_aqc_dz_1",
                          "name": "Amadeus AQC",
                          "offerId": "1",
                          "technology": "amadeus_aqc"
                        },
                        "pricing": {
                          "b2bPrice": 4736,
                          "basePrice": 3160,
                          "finalPrice": 4930.37,
                          "taxesAndFees": 1770.37,
                          "agencyCurrency": "DZD",
                          "agencyCommission": 4.93,
                          "supplierCurrency": "DZD",
                          "additionalServices": []
                        },
                        "payment": {
                          "paidAt": "2026-11-01T15:26:21.896710",
                          "status": "paid",
                          "awaitingPayment": false,
                          "paymentRequiredBy": null,
                          "priceGuaranteeExpiresAt": null
                        },
                        "documents": [
                          {
                            "type": "electronic_ticket",
                            "status": "ISSUED",
                            "segmentIds": [
                              "1"
                            ],
                            "documentType": "ETICKET",
                            "passengerIds": [
                              "1"
                            ],
                            "uniqueIdentifier": "124-2446977397"
                          }
                        ],
                        "bookingReferences": [
                          {
                            "pnr": "Y7PAEU",
                            "carrier": {
                              "name": "Air Algerie",
                              "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                              "iataCode": "AH",
                              "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg"
                            }
                          }
                        ],
                        "passengers": [
                          {
                            "id": "1",
                            "type": "ADULT",
                            "email": "traveler@example.com",
                            "title": "mr",
                            "bornOn": "1988-06-15",
                            "gender": "male",
                            "givenName": "JAMES",
                            "familyName": "SMITH",
                            "phoneNumber": "15555550123",
                            "ticketNumber": "124-2446977397",
                            "infantPassengerId": null,
                            "loyaltyProgrammeAccounts": []
                          }
                        ],
                        "owner": {
                          "id": null,
                          "name": "Air Algerie",
                          "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                          "iataCode": "AH",
                          "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg",
                          "conditionsOfCarriageUrl": null
                        }
                      }
                    }
                  },
                  "not_yet_returned": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "921cee35",
                      "ticket_available": false,
                      "ticket_number": null,
                      "booking_id": "Y7PAEU",
                      "supplier_order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
                      "booking_response": {
                        "booking": {
                          "pnr": "Y7PAEU",
                          "type": "hold",
                          "status": "pending",
                          "content": "managed",
                          "syncedAt": "2026-11-01T15:26:00.000",
                          "createdAt": "2026-11-01T15:26:00.000",
                          "reference": "eJzTd9ePNA9wdA0FAAs8AmE",
                          "cancelledAt": null,
                          "cancellation": null,
                          "voidableUntil": null,
                          "availableActions": [
                            "confirm",
                            "cancel"
                          ]
                        },
                        "supplier": {
                          "id": "amadeus_aqc_dz_1",
                          "name": "Amadeus AQC",
                          "offerId": "3",
                          "technology": "amadeus_aqc"
                        },
                        "pricing": {
                          "acd": {
                            "applied": false
                          },
                          "b2bPrice": 4736,
                          "basePrice": 3160,
                          "finalPrice": 4930.37,
                          "taxesAndFees": 1770.37,
                          "agencyCurrency": "DZD",
                          "agencyCommission": 4.93,
                          "supplierCurrency": "DZD",
                          "additionalServices": []
                        },
                        "payment": {
                          "paidAt": null,
                          "status": "awaiting_payment",
                          "awaitingPayment": true,
                          "paymentRequiredBy": null,
                          "priceGuaranteeExpiresAt": null
                        },
                        "documents": [],
                        "bookingReferences": [
                          {
                            "pnr": "Y7PAEU",
                            "carrier": {
                              "name": "Air Algerie",
                              "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                              "iataCode": "AH",
                              "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg"
                            }
                          }
                        ],
                        "passengers": [
                          {
                            "id": "1",
                            "type": "ADULT",
                            "email": "traveler@example.com",
                            "title": "mr",
                            "bornOn": "1988-06-15",
                            "gender": "male",
                            "givenName": "JAMES",
                            "familyName": "SMITH",
                            "phoneNumber": "15555550123",
                            "ticketNumber": null,
                            "infantPassengerId": null,
                            "loyaltyProgrammeAccounts": []
                          }
                        ],
                        "owner": {
                          "id": null,
                          "name": "Air Algerie",
                          "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                          "iataCode": "AH",
                          "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg",
                          "conditionsOfCarriageUrl": null
                        }
                      },
                      "message": "The airline has not returned a ticket number yet. Please retry shortly."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`booking_id` is missing or not a valid token, or the booking is not of a supplier that can answer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "booking_id is required",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "booking_id is required",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "unsupported_supplier": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "unsupported_supplier",
                        "message": "Ticket-number retrieval is only supported for Amadeus, Travelport and Duffel bookings."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "unsupported_supplier",
                          "type": "validation_error",
                          "title": "Unsupported supplier",
                          "message": "Ticket-number retrieval is only supported for Amadeus, Travelport and Duffel bookings.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#unsupported_supplier"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such booking in your agency and environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "No booking found for 'ZZZZZZ'"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No booking found for 'ZZZZZZ'",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Get the e-ticket number",
            "path": "/docs/flights/get-ticket-number"
          },
          {
            "label": "Get a booking",
            "path": "/docs/flights/get-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/get_order_quote": {
      "post": {
        "operationId": "getOrderQuote",
        "tags": [
          "Flights"
        ],
        "summary": "Get the current quote of an order",
        "description": "Get the current price of an order, its status and whether it can still be paid. The step is optional: call it to preview or refresh the price of a held order before `pay_order`.\n\nSend one of `order_id` (the supplier's order id, **preferred**), `booking_id` and `pnr`: the first one present wins. The FX-Port UUID is not accepted here. An Amadeus order is answered from the stored booking; a Duffel order is priced by the supplier. The order must be a booking of your agency in the environment of your key: an unknown order and the order of another agency answer the same `404 not_found`, before any supplier is asked.\n\n**Read `success`.** When the supplier refuses the quote for an order FX-Port knows (a Duffel order that no longer exists at the supplier, for example) the answer is not a `404`: it is HTTP `200` with `success: false` and the supplier's own `error`, as in the second example.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightOrderRequest"
              },
              "examples": {
                "by_order_id": {
                  "value": {
                    "order_id": "eJzTd9ePNA9wdA0FAAs8AmE"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The quote. `success: false` with `error` when the supplier refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightOrderQuote"
                },
                "examples": {
                  "held_order": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "469d8803",
                      "orderId": "eJzTd9ePNA9wdA0FAAs8AmE",
                      "processingTime": 0.134,
                      "orderQuote": {
                        "orderId": "eJzTd9ePNA9wdA0FAAs8AmE",
                        "bookingReference": "Y7PAEU",
                        "status": "awaiting_payment",
                        "pricing": {
                          "supplierCurrency": "DZD",
                          "agencyCurrency": "DZD",
                          "basePrice": 3160,
                          "taxesAndFees": 1765.44,
                          "agencyCommission": 4.93,
                          "finalPrice": 4930.37,
                          "b2bPrice": 4925.44,
                          "additionalServices": [],
                          "totalAmount": 4925.44
                        },
                        "paymentStatus": {
                          "awaitingPayment": true,
                          "paymentRequiredBy": null,
                          "priceGuaranteeExpiresAt": null,
                          "priceGuaranteed": true,
                          "canPay": true
                        },
                        "liveMode": false
                      },
                      "supplier": {
                        "id": "amadeus_aqc_dz_1",
                        "technology": "amadeus_aqc",
                        "name": "Amadeus Algeria"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "meta": {
                        "priceGuaranteed": true,
                        "paymentDeadline": null,
                        "canPay": true
                      }
                    }
                  },
                  "refused_by_the_supplier": {
                    "value": {
                      "environment": "sandbox",
                      "requestId": "0582790e",
                      "orderId": "ord_000000000000000000000X",
                      "processingTime": 0.085,
                      "error": {
                        "message": "The resource you are trying to access does not exist.",
                        "code": "not_found",
                        "type": "invalid_request_error",
                        "details": [
                          {
                            "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                            "title": "Not found",
                            "type": "invalid_request_error",
                            "message": "The resource you are trying to access does not exist.",
                            "code": "not_found"
                          }
                        ]
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "meta": {
                        "request_id": "0582790e",
                        "status": 200
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "supplier_error",
                          "title": "Not found",
                          "message": "The resource you are trying to access does not exist.",
                          "field": null,
                          "details": {
                            "supplier_type": "invalid_request_error",
                            "supplier_details": [
                              {
                                "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                                "title": "Not found",
                                "type": "invalid_request_error",
                                "message": "The resource you are trying to access does not exist.",
                                "code": "not_found"
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "request_id": "0582790e"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "None of `order_id`, `booking_id` and `pnr` was sent, or an id is not a valid token (letters, digits and `- _ . : = ~`, up to 128 characters).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameter: order_id (or booking_id / pnr)",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameter: order_id (or booking_id / pnr)",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "order_id",
                        "received": "Y7 PAEU/1"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "order_id",
                          "details": {
                            "received": "Y7 PAEU/1"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The order is not a booking of your agency in the environment of your key. An unknown order and the order of another agency are answered **identically**, before any supplier is asked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Booking not found for order_id: ZZZZZZ",
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "not_found",
                          "title": "Not found",
                          "message": "Booking not found for order_id: ZZZZZZ",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Get an order quote",
            "path": "/docs/flights/get-order-quote"
          },
          {
            "label": "Pay for a held order",
            "path": "/docs/flights/pay-order"
          },
          {
            "label": "Booking flows",
            "path": "/docs/flights/booking-flows"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/pay_order": {
      "post": {
        "operationId": "payOrder",
        "tags": [
          "Flights"
        ],
        "summary": "Pay a held order and issue the tickets",
        "description": "Pay for a held booking and issue the tickets. The agency balance is debited by `pricing.b2bPrice`; in the sandbox no money moves.\n\nThe amount and the currency come from the stored booking: do **not** send `currency` (`400 bad_request`); an `amount` is ignored. Send one of `order_id` (the supplier's order id, **preferred**), `booking_id` and `pnr`. `payment_type` is `balance` (default), `card` or `arc_bsp_cash`; only Duffel reads it, Amadeus Algeria pays from the balance whatever you send. A Duffel order that was bought instantly cannot be paid later.\n\n**A refusal of the supplier is HTTP `200` with `success: false` and `paymentSucceeded: false`**: the status alone is not enough, read both. `error.code` is a number as text for Amadeus (`4926`: the order is already issued; `9112`: the reservation is cancelled or expired) and a name for Duffel. Do not retry a payment blindly: read the booking with `get_booking` first. A second `pay_order` for an order that is paid answers `4926`, which is harmless. The exception is `503 balance_lock_timeout` (live only): another operation of your agency held the balance for a moment, the payment never started, and the same request can be sent again after a few seconds.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightPayOrderRequest"
              },
              "examples": {
                "by_order_id": {
                  "value": {
                    "order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
                    "payment_type": "balance"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The paid order. `success: false` with `paymentSucceeded: false` and `error` when the supplier refused the payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightOrder"
                },
                "examples": {
                  "paid": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "paymentSucceeded": true,
                      "requestId": "1ad06b56",
                      "orderId": "eJzTd9ePNA9wdA0FAAs8AmE",
                      "processingTime": 2.981,
                      "paymentDetails": {
                        "paymentId": null,
                        "status": "succeeded",
                        "type": "issuance",
                        "paymentAmount": 4925.44,
                        "currency": "DZD",
                        "createdAt": "2026-11-01T15:26:21.896668",
                        "message": "Payment successful! Tickets have been issued."
                      },
                      "booking": {
                        "reference": "eJzTd9ePNA9wdA0FAAs8AmE",
                        "status": "confirmed",
                        "pnr": "Y7PAEU",
                        "type": "instant",
                        "createdAt": "2026-11-01T15:26:21.896668",
                        "cancelledAt": null,
                        "content": "managed",
                        "syncedAt": "2026-11-01T15:26:21.896668",
                        "availableActions": [],
                        "voidableUntil": "2026-11-01T23:00:00Z",
                        "cancellation": null
                      },
                      "supplier": {
                        "id": "amadeus_aqc_dz_1",
                        "technology": "amadeus_aqc",
                        "name": "Amadeus AQC",
                        "offerId": "1"
                      },
                      "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": "paid",
                        "paidAt": "2026-11-01T15:26:21.896710",
                        "paymentRequiredBy": null,
                        "priceGuaranteeExpiresAt": null,
                        "awaitingPayment": false
                      },
                      "documents": [
                        {
                          "type": "electronic_ticket",
                          "documentType": "ETICKET",
                          "uniqueIdentifier": "124-2446977397",
                          "status": "ISSUED",
                          "passengerIds": [
                            "1"
                          ],
                          "segmentIds": [
                            "1"
                          ]
                        }
                      ],
                      "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": "124-2446977397"
                        }
                      ],
                      "travelerPricing": [
                        {
                          "travelerId": "1",
                          "travelerType": "ADULT",
                          "fareOption": "STANDARD",
                          "price": {
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD",
                            "base": 3160,
                            "total": 4930.37,
                            "b2bPrice": 4925.44
                          },
                          "fareDetailsBySegment": [
                            {
                              "segmentId": "1",
                              "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": "1",
                              "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": [],
                      "internalBooking": {
                        "bookingId": "Y7PAEU",
                        "internalId": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7"
                      },
                      "fareRules": {
                        "refund": {
                          "allowed": true
                        },
                        "exchange": {
                          "allowed": true
                        },
                        "agencyCurrency": "DZD"
                      },
                      "meta": {
                        "paymentStatus": "succeeded",
                        "orderConfirmed": true,
                        "supportHold": true,
                        "supportVoid": true,
                        "voidDeadline": "2026-11-01T23:00:00Z",
                        "voidWindowEndsAt": null,
                        "airlineInitiatedChanges": [],
                        "changes": [],
                        "users": [],
                        "orderMetadata": null
                      },
                      "postProcessing": {
                        "bucketLink": null,
                        "emailSent": false
                      }
                    }
                  },
                  "refused_already_issued": {
                    "value": {
                      "environment": "sandbox",
                      "paymentSucceeded": false,
                      "requestId": "a63285ae",
                      "orderId": "eJzTd9ePNPfw9vIFAAs8AmA",
                      "processingTime": 0.799,
                      "error": {
                        "message": "The flightOrder is already issued",
                        "code": "4926",
                        "type": "INVALID DATA RECEIVED",
                        "details": [
                          {
                            "status": 400,
                            "code": 4926,
                            "title": "INVALID DATA RECEIVED",
                            "detail": "The flightOrder is already issued"
                          }
                        ]
                      },
                      "supplier": {
                        "id": "amadeus_aqc_dz_1",
                        "technology": "amadeus_aqc",
                        "name": "Amadeus AQC"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "meta": {
                        "supportHold": true,
                        "supportVoid": true,
                        "voidDeadline": "2026-11-01T23:00:00Z",
                        "supplierStatusCode": 400,
                        "request_id": "a63285ae",
                        "status": 200
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "4926",
                          "type": "supplier_error",
                          "title": "Invalid data received",
                          "message": "The flightOrder is already issued",
                          "field": null,
                          "details": {
                            "supplier_type": "INVALID DATA RECEIVED",
                            "supplier_details": [
                              {
                                "status": 400,
                                "code": 4926,
                                "title": "INVALID DATA RECEIVED",
                                "detail": "The flightOrder is already issued"
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#4926"
                        }
                      ],
                      "request_id": "a63285ae"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "None of `order_id`, `booking_id` and `pnr` was sent, or an id is not a valid token (letters, digits and `- _ . : = ~`, up to 128 characters).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameter: order_id (or booking_id / pnr)",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameter: order_id (or booking_id / pnr)",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "order_id",
                        "received": "Y7 PAEU/1"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "order_id",
                          "details": {
                            "received": "Y7 PAEU/1"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "currency_sent": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Do not pass 'currency'. Currency is resolved automatically from booking/supplier context.",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Do not pass 'currency'. Currency is resolved automatically from booking/supplier context.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Live only: the balance of your agency is lower than what the booking debits (`pricing.b2bPrice`). The sandbox debits nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "insufficient_balance": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "insufficient_balance",
                        "message": "Agency balance is insufficient. Available: 120.0 DZD, Required: 29876.81 DZD.",
                        "details": {
                          "available": 120,
                          "required": 29876.81,
                          "currency": "DZD"
                        }
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "insufficient_balance",
                          "type": "payment_error",
                          "title": "Insufficient balance",
                          "message": "Agency balance is insufficient. Available: 120.0 DZD, Required: 29876.81 DZD.",
                          "field": null,
                          "details": {
                            "available": 120,
                            "required": 29876.81,
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#insufficient_balance"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 402
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency or the IP address is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The order is not a booking of your agency in the environment of your key. An unknown order and the order of another agency are answered **identically**, before any supplier is asked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Booking not found for order_id: ZZZZZZ",
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "not_found",
                          "title": "Not found",
                          "message": "Booking not found for order_id: ZZZZZZ",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live only: another operation of your agency holds the balance in the same currency for a moment. Nothing was paid: the supplier was not called. The message is always the same sentence and `errors[0].details.currency` is the currency of your balance. Send the same request again after a few seconds: no `Retry-After` header is sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "balance_lock_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "balance_lock_timeout",
                        "message": "The agency balance is busy with another operation. Retry in a few seconds.",
                        "currency": "DZD"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "balance_lock_timeout",
                          "type": "internal_error",
                          "title": "Balance lock timeout",
                          "message": "The agency balance is busy with another operation. Retry in a few seconds.",
                          "field": null,
                          "details": {
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#balance_lock_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 503
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Pay for a held order",
            "path": "/docs/flights/pay-order"
          },
          {
            "label": "Get an order quote",
            "path": "/docs/flights/get-order-quote"
          },
          {
            "label": "Booking flows",
            "path": "/docs/flights/booking-flows"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/cancel_order": {
      "post": {
        "operationId": "cancelOrder",
        "tags": [
          "Flights"
        ],
        "summary": "Cancel a held reservation",
        "description": "Cancel a **held (unpaid)** reservation: it is deleted at the supplier and the PNR is released. This is not a refund and it does not apply to issued tickets.\n\nOnly Amadeus Algeria and Travelport holds can be cancelled here; a Duffel order is answered `400`. Cancel a hold you abandon: the airline can refuse a new booking for the same passenger on the same route while the old reservation is pending. A booking that is not `awaiting_payment` (it is `confirmed` or already `cancelled`) is `409 invalid_booking_status`. To get money back for an issued Duffel order use `cancel_quote` then `confirm_cancel`; void, exchange and refund of an issued Amadeus Algeria ticket are manual (see Post-booking operations).\n\nSend one of `order_id` (preferred), `booking_id` and `pnr`. An unknown order and the order of another agency answer the same `404 not_found`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightOrderRequest"
              },
              "examples": {
                "by_order_id": {
                  "value": {
                    "order_id": "eJzTd9ePNA90dHUGAAsuAlA"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reservation was deleted and the booking is `cancelled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightCancelOrderResponse"
                },
                "examples": {
                  "hold_deleted": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "11487aa1",
                      "orderId": "eJzTd9ePNA90dHUGAAsuAlA",
                      "processingTime": 2.147,
                      "booking": {
                        "reference": "Y7QAEC",
                        "status": "cancelled",
                        "pnr": "Y7QAEC",
                        "type": "hold",
                        "createdAt": "2026-11-01T15:26:27.977426+00:00",
                        "cancelledAt": "2026-11-01T15:26:30.748257",
                        "content": "managed",
                        "syncedAt": "2026-11-01T15:26:30.748257",
                        "availableActions": [],
                        "voidableUntil": null,
                        "cancellation": {
                          "type": "void",
                          "deleted": true,
                          "deletedAt": "2026-11-01T15:26:30.748257"
                        }
                      },
                      "supplier": {
                        "id": "amadeus_aqc_dz_1",
                        "technology": "amadeus_aqc",
                        "name": "Amadeus Algeria",
                        "offerId": null
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "cancellation": {
                        "status": "deleted",
                        "deleted": true,
                        "deletedAt": "2026-11-01T15:26:30.748257",
                        "refundAmount": 0,
                        "refundCurrency": "DZD",
                        "refundTo": null,
                        "message": "Reservation deleted successfully."
                      },
                      "meta": {
                        "supportHold": true,
                        "supportVoid": true,
                        "voidDeadline": "2026-11-01T23:00:00Z",
                        "voidWindowEndsAt": null,
                        "airlineInitiatedChanges": [],
                        "changes": [],
                        "users": [],
                        "orderMetadata": {
                          "supplierStatusCode": 204,
                          "supplierDeleteErrorIgnored": false,
                          "supplierDeleteErrorCode": 204,
                          "supplierDeleteErrorMessage": null
                        }
                      },
                      "postProcessing": {
                        "bookingStatusUpdated": true
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "None of `order_id`, `booking_id` and `pnr` was sent, or an id is not a valid token (letters, digits and `- _ . : = ~`, up to 128 characters).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameter: order_id (or booking_id / pnr)",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameter: order_id (or booking_id / pnr)",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "order_id",
                        "received": "Y7 PAEU/1"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "order_id",
                          "details": {
                            "received": "Y7 PAEU/1"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "duffel_order": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Delete reservation is currently only supported for Amadeus Algeria and Travelport hold bookings. Received supplier_id: duffel_1",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Delete reservation is currently only supported for Amadeus Algeria and Travelport hold bookings. Received supplier_id: duffel_1",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency or the IP address is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The order is not a booking of your agency in the environment of your key. An unknown order and the order of another agency are answered **identically**, before any supplier is asked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Booking not found for order_id: ZZZZZZ",
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "not_found",
                          "title": "Not found",
                          "message": "Booking not found for order_id: ZZZZZZ",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The booking is not an unpaid hold: it is `confirmed` (paid) or already `cancelled`. Read it with `get_booking`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "invalid_booking_status": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_status",
                        "message": "Booking is 'confirmed', expected awaiting_payment"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_status",
                          "type": "conflict",
                          "title": "Invalid booking status",
                          "message": "Booking is 'confirmed', expected awaiting_payment",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_status"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 409
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The reservation was deleted but the FX-Port booking could not be updated. Read the booking; contact support with the `request_id` if it is still `awaiting_payment`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "booking_update_failed": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "booking_update_failed",
                        "message": "Reservation was deleted at the supplier, but local booking status could not be updated."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_update_failed",
                          "type": "internal_error",
                          "title": "Booking update failed",
                          "message": "Reservation was deleted at the supplier, but local booking status could not be updated.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_update_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 500
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Cancel a held reservation",
            "path": "/docs/flights/cancel-order"
          },
          {
            "label": "Post-booking operations",
            "path": "/docs/flights/post-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/cancel_quote": {
      "post": {
        "operationId": "cancelQuote",
        "tags": [
          "Flights"
        ],
        "summary": "Quote the refund of an issued order",
        "description": "Ask what cancelling an **issued Duffel order** would refund. The call creates a pending cancellation at the supplier with the refund amount; nothing is cancelled and no money moves until you confirm it with `confirm_cancel`.\n\nA quote is valid for one hour (`cancellation.expiresAt`) and the amount is the supplier's offer for that moment: ask again if you wait. Whether an order can be cancelled at all depends on the airline and the fare: many fares are refused with `order_not_cancellable`. Only Duffel orders are quoted; an order of another supplier is `400`. Held reservations are cancelled with `cancel_order`.\n\nSend one of `order_id` (the supplier's order id, `ord_...`), `booking_id` and `pnr`: FX-Port finds the booking of your agency and sends the supplier's own id to Duffel. An unknown order and the order of another agency answer the same `404 booking_not_found`. **Read `success`**: a refusal of the supplier is HTTP `200` with `success: false`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightCancelQuoteRequest"
              },
              "examples": {
                "by_order_id": {
                  "value": {
                    "order_id": "ord_0000BC0VDONwA54LrfoCkm"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The pending cancellation and its refund. Show the amount to your customer, then confirm with `cancellation.cancellationId`. `success: false` with `error` when the supplier refuses.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightCancelQuoteResponse"
                },
                "examples": {
                  "refund_quote": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "2cb63f69",
                      "orderId": "ord_0000BC0VDONwA54LrfoCkm",
                      "processingTime": 0.128,
                      "cancellation": {
                        "cancellationId": "ore_0000BC0VG9WBaFAZ83u3yy",
                        "orderId": "ord_0000BC0VDONwA54LrfoCkm",
                        "status": "pending",
                        "refund": {
                          "originalRefundCurrency": "USD",
                          "originalRefundAmount": 76.86,
                          "refundCurrency": "DZD",
                          "refundAmount": 19215,
                          "refundTo": "FX-Port Balance",
                          "refundToRaw": "balance",
                          "convertedSupplierRefundAmount": 19215,
                          "isFullBalanceRefund": false,
                          "kioskCommissionRefund": 0
                        },
                        "expiresAt": "2026-11-01T15:28:32Z",
                        "confirmedAt": null,
                        "createdAt": "2026-11-01T14:28:32.488592Z",
                        "airlineCredits": null,
                        "liveMode": false
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "exchangeRate": {
                        "rate": 250,
                        "marginPercent": 0,
                        "fromCurrency": "USD",
                        "toCurrency": "DZD"
                      },
                      "meta": {
                        "quoteValidUntil": "2026-11-01T15:28:32Z",
                        "isConfirmed": false,
                        "canConfirm": true
                      }
                    }
                  },
                  "order_not_cancellable": {
                    "value": {
                      "environment": "sandbox",
                      "requestId": "d0786d15",
                      "orderId": "ord_0000BC0VKb81MLN9OaFAhB",
                      "processingTime": 0.11,
                      "error": {
                        "message": "This order cannot be cancelled through the API.",
                        "code": "order_not_cancellable",
                        "type": "invalid_state_error",
                        "details": [
                          {
                            "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                            "title": "Order not cancellable",
                            "type": "invalid_state_error",
                            "message": "This order cannot be cancelled through the API.",
                            "code": "order_not_cancellable"
                          }
                        ]
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "meta": {
                        "request_id": "d0786d15",
                        "status": 200
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "order_not_cancellable",
                          "type": "supplier_error",
                          "title": "Order not cancellable",
                          "message": "This order cannot be cancelled through the API.",
                          "field": null,
                          "details": {
                            "supplier_type": "invalid_state_error",
                            "supplier_details": [
                              {
                                "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                                "title": "Order not cancellable",
                                "type": "invalid_state_error",
                                "message": "This order cannot be cancelled through the API.",
                                "code": "order_not_cancellable"
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#order_not_cancellable"
                        }
                      ],
                      "request_id": "d0786d15"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "None of `order_id`, `booking_id` and `pnr` was sent, or an id is not a valid token (letters, digits and `- _ . : = ~`, up to 128 characters).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameter: order_id (or booking_id / pnr)",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameter: order_id (or booking_id / pnr)",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "order_id",
                        "received": "Y7 PAEU/1"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`order_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "order_id",
                          "details": {
                            "received": "Y7 PAEU/1"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "supplier_id_not_duffel": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Cancellation quotes are currently only supported for Duffel. Received supplier_id: amadeus_aqc_dz_1",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Cancellation quotes are currently only supported for Duffel. Received supplier_id: amadeus_aqc_dz_1",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No booking of your agency in this environment has this reference. An unknown order and the order of another agency are answered the same way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "No booking found for 'ZZZZZZ'"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No booking found for 'ZZZZZZ'",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Quote a cancellation",
            "path": "/docs/flights/cancel-quote"
          },
          {
            "label": "Confirm a cancellation",
            "path": "/docs/flights/confirm-cancel"
          },
          {
            "label": "Post-booking operations",
            "path": "/docs/flights/post-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/confirm_cancel": {
      "post": {
        "operationId": "confirmCancel",
        "tags": [
          "Flights"
        ],
        "summary": "Confirm the cancellation of an order",
        "description": "Confirm the cancellation you quoted with `cancel_quote`: the Duffel order is cancelled at the airline and the refund is credited to your FX-Port balance. **This cannot be undone.**\n\nShow the amount of `cancel_quote` to your customer before you call this endpoint, and call it once: a second call answers `already_cancelled`. Send `cancellation_id` (the `ore_...` id of the quote) **and** `order_id` (or `booking_id` or `pnr`): the order id makes FX-Port check that the booking is yours and update the booking and the balance, so a call without it is `400 missing_field` and nothing is cancelled. An unknown order and the order of another agency answer the same `404 booking_not_found`. **Read `success`**: a refusal of the supplier is HTTP `200` with `success: false`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FlightConfirmCancelRequest"
              },
              "examples": {
                "confirm_the_quote": {
                  "value": {
                    "cancellation_id": "ore_0000BC0VG9WBaFAZ83u3yy",
                    "order_id": "ord_0000BC0VDONwA54LrfoCkm"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order is cancelled and the refund is on its way to your balance. `success: false` with `error` when the supplier refuses.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightConfirmCancelResponse"
                },
                "examples": {
                  "cancelled": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "64666d3c",
                      "orderId": "ord_0000BC0VDONwA54LrfoCkm",
                      "processingTime": 0.245,
                      "confirmation": {
                        "cancellationId": "ore_0000BC0VG9WBaFAZ83u3yy",
                        "orderId": "ord_0000BC0VDONwA54LrfoCkm",
                        "status": "confirmed",
                        "refund": {
                          "originalRefundCurrency": "USD",
                          "originalRefundAmount": 76.86,
                          "refundCurrency": "DZD",
                          "refundAmount": 19215,
                          "refundTo": "FX-Port Balance",
                          "refundToRaw": "balance",
                          "convertedSupplierRefundAmount": 19215,
                          "isFullBalanceRefund": false,
                          "kioskCommissionRefund": 0
                        },
                        "confirmedAt": "2026-11-01T14:30:04.851325Z",
                        "createdAt": "2026-11-01T14:28:32.488592Z",
                        "airlineCredits": null,
                        "liveMode": false,
                        "message": "Order cancellation confirmed. Refund will be processed."
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "agency": {
                        "currency": "DZD"
                      },
                      "exchangeRate": {
                        "rate": 250,
                        "marginPercent": 0,
                        "fromCurrency": "USD",
                        "toCurrency": "DZD"
                      },
                      "meta": {
                        "cancellationConfirmedAt": "2026-11-01T14:30:04.851325Z",
                        "refundProcessing": {
                          "method": "FX-Port Balance",
                          "estimatedDays": "Immediate",
                          "description": "Refund credited to your FX-Port balance"
                        }
                      },
                      "postProcessing": {
                        "refundMethod": "balance",
                        "emailSent": false
                      }
                    }
                  },
                  "already_cancelled": {
                    "value": {
                      "environment": "sandbox",
                      "requestId": "c2eb0461",
                      "orderId": "ord_0000BC0VDONwA54LrfoCkm",
                      "cancellationId": "ore_0000BC0VG9WBaFAZ83u3yy",
                      "processingTime": 0.126,
                      "error": {
                        "message": "This order has already been cancelled.",
                        "code": "already_cancelled",
                        "type": "invalid_state_error",
                        "details": [
                          {
                            "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                            "title": "Order already cancelled",
                            "type": "invalid_state_error",
                            "message": "This order has already been cancelled.",
                            "code": "already_cancelled"
                          }
                        ],
                        "userMessage": "This order has already been cancelled."
                      },
                      "supplier": {
                        "id": "duffel_1",
                        "technology": "duffel",
                        "name": "Duffel"
                      },
                      "meta": {
                        "request_id": "c2eb0461",
                        "status": 200
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "already_cancelled",
                          "type": "supplier_error",
                          "title": "Already cancelled",
                          "message": "This order has already been cancelled.",
                          "field": null,
                          "details": {
                            "supplier_type": "invalid_state_error",
                            "supplier_details": [
                              {
                                "documentation_url": "https://duffel.com/docs/api/overview/response-handling",
                                "title": "Order already cancelled",
                                "type": "invalid_state_error",
                                "message": "This order has already been cancelled.",
                                "code": "already_cancelled"
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#already_cancelled"
                        }
                      ],
                      "request_id": "c2eb0461"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`cancellation_id` is missing, `order_id` is missing, or an id is not a valid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "detail": "Missing required parameter: cancellation_id",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Missing required parameter: cancellation_id",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "missing_field",
                        "message": "`order_id` (or `booking_id` / `pnr`) is required together with `cancellation_id`.",
                        "field": "order_id"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "`order_id` (or `booking_id` / `pnr`) is required together with `cancellation_id`.",
                          "field": "order_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  },
                  "invalid_cancellation_id": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "invalid_cancellation_id",
                        "message": "`cancellation_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                        "field": "cancellation_id",
                        "received": "ore 1/2"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_cancellation_id",
                          "type": "validation_error",
                          "title": "Invalid cancellation id",
                          "message": "`cancellation_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": "cancellation_id",
                          "details": {
                            "received": "ore 1/2"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_cancellation_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 400
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency or the IP address is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`order_id` is not a booking of your agency in this environment. An unknown order and the order of another agency are answered the same way, and nothing is cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "No booking found for 'ZZZZZZ'"
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No booking found for 'ZZZZZZ'",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 404
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Confirm a cancellation",
            "path": "/docs/flights/confirm-cancel"
          },
          {
            "label": "Quote a cancellation",
            "path": "/docs/flights/cancel-quote"
          },
          {
            "label": "Post-booking operations",
            "path": "/docs/flights/post-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/get_acd_documents": {
      "post": {
        "operationId": "getAcdDocuments",
        "tags": [
          "Flights"
        ],
        "summary": "Get the ACD document state of a booking",
        "description": "Read what a booking needs for ACD, which travelers already have a passport on file, and whether you can upload. Call it before every upload and use its `traveler_id` values and `missing_traveler_ids`: do not build traveler ids yourself.\n\nA booking needs ACD passports when its fare has `pricing.acd.applied: true` (`requiresACD: true` on the offer and the booking): one passport copy for every traveler on the booking. `acd_required` of `get_booking` and `acd.acd_required` here say whether passports are still expected. The endpoint answers `200` for every booking of your agency, ACD or not: for a booking without ACD `acd_required` is `false`, `can_upload` is `false` and `blocked_reason` is `ACD_NOT_REQUIRED`. Travelers are listed either way. `ticket_number` of a traveler is `null` until the airline returns one (always for a hold).\n\n`booking_id` takes the PNR, the supplier order id or the FX-Port UUID (`order_id` and `pnr` are other names of the same field). Only bookings of your agency in the environment of your key are found: a booking of another agency, a booking of the other environment and an id that does not exist all answer the same `404 BOOKING_NOT_FOUND`, so an answer never tells whether a booking exists. The `download_url` of a document is a **public** address that needs no key: anybody who has it can open the passport, so treat it as sensitive.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetAcdDocumentsRequest"
              },
              "examples": {
                "partially_uploaded": {
                  "value": {
                    "booking_id": "Y7IA34"
                  }
                },
                "not_an_acd_booking": {
                  "value": {
                    "booking_id": "Y7HKJM"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The ACD state of the booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetAcdDocumentsResponse"
                },
                "examples": {
                  "three_travelers_one_uploaded": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "8201f6f6",
                      "booking_id": "Y7IA34",
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 1,
                        "missing_documents": 2,
                        "missing_traveler_ids": [
                          "1",
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 1,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      }
                    }
                  },
                  "acd_not_required": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "requestId": "8b5b5c52",
                      "booking_id": "Y7HKJM",
                      "acd": {
                        "booking_id": "Y7HKJM",
                        "acd_required": false,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": false,
                        "blocked_reason": "ACD_NOT_REQUIRED",
                        "required_documents": 0,
                        "uploaded_documents": 0,
                        "missing_documents": 0,
                        "missing_traveler_ids": [],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JOHN SMITH",
                            "traveler_type": "ADULT",
                            "ticket_number": "124-2446977395",
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 0,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 1
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`booking_id` is missing (`MISSING_BOOKING_ID`) or is not an identifier (`INVALID_BOOKING_ID`), or the body is not a JSON object (`INVALID_JSON`, `INVALID_BODY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "missing_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "requestId": "528f8a0b",
                      "error": "missing_booking_id",
                      "code": "MISSING_BOOKING_ID",
                      "message": "Missing required field: booking_id",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_booking_id",
                          "type": "validation_error",
                          "title": "Missing booking id",
                          "message": "Missing required field: booking_id",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "528f8a0b",
                        "status": 400
                      },
                      "request_id": "528f8a0b"
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "invalid_booking_id",
                      "code": "INVALID_BOOKING_ID",
                      "message": "`booking_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                      "field": "booking_id",
                      "received": "A B",
                      "requestId": "9d1f0a2c",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "`booking_id` is not a valid identifier (letters, digits and - _ . : = ~ only, up to 128 characters).",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "9d1f0a2c",
                        "status": 400
                      },
                      "request_id": "9d1f0a2c"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "None of your bookings in the environment of your key matches `booking_id`: the id is unknown, or the booking belongs to another agency or to the other environment (the three answer the same way).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "booking_not_found",
                      "code": "BOOKING_NOT_FOUND",
                      "message": "No flight booking found for 'ZZZZZZ'.",
                      "requestId": "e802c2aa",
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No flight booking found for 'ZZZZZZ'.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "e802c2aa",
                        "status": 404
                      },
                      "request_id": "e802c2aa"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Upload ACD documents",
            "path": "/docs/flights/acd-documents"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/flights/upload_acd_documents": {
      "post": {
        "operationId": "uploadAcdDocuments",
        "tags": [
          "Flights"
        ],
        "summary": "Upload ACD passport documents",
        "description": "Add or replace the passport copies of a booking that needs ACD: one file for each traveler, sent as `multipart/form-data` (not JSON).\n\n**The form.** `booking_id` is the PNR, the supplier order id or the FX-Port UUID of a booking of your agency (any other booking, also one of another agency, answers `404 BOOKING_NOT_FOUND`, like an unknown id). `traveler_ids` and `documents` are matched **by order**: the first id goes with the first file. Send `traveler_ids` once for each file, or as one comma-separated value (`1,2`), or as a JSON array as text (`[\"1\",\"2\"]`); the `documents` field is repeated, one file each. `replace=true` overwrites a traveler who already has a passport; without it that traveler is refused (`DOCUMENT_ALREADY_UPLOADED`) and nothing changes. The older names `bookingId`, `orderId`, `travelerIds`, `files`, `file` and `document` are still read.\n\n**The files.** A PDF or an image (JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000, JPEG XL), recognised **by content** (the extension and the `Content-Type` you send are not trusted). At most 10 MiB per file and 25 MiB per request, and no more files than travelers. Several files with problems are all reported in `results[]`: a request with any bad file stores nothing.\n\n**Reading the answer.** `200` means every file was saved, `207` means only some were (`status: partial_success`; read `results[]`), `4xx` means nothing changed. A `5xx` after the files were stored cannot be told from the outside: call `get_acd_documents` before you retry. A retry without `replace` is safe; with `replace=true` it replaces the file again.\n\n`notification` says whether the ACD team was told: in the sandbox it is never sent (`skipped: true`, `reason: sandbox_environment`). A hold accepts uploads (`reason: hold_booking`). The `download_url` links are public: treat them as sensitive. In the sandbox, a booking of `CDG` to `ALG` on `amadeus_aqc_dz_1` is an ACD booking: use fake files (a PDF or PNG a few bytes long), never a real document.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AcdUploadRequest"
              },
              "examples": {
                "one_traveler": {
                  "value": {
                    "booking_id": "Y7HWMD",
                    "traveler_ids": "1",
                    "documents": [
                      "passport.pdf"
                    ]
                  }
                },
                "replace_a_passport": {
                  "value": {
                    "booking_id": "Y7IA34",
                    "traveler_ids": "2",
                    "documents": [
                      "passport-2.png"
                    ],
                    "replace": "true"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every file was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadAcdDocumentsResponse"
                },
                "examples": {
                  "all_saved": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "status": "success",
                      "requestId": "58bfcf4a",
                      "booking_id": "Y7IA34",
                      "message": "2 documents saved.",
                      "summary": {
                        "received": 2,
                        "uploaded": 2,
                        "replaced": 0,
                        "failed": 0
                      },
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "1",
                          "traveler_name": "JANE DOE",
                          "filename": "passport-1.png",
                          "status": "uploaded",
                          "content_type": "image/png",
                          "size_bytes": 69,
                          "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/1/20261101142620_28e115c8.png"
                        },
                        {
                          "index": 1,
                          "traveler_id": "3",
                          "traveler_name": "JAMES DOE",
                          "filename": "passport-3.pdf",
                          "status": "uploaded",
                          "content_type": "application/pdf",
                          "size_bytes": 191,
                          "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/3/20261101142620_4194315e.pdf"
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": true,
                        "acd_paid": false,
                        "complete": true,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 3,
                        "missing_documents": 0,
                        "missing_traveler_ids": [],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 2,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "notification": {
                        "sent": false,
                        "type": "submitted",
                        "skipped": true,
                        "reason": "sandbox_environment"
                      },
                      "warnings": []
                    }
                  }
                }
              }
            }
          },
          "207": {
            "description": "Some files were saved and others failed. `results[]` says which, with a code for each; send the failed travelers again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadAcdDocumentsResponse"
                },
                "examples": {
                  "partial_success": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "status": "partial_success",
                      "requestId": "3d7a91c0",
                      "booking_id": "Y7IA34",
                      "message": "1 of 2 documents saved; 1 failed — retry the failed travelers.",
                      "summary": {
                        "received": 2,
                        "uploaded": 1,
                        "replaced": 0,
                        "failed": 1
                      },
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "1",
                          "traveler_name": "JANE DOE",
                          "filename": "passport-1.png",
                          "status": "uploaded",
                          "content_type": "image/png",
                          "size_bytes": 69,
                          "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/1/20261101142620_28e115c8.png"
                        },
                        {
                          "index": 1,
                          "traveler_id": "3",
                          "traveler_name": "JAMES DOE",
                          "filename": "passport-3.pdf",
                          "status": "failed",
                          "error": {
                            "code": "STORAGE_UPLOAD_FAILED",
                            "message": "The document could not be stored. Retry this traveler.",
                            "reason": "storage_error"
                          }
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 2,
                        "missing_documents": 1,
                        "missing_traveler_ids": [
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 2,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "notification": {
                        "sent": false,
                        "skipped": true,
                        "reason": "awaiting_remaining_documents"
                      },
                      "warnings": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The form is wrong: a field is missing (`MISSING_BOOKING_ID`, `MISSING_TRAVELER_IDS`, `MISSING_DOCUMENTS`), the ids and the files do not match (`DOCUMENT_COUNT_MISMATCH`, `DUPLICATE_TRAVELER_ID`, `TOO_MANY_DOCUMENTS`, `UNKNOWN_TRAVELER_ID`), a file is empty (`EMPTY_FILE`), `replace` is not a boolean (`INVALID_REPLACE_FLAG`), the multipart body cannot be read (`INVALID_MULTIPART`), or several files have different problems (`VALIDATION_FAILED`). Nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "document_count_mismatch": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "document_count_mismatch",
                      "code": "DOCUMENT_COUNT_MISMATCH",
                      "message": "traveler_ids and documents must have the same number of entries (they are matched by order).",
                      "received_traveler_ids": 2,
                      "received_documents": 1,
                      "requestId": "3368e4c6",
                      "success": false,
                      "errors": [
                        {
                          "code": "document_count_mismatch",
                          "type": "validation_error",
                          "title": "Document count mismatch",
                          "message": "traveler_ids and documents must have the same number of entries (they are matched by order).",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#document_count_mismatch"
                        }
                      ],
                      "meta": {
                        "request_id": "3368e4c6",
                        "status": 400
                      },
                      "request_id": "3368e4c6"
                    }
                  },
                  "validation_failed": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "validation_failed",
                      "code": "VALIDATION_FAILED",
                      "message": "2 of 2 documents were rejected. Nothing was uploaded.",
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "1",
                          "traveler_name": "JANE DOE",
                          "filename": "bad.pdf",
                          "status": "failed",
                          "error": {
                            "code": "UNSUPPORTED_FILE_TYPE",
                            "message": "'bad.pdf' is not a supported passport document. Upload a PDF or a JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000 or JPEG XL image.",
                            "accepted_extensions": [
                              "avif",
                              "bmp",
                              "dib",
                              "gif",
                              "heic",
                              "heif",
                              "j2k",
                              "jfif",
                              "jp2",
                              "jpeg",
                              "jpg",
                              "jpx",
                              "jxl",
                              "pdf",
                              "pjp",
                              "pjpeg",
                              "png",
                              "tif",
                              "tiff",
                              "webp"
                            ]
                          }
                        },
                        {
                          "index": 1,
                          "traveler_id": "2",
                          "traveler_name": "JOHN DOE",
                          "filename": "empty.pdf",
                          "status": "failed",
                          "error": {
                            "code": "EMPTY_FILE",
                            "message": "Uploaded document 'empty.pdf' is empty."
                          }
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 1,
                        "missing_documents": 2,
                        "missing_traveler_ids": [
                          "1",
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 1,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "requestId": "34d61957",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_failed",
                          "type": "validation_error",
                          "title": "Validation failed",
                          "message": "2 of 2 documents were rejected. Nothing was uploaded.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#validation_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "34d61957",
                        "status": 400
                      },
                      "request_id": "34d61957"
                    }
                  },
                  "missing_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "requestId": "528f8a0b",
                      "error": "missing_booking_id",
                      "code": "MISSING_BOOKING_ID",
                      "message": "Missing required form field: booking_id",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_booking_id",
                          "type": "validation_error",
                          "title": "Missing booking id",
                          "message": "Missing required form field: booking_id",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "528f8a0b",
                        "status": 400
                      },
                      "request_id": "528f8a0b"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only (`read_write_permission_required`). A booking of another agency is never a `403`: it answers `404 BOOKING_NOT_FOUND`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightError"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      },
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_8f3a61c2d9e4",
                        "status": 403
                      },
                      "request_id": "req_8f3a61c2d9e4"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "None of your bookings in the environment of your key matches `booking_id`: the id is unknown, or the booking belongs to another agency or to the other environment (the three answer the same way).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "booking_not_found",
                      "code": "BOOKING_NOT_FOUND",
                      "message": "No flight booking found for 'ZZZZZZ'.",
                      "requestId": "e802c2aa",
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No flight booking found for 'ZZZZZZ'.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "e802c2aa",
                        "status": 404
                      },
                      "request_id": "e802c2aa"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Nothing was changed: the traveler already has a passport (`DOCUMENT_ALREADY_UPLOADED`: send `replace=true`), the booking does not need ACD documents (`ACD_NOT_REQUIRED`, `ACD_WAIVED`), it is cancelled or failed (`BOOKING_NOT_ELIGIBLE`), it has no traveler data (`TRAVELERS_NOT_AVAILABLE`), or another request changed the same booking at the same moment (`CONCURRENT_UPDATE`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "document_already_uploaded": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "document_already_uploaded",
                      "code": "DOCUMENT_ALREADY_UPLOADED",
                      "message": "A passport is already uploaded for traveler '2'. Send replace=true to replace it.",
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "2",
                          "traveler_name": "JOHN DOE",
                          "filename": "again.pdf",
                          "status": "failed",
                          "error": {
                            "code": "DOCUMENT_ALREADY_UPLOADED",
                            "message": "A passport is already uploaded for traveler '2'. Send replace=true to replace it."
                          }
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 1,
                        "missing_documents": 2,
                        "missing_traveler_ids": [
                          "1",
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 1,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "requestId": "4c54bcef",
                      "success": false,
                      "errors": [
                        {
                          "code": "document_already_uploaded",
                          "type": "conflict",
                          "title": "Document already uploaded",
                          "message": "A passport is already uploaded for traveler '2'. Send replace=true to replace it.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#document_already_uploaded"
                        }
                      ],
                      "meta": {
                        "request_id": "4c54bcef",
                        "status": 409
                      },
                      "request_id": "4c54bcef"
                    }
                  },
                  "acd_not_required": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "acd_not_required",
                      "code": "ACD_NOT_REQUIRED",
                      "message": "This booking does not require ACD documents.",
                      "acd": {
                        "booking_id": "Y7HKJM",
                        "acd_required": false,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": false,
                        "blocked_reason": "ACD_NOT_REQUIRED",
                        "required_documents": 0,
                        "uploaded_documents": 0,
                        "missing_documents": 0,
                        "missing_traveler_ids": [],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JOHN SMITH",
                            "traveler_type": "ADULT",
                            "ticket_number": "124-2446977395",
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 0,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 1
                        }
                      },
                      "requestId": "5b73272a",
                      "success": false,
                      "errors": [
                        {
                          "code": "acd_not_required",
                          "type": "conflict",
                          "title": "Acd not required",
                          "message": "This booking does not require ACD documents.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#acd_not_required"
                        }
                      ],
                      "meta": {
                        "request_id": "5b73272a",
                        "status": 409
                      },
                      "request_id": "5b73272a"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "A file is over 10 MiB (`FILE_TOO_LARGE`), or the files together are over 25 MiB (`REQUEST_TOO_LARGE`, with `max_request_size_bytes`). The platform refuses a whole body over 26 MiB with `request_too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "file_too_large": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "file_too_large",
                      "code": "FILE_TOO_LARGE",
                      "message": "Uploaded document 'big.pdf' exceeds the 10 MB limit.",
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "2",
                          "traveler_name": "JOHN DOE",
                          "filename": "big.pdf",
                          "status": "failed",
                          "error": {
                            "code": "FILE_TOO_LARGE",
                            "message": "Uploaded document 'big.pdf' exceeds the 10 MB limit.",
                            "max_file_size_bytes": 10485760
                          }
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 1,
                        "missing_documents": 2,
                        "missing_traveler_ids": [
                          "1",
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 1,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "requestId": "740b70d0",
                      "success": false,
                      "errors": [
                        {
                          "code": "file_too_large",
                          "type": "validation_error",
                          "title": "File too large",
                          "message": "Uploaded document 'big.pdf' exceeds the 10 MB limit.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#file_too_large"
                        }
                      ],
                      "meta": {
                        "request_id": "740b70d0",
                        "status": 413
                      },
                      "request_id": "740b70d0"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The content of a file is not a PDF or a supported image (`UNSUPPORTED_FILE_TYPE`), or the request is not `multipart/form-data` (`UNSUPPORTED_MEDIA_TYPE`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "unsupported_file_type": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "unsupported_file_type",
                      "code": "UNSUPPORTED_FILE_TYPE",
                      "message": "'bad.pdf' is not a supported passport document. Upload a PDF or a JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000 or JPEG XL image.",
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "2",
                          "traveler_name": "JOHN DOE",
                          "filename": "bad.pdf",
                          "status": "failed",
                          "error": {
                            "code": "UNSUPPORTED_FILE_TYPE",
                            "message": "'bad.pdf' is not a supported passport document. Upload a PDF or a JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000 or JPEG XL image.",
                            "accepted_extensions": [
                              "avif",
                              "bmp",
                              "dib",
                              "gif",
                              "heic",
                              "heif",
                              "j2k",
                              "jfif",
                              "jp2",
                              "jpeg",
                              "jpg",
                              "jpx",
                              "jxl",
                              "pdf",
                              "pjp",
                              "pjpeg",
                              "png",
                              "tif",
                              "tiff",
                              "webp"
                            ]
                          }
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 1,
                        "missing_documents": 2,
                        "missing_traveler_ids": [
                          "1",
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 1,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "requestId": "6f0d3b19",
                      "success": false,
                      "errors": [
                        {
                          "code": "unsupported_file_type",
                          "type": "validation_error",
                          "title": "Unsupported file type",
                          "message": "'bad.pdf' is not a supported passport document. Upload a PDF or a JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000 or JPEG XL image.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#unsupported_file_type"
                        }
                      ],
                      "meta": {
                        "request_id": "6f0d3b19",
                        "status": 415
                      },
                      "request_id": "6f0d3b19"
                    }
                  },
                  "unsupported_media_type": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "unsupported_media_type",
                      "code": "UNSUPPORTED_MEDIA_TYPE",
                      "message": "Send multipart/form-data with the fields booking_id, traveler_ids and documents.",
                      "requestId": "1c8e5a07",
                      "success": false,
                      "errors": [
                        {
                          "code": "unsupported_media_type",
                          "type": "validation_error",
                          "title": "Unsupported media type",
                          "message": "Send multipart/form-data with the fields booking_id, traveler_ids and documents.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#unsupported_media_type"
                        }
                      ],
                      "meta": {
                        "request_id": "1c8e5a07",
                        "status": 415
                      },
                      "request_id": "1c8e5a07"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "No file could be stored. Nothing changed: retry the request. Every `results[].error` carries the fixed `reason: storage_error`; the storage's own message is never returned. Cloudflare can replace the body of a `502` by its own HTML page: treat any `502` of this endpoint the same way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "storage_upload_failed": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "storage_upload_failed",
                      "code": "STORAGE_UPLOAD_FAILED",
                      "message": "None of the documents could be stored. Nothing was changed. Please retry.",
                      "results": [
                        {
                          "index": 0,
                          "traveler_id": "1",
                          "traveler_name": "JANE DOE",
                          "filename": "passport-1.png",
                          "status": "failed",
                          "error": {
                            "code": "STORAGE_UPLOAD_FAILED",
                            "message": "The document could not be stored. Retry this traveler.",
                            "reason": "storage_error"
                          }
                        }
                      ],
                      "acd": {
                        "booking_id": "Y7IA34",
                        "acd_required": true,
                        "acd_waived": false,
                        "acd_submitted": false,
                        "acd_paid": false,
                        "complete": false,
                        "can_upload": true,
                        "blocked_reason": null,
                        "required_documents": 3,
                        "uploaded_documents": 1,
                        "missing_documents": 2,
                        "missing_traveler_ids": [
                          "1",
                          "3"
                        ],
                        "travelers": [
                          {
                            "traveler_id": "1",
                            "traveler_name": "JANE DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          },
                          {
                            "traveler_id": "2",
                            "traveler_name": "JOHN DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "uploaded",
                            "document": {
                              "filename": "john-passport.pdf",
                              "content_type": "application/pdf",
                              "size_bytes": 482113,
                              "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                              "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                              "replaced_count": 0
                            }
                          },
                          {
                            "traveler_id": "3",
                            "traveler_name": "JAMES DOE",
                            "traveler_type": "ADULT",
                            "ticket_number": null,
                            "status": "missing",
                            "document": null
                          }
                        ],
                        "unassigned_documents": [],
                        "revision": 1,
                        "limits": {
                          "accepted_formats": [
                            "avif",
                            "bmp",
                            "gif",
                            "heif",
                            "jp2",
                            "jpeg",
                            "jxl",
                            "pdf",
                            "png",
                            "tiff",
                            "webp"
                          ],
                          "accepted_extensions": [
                            "avif",
                            "bmp",
                            "dib",
                            "gif",
                            "heic",
                            "heif",
                            "j2k",
                            "jfif",
                            "jp2",
                            "jpeg",
                            "jpg",
                            "jpx",
                            "jxl",
                            "pdf",
                            "pjp",
                            "pjpeg",
                            "png",
                            "tif",
                            "tiff",
                            "webp"
                          ],
                          "max_file_size_bytes": 10485760,
                          "max_request_size_bytes": 26214400,
                          "max_documents_per_request": 3
                        }
                      },
                      "requestId": "5e1d9c43",
                      "success": false,
                      "errors": [
                        {
                          "code": "storage_upload_failed",
                          "type": "internal_error",
                          "title": "Storage upload failed",
                          "message": "None of the documents could be stored. Nothing was changed. Please retry.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#storage_upload_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "5e1d9c43",
                        "status": 502
                      },
                      "request_id": "5e1d9c43"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Document storage is not available. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcdError"
                },
                "examples": {
                  "storage_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "status": "failed",
                      "error": "storage_unavailable",
                      "code": "STORAGE_UNAVAILABLE",
                      "message": "Document storage is temporarily unavailable. Please retry.",
                      "requestId": "77a3b0f2",
                      "success": false,
                      "errors": [
                        {
                          "code": "storage_unavailable",
                          "type": "internal_error",
                          "title": "Storage unavailable",
                          "message": "Document storage is temporarily unavailable. Please retry.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#storage_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "77a3b0f2",
                        "status": 503
                      },
                      "request_id": "77a3b0f2"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Upload ACD documents",
            "path": "/docs/flights/acd-documents"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/locations/search": {
      "get": {
        "operationId": "searchHotelLocations",
        "tags": [
          "Hotels"
        ],
        "summary": "Search destinations and get coordinates",
        "description": "Turns the text a user typed (a city, an area, an airport, a hotel) into places with coordinates. Pass the `latitude` and `longitude` of the place the user picks to `search_advanced`, with a `radius`. The results mix cities, airports and hotels of a location provider: the `id` is the provider's, not a FX-Port hotel id, and `type` is always `location` (the kind of place is `raw.loctype`). Places without coordinates are dropped. The answer is the same in the sandbox and on live. Cache the answers for a user's session and debounce typeahead calls (about 300 ms).",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "The text to look for: a city, an area, an airport or a hotel name, 1 to 64 characters once spaces and control characters are cleaned.",
            "schema": {
              "type": "string",
              "description": "The text to look for."
            },
            "example": "Tegucigalpa"
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Language of the names: `en`, `fr`, `ar`, `es`, `de`, `it`, `ru`, `tr`, `ja`, `ko` ... Written `ll` or `ll-RR` (`es-AR`); any other value answers in English.",
            "schema": {
              "type": "string",
              "description": "A language code.",
              "default": "en"
            },
            "example": "en"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Most places to return, 1 to 50.",
            "schema": {
              "type": "integer",
              "description": "Most places to return.",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            },
            "example": 3
          }
        ],
        "responses": {
          "200": {
            "description": "The places that match, best first. `locations` is empty when nothing matches.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelLocationsResponse"
                },
                "examples": {
                  "tegucigalpa": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "query": "Tegucigalpa",
                      "resultsCount": 2,
                      "locations": [
                        {
                          "id": "20119",
                          "name": "Tegucigalpa",
                          "displayName": "Tegucigalpa, Honduras",
                          "city": "Tegucigalpa",
                          "countryCode": "HN",
                          "latitude": 14.094204124743296,
                          "longitude": -87.19410705810549,
                          "type": "location",
                          "raw": {
                            "id": "20119",
                            "displayname": "Tegucigalpa, Honduras",
                            "loctype": "city",
                            "cid": 107,
                            "lat": 14.094204124743296,
                            "lng": -87.19410705810549,
                            "cc": "HN",
                            "country": "Honduras",
                            "timezone": "America/Tegucigalpa",
                            "utc": "-06:00",
                            "ap": "TGU"
                          }
                        },
                        {
                          "id": "TGU",
                          "name": "Toncontin",
                          "displayName": "Toncontin, Tegucigalpa, Honduras, (TGU)",
                          "city": "Tegucigalpa",
                          "countryCode": "HN",
                          "latitude": 14.06088,
                          "longitude": -87.2172,
                          "type": "location",
                          "raw": {
                            "id": "TGU",
                            "displayname": "Toncontin, Tegucigalpa, Honduras, (TGU)",
                            "loctype": "ap",
                            "cid": 107,
                            "lat": 14.06088,
                            "lng": -87.2172,
                            "cc": "HN",
                            "country": "Honduras",
                            "airportname": "Toncontin",
                            "ap": "TGU"
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`query` is missing, blank or longer than 64 characters (`invalid_query` when it is blank or too long), or `limit` is outside 1 to 50.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation_error": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Input should be less than or equal to 50",
                          "field": "limit",
                          "details": {
                            "type": "less_than_equal",
                            "limit": 50
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "validation_error",
                        "message": "Input should be less than or equal to 50",
                        "field": "limit"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Search locations",
            "path": "/docs/hotels/locations"
          }
        ],
        "x-sample-query": "query=Tegucigalpa&limit=3",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/name-search": {
      "get": {
        "operationId": "getHotelNameSearch",
        "tags": [
          "Hotels"
        ],
        "summary": "Find a hotel by name",
        "description": "Fuzzy search over the names of every hotel FX-Port knows. It tolerates typos, partial names and accents. Each hit carries the FX-Port hotel id to send as `hotel_id` to `get_rates` and `search_advanced`. Hits with a `match_score` below about 80 are often other hotels (a search for `Sofitel Algiers` also finds a hotel called `Algiers` in Las Vegas): show the score or keep the best few. The ids are the same in the sandbox and on live, and a hit is not necessarily bookable in the sandbox. `POST /hotels/search-by-name` takes the same fields as a JSON body.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "The hotel name or a part of it, 2 to 100 characters.",
            "schema": {
              "type": "string",
              "description": "The name to look for."
            },
            "example": "Sofitel Algiers"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Most hits to return, 1 to 100. A number outside that range, or text that is not a number, answers `400 invalid_limit`.",
            "schema": {
              "type": "integer",
              "description": "Most hits to return.",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "example": 3
          },
          {
            "name": "min_score",
            "in": "query",
            "required": false,
            "description": "Lowest match score to return, 0 to 100. A number outside that range, or text that is not a number, answers `400 invalid_min_score`.",
            "schema": {
              "type": "number",
              "description": "The lowest score.",
              "minimum": 0,
              "maximum": 100,
              "default": 60
            },
            "example": 60
          },
          {
            "name": "include_location",
            "in": "query",
            "required": false,
            "description": "Add the address, city, country code and postal code to each hit.",
            "schema": {
              "type": "boolean",
              "description": "Add the location.",
              "default": true
            }
          },
          {
            "name": "include_details",
            "in": "query",
            "required": false,
            "description": "Return the whole cached hotel document of each hit (about 26 KB each) instead of the summary.",
            "schema": {
              "type": "boolean",
              "description": "Return full documents.",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The hotels that match, best match first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelNameSearchResponse"
                },
                "examples": {
                  "sofitel": {
                    "value": {
                      "environment": "sandbox",
                      "query": "Sofitel Algiers",
                      "results_count": 2,
                      "search_time": "0.060s",
                      "hotels": [
                        {
                          "id": "sofitel_algiers_hamma_garden",
                          "name": "Sofitel Algiers Hamma Garden",
                          "match_score": 83.39,
                          "match_type": "partial",
                          "address": "172, rue Hassiba Benbouali, Algiers",
                          "city": "Algiers",
                          "country_code": "DZ",
                          "postal_code": "16014"
                        },
                        {
                          "id": "algiers_hotel",
                          "name": "Algiers",
                          "match_score": 76.67,
                          "match_type": "partial",
                          "address": "2845 South Las Vegas Boulevard Las Vegas, South Las Vegas Boulevard, Las Vegas",
                          "city": "Las Vegas",
                          "country_code": "US",
                          "postal_code": "89109"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`query` is blank, or has fewer than 2 or more than 100 characters, or `limit` or `min_score` is not a number in its range. Nothing is capped on this form: a `limit` above 100 is refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_query": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_query",
                          "type": "validation_error",
                          "title": "Invalid query",
                          "message": "'query' must be 2 to 100 characters long.",
                          "field": "query",
                          "details": {
                            "min_length": 2,
                            "max_length": 100
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_query"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_query",
                        "message": "'query' must be 2 to 100 characters long.",
                        "field": "query"
                      }
                    }
                  },
                  "invalid_limit": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_limit",
                          "type": "validation_error",
                          "title": "Invalid limit",
                          "message": "'limit' must be at most 100.",
                          "field": "limit",
                          "details": {
                            "min": 1,
                            "max": 100
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_limit"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_limit",
                        "message": "'limit' must be at most 100.",
                        "field": "limit"
                      }
                    }
                  },
                  "invalid_min_score": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_min_score",
                          "type": "validation_error",
                          "title": "Invalid min score",
                          "message": "'min_score' must be a number between 0 and 100.",
                          "field": "min_score",
                          "details": {
                            "min": 0,
                            "max": 100
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_min_score"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_min_score",
                        "message": "'min_score' must be a number between 0 and 100.",
                        "field": "min_score"
                      }
                    }
                  },
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Query cannot be empty",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`query` is missing, or `include_location` or `include_details` is not a boolean.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation_error": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Field required",
                          "field": "query",
                          "details": {
                            "type": "missing"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The name search is busy. Retry after the seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_busy",
                          "type": "internal_error",
                          "title": "Service busy",
                          "message": "Hotel name search is busy right now. Retry in a couple of seconds.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#service_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_busy",
                        "message": "Hotel name search is busy right now. Retry in a couple of seconds."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Hotel name search",
            "path": "/docs/hotels/name-search"
          }
        ],
        "x-sample-query": "query=Sofitel%20Algiers&limit=3",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/search-by-name": {
      "post": {
        "operationId": "postHotelNameSearch",
        "tags": [
          "Hotels"
        ],
        "summary": "Find a hotel by name (JSON body)",
        "description": "The same search as `GET /hotels/name-search`, with the parameters in a JSON body. Use whichever your client prefers. A body error answers `400`: `missing_field` for a missing `query`, `invalid_query` for the text, `invalid_limit` and `invalid_min_score` for the numbers, and `bad_request` with a message that names the field for the two booleans; the GET form answers `422` for a query parameter that is missing. A `limit` above 100 is capped at 100 and the answer carries a `coerced_value` warning; the GET form refuses it with `invalid_limit`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelNameSearchRequest"
              },
              "examples": {
                "sofitel": {
                  "value": {
                    "query": "Sofitel Algiers",
                    "limit": 3,
                    "min_score": 60,
                    "include_location": true,
                    "include_details": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The hotels that match, best match first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelNameSearchResponse"
                },
                "examples": {
                  "sofitel": {
                    "value": {
                      "environment": "sandbox",
                      "query": "Sofitel Algiers",
                      "results_count": 2,
                      "search_time": "0.060s",
                      "hotels": [
                        {
                          "id": "sofitel_algiers_hamma_garden",
                          "name": "Sofitel Algiers Hamma Garden",
                          "match_score": 83.39,
                          "match_type": "partial",
                          "address": "172, rue Hassiba Benbouali, Algiers",
                          "city": "Algiers",
                          "country_code": "DZ",
                          "postal_code": "16014"
                        },
                        {
                          "id": "algiers_hotel",
                          "name": "Algiers",
                          "match_score": 76.67,
                          "match_type": "partial",
                          "address": "2845 South Las Vegas Boulevard Las Vegas, South Las Vegas Boulevard, Las Vegas",
                          "city": "Las Vegas",
                          "country_code": "US",
                          "postal_code": "89109"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`query` is missing, blank, or has fewer than 2 or more than 100 characters, or `limit`, `min_score`, `include_details` or `include_location` has the wrong type or range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required field: 'query'",
                          "field": "query",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required field: 'query'",
                        "field": "query"
                      }
                    }
                  },
                  "invalid_query": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_query",
                          "type": "validation_error",
                          "title": "Invalid query",
                          "message": "'query' must be 2 to 100 characters long.",
                          "field": "query",
                          "details": {
                            "min_length": 2,
                            "max_length": 100
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_query"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_query",
                        "message": "'query' must be 2 to 100 characters long.",
                        "field": "query"
                      }
                    }
                  },
                  "invalid_limit": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_limit",
                          "type": "validation_error",
                          "title": "Invalid limit",
                          "message": "'limit' must be a positive integer.",
                          "field": "limit",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_limit"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_limit",
                        "message": "'limit' must be a positive integer.",
                        "field": "limit"
                      }
                    }
                  },
                  "invalid_min_score": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_min_score",
                          "type": "validation_error",
                          "title": "Invalid min score",
                          "message": "'min_score' must be a number between 0 and 100.",
                          "field": "min_score",
                          "details": {
                            "min": 0,
                            "max": 100
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_min_score"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_min_score",
                        "message": "'min_score' must be a number between 0 and 100.",
                        "field": "min_score"
                      }
                    }
                  },
                  "bad_request": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "'include_details' must be a boolean",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The name search is busy. Retry after the seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_busy",
                          "type": "internal_error",
                          "title": "Service busy",
                          "message": "Hotel name search is busy right now. Retry in a couple of seconds.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#service_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_busy",
                        "message": "Hotel name search is busy right now. Retry in a couple of seconds."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Hotel name search",
            "path": "/docs/hotels/name-search"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/search_advanced": {
      "post": {
        "operationId": "searchHotels",
        "tags": [
          "Hotels"
        ],
        "summary": "Geo search: hotels around a point",
        "description": "Geo search: finds and prices the hotels around a point for a stay. Send `latitude`, `longitude` and `radius` (meters, required) with the dates, the guests and the residency; the coordinates of a place come from the location search. The same call searches one hotel when you send `hotel_id` instead. The answer has two parts that you join by hotel id: `hotels_details[]` holds the MINI card of each hotel (identity, place, photos, amenities, no rates) and `hotels_pricing` holds the price of the cheapest rate of each hotel for the whole stay. Call `get_rates` for the rooms, meals and cancellation terms of the hotel the user picks. Hotels that are priced but whose content FX-Port has not cached yet are in `missing_hotel_ids`: read each one with `get_hotel_info`. The answer is always one JSON body: `stream=true` and `Accept: text/event-stream` are ignored for API keys. There is no pagination: a 5 km radius around a big city can return 1,800 hotels and 8 MB, so keep the radius to what the screen needs. A search answers at most 5,000 hotels: when there are more the answer says `truncated` and how many were found. A geo search takes 4 to 20 seconds, a search of one hotel less than a second. Identical searches of your agency that arrive at the same moment are merged into one supplier call; each caller still gets its own `request_id` and headers. Do not send `currency`. A value written in another well-defined way is read and reported in `warnings` (`coerced_value`): a number as text, a date with a midnight time, a sub-territory residency, a radius outside 100 to 70,000 meters, which is moved to the nearest bound. What cannot be read keeps its error code. In the sandbox the search runs on real hotels and prices, but only the supplier test hotels can be booked.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelSearchRequest"
              },
              "examples": {
                "geo_search": {
                  "value": {
                    "latitude": 48.8566,
                    "longitude": 2.3522,
                    "radius": 1000,
                    "checkin": "2026-12-01",
                    "checkout": "2026-12-04",
                    "residency": "gb",
                    "guests": [
                      {
                        "adults": 2,
                        "children": []
                      }
                    ]
                  }
                },
                "one_hotel": {
                  "value": {
                    "checkin": "2026-12-01",
                    "checkout": "2026-12-04",
                    "residency": "gb",
                    "language": "en",
                    "guests": [
                      {
                        "adults": 2,
                        "children": []
                      }
                    ],
                    "hotel_id": "test_hotel_do_not_book"
                  }
                },
                "two_rooms_with_a_child": {
                  "value": {
                    "checkin": "2026-12-10",
                    "checkout": "2026-12-12",
                    "residency": "gb",
                    "language": "en",
                    "guests": [
                      {
                        "adults": 2,
                        "children": [
                          8
                        ]
                      },
                      {
                        "adults": 1,
                        "children": []
                      }
                    ],
                    "hotel_id": "test_hotel_do_not_book"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The hotels that are available, with their mini cards and prices. `status` tells how many have content; an empty `hotels_details` with `no_hotels_found` means no hotel is available for the request (also the case when a hotel exists but not for this residency). The examples are real answers of the sandbox: a geo search around Nuuk, a search of one hotel, a geo search around Tegucigalpa where one priced hotel has no card yet, and an empty answer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelSearchResponse"
                },
                "examples": {
                  "geo_search": {
                    "value": {
                      "environment": "sandbox",
                      "search_id": "71498fa9-d3a7-4b6c-81bd-76693be726d1",
                      "status": "complete",
                      "hotels_details": [
                        {
                          "id": "hotel_nordbo_2",
                          "hid": 10048834,
                          "name": "Hotel Nordbo",
                          "kind": "Hotel",
                          "address": "Vandsovej 13, Nuuk, 3900, Greenland, Nuuk",
                          "latitude": 64.17923,
                          "longitude": -51.73019,
                          "postal_code": null,
                          "region": {
                            "id": 1359,
                            "country_code": "GL",
                            "iata": "GOH",
                            "name": "Nuuk",
                            "type": "City",
                            "type_v2": "city"
                          },
                          "star_rating": 0,
                          "images": [
                            "https://cdn.worldota.net/t/{size}/content/16/be/16be5aea51c0060686286cf8c886e4e07e1e5dce.jpeg",
                            "https://cdn.worldota.net/t/{size}/content/b1/98/b198dca0393650833ee449c3e460b2af85a04dda.jpeg"
                          ],
                          "serp_filters": [
                            "has_parking",
                            "has_meal",
                            "has_pets"
                          ],
                          "amenity_groups": [
                            {
                              "amenities": [
                                "24-hour reception",
                                "Smoke-free property",
                                "Ticket assistance"
                              ],
                              "non_free_amenities": [],
                              "group_name": "General"
                            },
                            {
                              "amenities": [
                                "Breakfast",
                                "Restaurant"
                              ],
                              "non_free_amenities": [],
                              "group_name": "Meals"
                            }
                          ]
                        },
                        {
                          "id": "hhe_express",
                          "hid": 9168363,
                          "name": "HHE Express Hotel",
                          "kind": "Hotel",
                          "address": "Aqqusinersuaq 14B, Nuuk",
                          "latitude": 64.17208,
                          "longitude": -51.73649,
                          "postal_code": "3900",
                          "region": {
                            "id": 1359,
                            "country_code": "GL",
                            "iata": "GOH",
                            "name": "Nuuk",
                            "type": "City",
                            "type_v2": "city"
                          },
                          "star_rating": 3,
                          "images": [
                            "https://cdn.worldota.net/t/{size}/content/b5/39/b539a20fd8349ef419e73802ced9a04215155205.JPEG",
                            "https://cdn.worldota.net/t/{size}/content/41/97/4197c95a76a3ada2539a0fe169e7e3d7df3e80f9.jpeg"
                          ],
                          "serp_filters": [
                            "has_internet",
                            "has_parking"
                          ],
                          "amenity_groups": [
                            {
                              "amenities": [
                                "24-hour reception",
                                "Elevator/lift",
                                "Smoke-free property"
                              ],
                              "non_free_amenities": [],
                              "group_name": "General"
                            },
                            {
                              "amenities": [
                                "Free Wi-Fi"
                              ],
                              "non_free_amenities": [],
                              "group_name": "Internet"
                            }
                          ]
                        }
                      ],
                      "processing_status": {
                        "total_hotels": 5,
                        "cached_hotels": 5,
                        "queued_hotels": 0
                      },
                      "queue_info": {
                        "queued_hotel_ids": [],
                        "estimated_completion": null
                      },
                      "timing": {
                        "total_duration": 0.84,
                        "search_duration": 0.82,
                        "cache_check_duration": 0.02
                      },
                      "hotels_pricing": {
                        "hotel_nordbo_2": {
                          "exchange_rate": 285,
                          "agency_currency": "DZD",
                          "agency_commission": 438.68,
                          "final_price": 219774.68,
                          "b2b_price": 219336
                        },
                        "hhe_express": {
                          "exchange_rate": 285,
                          "agency_currency": "DZD",
                          "agency_commission": 228.83,
                          "final_price": 114639.23,
                          "b2b_price": 114410.4
                        }
                      },
                      "missing_hotel_ids": [],
                      "missing_hotels_pricing": {}
                    }
                  },
                  "one_hotel": {
                    "value": {
                      "environment": "sandbox",
                      "search_id": "71498fa9-d3a7-4b6c-81bd-76693be726d1",
                      "status": "complete",
                      "hotels_details": [
                        {
                          "id": "test_hotel_do_not_book",
                          "hid": 8473727,
                          "name": "Test Hotel (Do Not Book) test",
                          "kind": "Apartment",
                          "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                          "latitude": 14.079872,
                          "longitude": -87.21677,
                          "postal_code": null,
                          "region": {
                            "id": 3421,
                            "country_code": "HN",
                            "iata": "TGU",
                            "name": "Tegucigalpa",
                            "type": "City",
                            "type_v2": "city"
                          },
                          "star_rating": 0,
                          "images": [
                            "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
                            "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG"
                          ],
                          "serp_filters": [
                            "has_internet",
                            "has_parking",
                            "has_spa",
                            "has_pets",
                            "has_jacuzzi",
                            "kitchen"
                          ],
                          "amenity_groups": [
                            {
                              "amenities": [
                                "24-hour reception",
                                "Early check-in",
                                "Late check-out"
                              ],
                              "non_free_amenities": [],
                              "group_name": "General"
                            },
                            {
                              "amenities": [
                                "Concierge services",
                                "Dry-cleaning",
                                "Shoe shine"
                              ],
                              "non_free_amenities": [
                                "Dry-cleaning",
                                "Shoe shine"
                              ],
                              "group_name": "Services and amenities"
                            }
                          ]
                        }
                      ],
                      "processing_status": {
                        "total_hotels": 1,
                        "cached_hotels": 1,
                        "queued_hotels": 0
                      },
                      "queue_info": {
                        "queued_hotel_ids": [],
                        "estimated_completion": null
                      },
                      "timing": {
                        "total_duration": 0.31,
                        "search_duration": 0.3,
                        "cache_check_duration": 0.01
                      },
                      "hotels_pricing": {
                        "test_hotel_do_not_book": {
                          "exchange_rate": 285,
                          "agency_currency": "DZD",
                          "agency_commission": 2.97,
                          "final_price": 1484.97,
                          "b2b_price": 1482
                        }
                      },
                      "missing_hotel_ids": [],
                      "missing_hotels_pricing": {}
                    }
                  },
                  "geo_search_with_missing_hotels": {
                    "value": {
                      "environment": "sandbox",
                      "search_id": "4a6877dc-96da-4f32-8c1b-4aaed8b60fe4",
                      "status": "partial",
                      "hotels_details": [
                        {
                          "id": "hotel_honduras_maya_6",
                          "hid": 6329711,
                          "name": "Hotel Honduras Maya",
                          "kind": "Hotel",
                          "address": "Colonia Palmira 3era calle ave, Tegucigalpa",
                          "latitude": 14.103492,
                          "longitude": -87.19721,
                          "postal_code": "11101",
                          "region": {
                            "id": 3421,
                            "country_code": "HN",
                            "iata": "TGU",
                            "name": "Tegucigalpa",
                            "type": "City",
                            "type_v2": "city"
                          },
                          "star_rating": 3,
                          "images": [
                            "https://cdn.worldota.net/t/{size}/content/1d/67/1d67eefb8685fb4b809624bb4f6c437592722183.JPEG",
                            "https://cdn.worldota.net/t/{size}/content/6a/0e/6a0ebfe39089a02e1e6da4a03eb6200047545ba1.JPEG"
                          ],
                          "serp_filters": [
                            "has_internet",
                            "has_airport_transfer",
                            "has_parking",
                            "has_kids",
                            "has_pool"
                          ],
                          "amenity_groups": [
                            {
                              "amenities": [
                                "ATM",
                                "Air conditioning",
                                "24-hour reception"
                              ],
                              "non_free_amenities": [],
                              "group_name": "General"
                            },
                            {
                              "amenities": [
                                "Bridal suite",
                                "Room service",
                                "Interconnecting rooms available"
                              ],
                              "non_free_amenities": [],
                              "group_name": "Rooms"
                            }
                          ]
                        }
                      ],
                      "processing_status": {
                        "total_hotels": 15,
                        "cached_hotels": 14,
                        "queued_hotels": 1
                      },
                      "queue_info": {
                        "queued_hotel_ids": [
                          "comfort_inn_tegucigalpa"
                        ],
                        "estimated_completion": "about 9 minutes",
                        "estimated_completion_seconds": 541
                      },
                      "timing": {
                        "total_duration": 4.38,
                        "search_duration": 4.33,
                        "cache_check_duration": 0.05
                      },
                      "hotels_pricing": {
                        "hotel_honduras_maya_6": {
                          "exchange_rate": 285,
                          "agency_currency": "DZD",
                          "agency_commission": 285.14,
                          "final_price": 142853.54,
                          "b2b_price": 142568.4
                        },
                        "comfort_inn_tegucigalpa": {
                          "exchange_rate": 285,
                          "agency_currency": "DZD",
                          "agency_commission": 218.75,
                          "final_price": 109590.36,
                          "b2b_price": 109371.61
                        }
                      },
                      "missing_hotel_ids": [
                        "comfort_inn_tegucigalpa"
                      ],
                      "missing_hotels_pricing": {
                        "comfort_inn_tegucigalpa": {
                          "exchange_rate": 285,
                          "agency_currency": "DZD",
                          "agency_commission": 218.75,
                          "final_price": 109590.36,
                          "b2b_price": 109371.61
                        }
                      }
                    }
                  },
                  "no_hotels_found": {
                    "value": {
                      "environment": "sandbox",
                      "search_id": "0c3f0e0e-1f0b-4f55-a2a6-8d4f2f1d2d11",
                      "status": "no_hotels_found",
                      "hotels_details": [],
                      "processing_status": {
                        "total_hotels": 0,
                        "cached_hotels": 0,
                        "queued_hotels": 0
                      },
                      "queue_info": {
                        "queued_hotel_ids": [],
                        "estimated_completion": null
                      },
                      "timing": {
                        "total_duration": 0.24,
                        "search_duration": 0.22,
                        "cache_check_duration": 0
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid. `errors[0].code` says which field and why: a date that is not a date, a stay of more than 30 nights, a room with no adult ... A required field that is missing answers `missing_field`: `errors[0].field` is its name and `errors[0].details.missing_fields` lists all of them when several are missing. A `currency` field answers `bad_request`: prices come in the currency of your agency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "stay_too_long": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "stay_too_long",
                          "type": "validation_error",
                          "title": "Stay too long",
                          "message": "A stay can be at most 30 nights ('checkout' is 35 nights after 'checkin').",
                          "field": "checkout",
                          "details": {
                            "max_nights": 30
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#stay_too_long"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "stay_too_long",
                        "message": "A stay can be at most 30 nights ('checkout' is 35 nights after 'checkin').",
                        "field": "checkout"
                      }
                    }
                  },
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required fields for hotel_id search: ['guests']",
                          "field": "guests",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required fields for hotel_id search: ['guests']",
                        "field": "guests"
                      }
                    }
                  },
                  "several_missing_fields": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required fields for hotel_id search: ['checkin', 'guests']",
                          "field": "checkin",
                          "details": {
                            "missing_fields": [
                              "checkin",
                              "guests"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required fields for hotel_id search: ['checkin', 'guests']",
                        "field": "checkin",
                        "details": {
                          "missing_fields": [
                            "checkin",
                            "guests"
                          ]
                        }
                      }
                    }
                  },
                  "currency_sent": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Do not pass 'currency'. Currency is resolved automatically from the authenticated agency.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "missing_radius": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "'radius' (meters) is required for a geo search.",
                          "field": "radius",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "'radius' (meters) is required for a geo search.",
                        "field": "radius"
                      }
                    }
                  },
                  "invalid_radius": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_radius",
                          "type": "validation_error",
                          "title": "Invalid radius",
                          "message": "'radius' must be a whole number of meters.",
                          "field": "radius",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_radius"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_radius",
                        "message": "'radius' must be a whole number of meters.",
                        "field": "radius"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The hotel supplier refused a search that is well formed, for a reason FX-Port cannot check beforehand. Change the dates, the guests, the residency or the radius.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_rejected_request": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_rejected_request",
                          "type": "supplier_error",
                          "title": "Supplier rejected request",
                          "message": "The hotel supplier rejected the request parameters. Check dates, guests, residency and radius.",
                          "field": null,
                          "details": {
                            "supplier_code": "invalid_params"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_rejected_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_rejected_request",
                        "message": "The hotel supplier rejected the request parameters. Check dates, guests, residency and radius."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "The hotel supplier is receiving too many searches: `errors[0].code` is `rate_limit_exceeded` and `errors[0].type` is `supplier_error`. Retry in a minute with backoff. (When your own key is over its limit the same code has the type `rate_limit_error` and a `Retry-After` header: see Rate limits.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The supplier could not complete the search: `errors[0].code` is `bad_gateway` and `errors[0].type` is `supplier_error`. Retry in a few minutes with backoff; the cause is not your request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The supplier is at capacity for this kind of call. The call was refused before anything was sent: nothing was searched, locked or booked, no booking exists and nothing is debited. Retry after the seconds in `Retry-After` (and in `errors[0].details.retry_after`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3,
                            "reason": "busy",
                            "lane": "search"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Geo search",
            "path": "/docs/hotels/search"
          },
          {
            "label": "Mini and full hotel data",
            "path": "/docs/hotels/hotel-data"
          },
          {
            "label": "Guests and rooms",
            "path": "/docs/hotels/guests-and-rooms"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/search": {
      "post": {
        "operationId": "getHotelContent",
        "tags": [
          "Hotels"
        ],
        "summary": "Read the content of many hotels by id",
        "description": "Reads the content FX-Port holds for up to 200 hotels at once, by id. With `detail_level=minimal` (alias `mini`) each hotel is the MINI card of `search_advanced`: 13 fields, about 5 KB. Without it each hotel is the FULL document: 31 fields with every room type, about 35 KB. Use the mini form for lists and the full form (or `get_hotel_info`) for one hotel the user opened. The answer is a bare JSON array of snake_case hotels, not in the order you asked; an id that has no cached content is left out and queued for loading, it is not reported (use `missing_hotel_ids` of the search, or `get_hotel_info`, for those). An id made of digits (a JSON integer or text) is also looked up as the supplier's numeric `hid`; a hotel reached by both comes back once, and a digits-only id that matches nothing is not queued. `fields` returns only the top-level fields you list (`id` is always added). `images_limit` keeps the first N photos of each mini card. The call reads FX-Port's cache only: it never asks the supplier.",
        "parameters": [
          {
            "name": "detail_level",
            "in": "query",
            "required": false,
            "description": "`minimal` (alias `mini`): the compact card. `full` (default): the whole document.",
            "schema": {
              "type": "string",
              "enum": [
                "minimal",
                "mini",
                "full"
              ],
              "description": "The detail level.",
              "default": "full"
            },
            "example": "minimal"
          },
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "description": "A comma separated list of top-level fields to return instead of a detail level, at most 40 (`id,name,star_rating`). Nested paths are not supported.",
            "schema": {
              "type": "string",
              "description": "The fields to return."
            }
          },
          {
            "name": "images_limit",
            "in": "query",
            "required": false,
            "description": "With `detail_level=minimal` and no `fields`: keep only the first N photos of each hotel, 0 to 30. In any other case it is ignored and the answer carries a `coerced_value` warning. A number outside 0 to 30 answers `400 invalid_images_limit`.",
            "schema": {
              "type": "integer",
              "description": "The most photos per hotel.",
              "minimum": 0,
              "maximum": 30
            },
            "example": 1
          },
          {
            "name": "detailLevel",
            "in": "query",
            "required": false,
            "description": "The detail level, in its older spelling. Use `detail_level`.",
            "schema": {
              "type": "string",
              "description": "The detail level, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "detail_level"
            }
          },
          {
            "name": "imagesLimit",
            "in": "query",
            "required": false,
            "description": "The photo limit, in its older spelling. Use `images_limit`.",
            "schema": {
              "type": "integer",
              "description": "The photo limit, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "images_limit"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelIdsRequest"
              },
              "examples": {
                "two_hotels": {
                  "value": {
                    "hotel_ids": [
                      "test_hotel_do_not_book",
                      "hotel_honduras_maya_6"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The hotels found: mini cards with `detail_level=minimal`, full documents otherwise. The `X-Results-Count` header counts them.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HotelMini"
                      },
                      "description": "Mini cards."
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HotelFull"
                      },
                      "description": "Full documents."
                    }
                  ]
                },
                "examples": {
                  "mini": {
                    "value": [
                      {
                        "id": "test_hotel_do_not_book",
                        "hid": 8473727,
                        "name": "Test Hotel (Do Not Book) test",
                        "kind": "Apartment",
                        "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                        "latitude": 14.079872,
                        "longitude": -87.21677,
                        "postal_code": null,
                        "region": {
                          "id": 3421,
                          "country_code": "HN",
                          "iata": "TGU",
                          "name": "Tegucigalpa",
                          "type": "City",
                          "type_v2": "city"
                        },
                        "star_rating": 0,
                        "images": [
                          "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
                          "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG"
                        ],
                        "serp_filters": [
                          "has_internet",
                          "has_parking",
                          "has_spa",
                          "has_pets",
                          "has_jacuzzi",
                          "kitchen"
                        ],
                        "amenity_groups": [
                          {
                            "amenities": [
                              "24-hour reception",
                              "Early check-in",
                              "Late check-out"
                            ],
                            "non_free_amenities": [],
                            "group_name": "General"
                          },
                          {
                            "amenities": [
                              "Concierge services",
                              "Dry-cleaning",
                              "Shoe shine"
                            ],
                            "non_free_amenities": [
                              "Dry-cleaning",
                              "Shoe shine"
                            ],
                            "group_name": "Services and amenities"
                          }
                        ]
                      }
                    ]
                  },
                  "full": {
                    "value": [
                      {
                        "id": "test_hotel_do_not_book",
                        "hid": 8473727,
                        "name": "Test Hotel (Do Not Book) test",
                        "kind": "Apartment",
                        "hotel_chain": "No chain",
                        "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                        "postal_code": null,
                        "latitude": 14.079872,
                        "longitude": -87.21677,
                        "region": {
                          "id": 3421,
                          "country_code": "HN",
                          "iata": "TGU",
                          "name": "Tegucigalpa",
                          "type": "City",
                          "type_v2": "city"
                        },
                        "star_rating": 0,
                        "star_certificate": null,
                        "email": "test@example.com",
                        "phone": "+15555550123",
                        "check_in_time": "14:00:00",
                        "check_out_time": "12:00:00",
                        "front_desk_time_start": "00:00:00",
                        "front_desk_time_end": "00:00:00",
                        "images": [
                          "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                        ],
                        "amenity_groups": [
                          {
                            "amenities": [
                              "24-hour reception",
                              "Early check-in",
                              "Late check-out"
                            ],
                            "non_free_amenities": [],
                            "group_name": "General"
                          }
                        ],
                        "serp_filters": [
                          "has_internet",
                          "has_parking",
                          "kitchen"
                        ],
                        "description_struct": [
                          {
                            "title": "Location",
                            "paragraphs": [
                              "A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
                            ]
                          }
                        ],
                        "metapolicy_struct": {
                          "internet": [
                            {
                              "inclusion": "included",
                              "price": "0.00",
                              "currency": null,
                              "price_unit": "unspecified",
                              "internet_type": "unspecified",
                              "work_area": "hotel"
                            }
                          ],
                          "meal": [
                            {
                              "inclusion": "not_included",
                              "price": "300.00",
                              "currency": "USD",
                              "meal_type": "breakfast"
                            }
                          ],
                          "children_meal": [
                            {
                              "inclusion": "not_included",
                              "price": "200.00",
                              "currency": "USD",
                              "meal_type": "breakfast",
                              "age_start": 0,
                              "age_end": 5
                            }
                          ],
                          "extra_bed": [
                            {
                              "inclusion": "not_included",
                              "price": "100.00",
                              "currency": "ALL",
                              "price_unit": "per_guest_per_night",
                              "amount": 1
                            }
                          ],
                          "cot": [
                            {
                              "inclusion": "not_included",
                              "price": "0.00",
                              "currency": "USD",
                              "price_unit": "per_room_per_night",
                              "amount": 1
                            }
                          ],
                          "pets": [
                            {
                              "inclusion": "not_included",
                              "price": "560.00",
                              "currency": "USD",
                              "price_unit": "per_room_per_stay",
                              "pets_type": "unspecified"
                            }
                          ],
                          "shuttle": [
                            {
                              "inclusion": "not_included",
                              "price": "100.00",
                              "currency": "EUR",
                              "shuttle_type": "one_way",
                              "destination_type": "train"
                            }
                          ],
                          "parking": [
                            {
                              "inclusion": "not_included",
                              "price": "100.00",
                              "currency": "USD",
                              "price_unit": "per_car_per_night",
                              "territory_type": "unspecified"
                            }
                          ],
                          "children": [
                            {
                              "age_start": 0,
                              "age_end": 3,
                              "price": "500.00",
                              "currency": "CHF",
                              "extra_bed": "available"
                            }
                          ],
                          "visa": {
                            "visa_support": "support_enable"
                          },
                          "deposit": [
                            {
                              "availability": "available",
                              "price": "123.00",
                              "currency": "HNL",
                              "price_unit": "per_room_per_stay",
                              "pricing_method": "fixed",
                              "payment_type": "cash",
                              "deposit_type": "unspecified"
                            }
                          ],
                          "no_show": {
                            "availability": "available",
                            "time": "09:00:00",
                            "day_period": "after_midday"
                          },
                          "add_fee": [],
                          "check_in_check_out": []
                        },
                        "metapolicy_extra_info": "Front desk is open 24/7.",
                        "room_groups": [
                          {
                            "room_group_id": 95,
                            "name": "Standard Double room full double bed",
                            "name_struct": {
                              "main_name": "Standard Double room",
                              "bedding_type": "full double bed",
                              "bathroom": "shared bathroom"
                            },
                            "rg_ext": {
                              "class": 3,
                              "quality": 2,
                              "sex": 0,
                              "bathroom": 1,
                              "bedding": 3,
                              "family": 0,
                              "capacity": 2,
                              "club": 0,
                              "bedrooms": 0,
                              "balcony": 0,
                              "floor": 0,
                              "view": 0
                            },
                            "room_amenities": [
                              "bath",
                              "heating"
                            ],
                            "images": [
                              "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                            ]
                          }
                        ],
                        "facts": {
                          "floors_number": null,
                          "rooms_number": null,
                          "year_built": null,
                          "year_renovated": null,
                          "electricity": {
                            "frequency": [
                              60
                            ],
                            "voltage": null,
                            "sockets": [
                              "a",
                              "b"
                            ]
                          },
                          "register": null
                        },
                        "payment_methods": [
                          "visa",
                          "maestro",
                          "cash"
                        ],
                        "keys_pickup": {
                          "type": "lockbox",
                          "phone": "+15555550123",
                          "email": "test@example.com",
                          "apartment_office_address": null,
                          "apartment_extra_information": "The keys are in the box by the front door.",
                          "is_contactless": true
                        },
                        "is_closed": false,
                        "is_gender_specification_required": false,
                        "deleted": false
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not a list of hotel ids, an id is malformed, there are more than 200 ids, or `detail_level`, `fields` or `images_limit` is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_detail_level": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_detail_level",
                          "type": "validation_error",
                          "title": "Invalid detail level",
                          "message": "'detail_level' must be 'minimal' (alias 'mini') or 'full' (got 'tiny').",
                          "field": "detail_level",
                          "details": {
                            "allowed": [
                              "minimal",
                              "mini",
                              "full"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_detail_level"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_detail_level",
                        "message": "'detail_level' must be 'minimal' (alias 'mini') or 'full' (got 'tiny').",
                        "field": "detail_level"
                      }
                    }
                  },
                  "invalid_body": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_body",
                          "type": "validation_error",
                          "title": "Invalid body",
                          "message": "'body' can hold at most 200 hotel ids per request.",
                          "field": "body",
                          "details": {
                            "max_items": 200
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_body"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_body",
                        "message": "'body' can hold at most 200 hotel ids per request.",
                        "field": "body"
                      }
                    }
                  },
                  "invalid_images_limit": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_images_limit",
                          "type": "validation_error",
                          "title": "Invalid images limit",
                          "message": "'images_limit' must be an integer from 0 to 30.",
                          "field": "images_limit",
                          "details": {
                            "min": 0,
                            "max": 30
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_images_limit"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_images_limit",
                        "message": "'images_limit' must be an integer from 0 to 30.",
                        "field": "images_limit"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Mini and full hotel data",
            "path": "/docs/hotels/hotel-data"
          }
        ],
        "x-sample-query": "detail_level=minimal",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/details": {
      "post": {
        "operationId": "getHotelDetails",
        "tags": [
          "Hotels"
        ],
        "summary": "Read the raw hotel documents by id",
        "description": "Reads the cached hotel documents of up to 200 hotels, by id, exactly as FX-Port stores them: the FULL fields plus `images_ext` (the photos with their category) and `policy_struct`. The default `detail_level=full` is about 27% larger than `POST /hotels/search` for the same hotel and adds nothing most clients need: prefer `POST /hotels/search` (or `get_hotel_info` for one hotel). With `detail_level=minimal` the answer is the mini card, and `images_limit` keeps the first N photos of each card. Like `POST /hotels/search` it reads the cache only, returns a bare array in no particular order, leaves out ids it does not hold and also looks an id made of digits up as the supplier's numeric `hid`.",
        "parameters": [
          {
            "name": "detail_level",
            "in": "query",
            "required": false,
            "description": "`full` (default): the raw document. `minimal` (alias `mini`): the compact card.",
            "schema": {
              "type": "string",
              "enum": [
                "minimal",
                "mini",
                "full"
              ],
              "description": "The detail level.",
              "default": "full"
            }
          },
          {
            "name": "images_limit",
            "in": "query",
            "required": false,
            "description": "With `detail_level=minimal` (alias `mini`): keep only the first N photos of each card, 0 to 30. With `detail_level=full` it is ignored and the answer carries a `coerced_value` warning. A number outside 0 to 30 answers `400 invalid_images_limit`.",
            "schema": {
              "type": "integer",
              "description": "The most photos per hotel, 0 to 30.",
              "minimum": 0,
              "maximum": 30
            },
            "example": 1
          },
          {
            "name": "detailLevel",
            "in": "query",
            "required": false,
            "description": "The detail level, in its older spelling. Use `detail_level`.",
            "schema": {
              "type": "string",
              "description": "The detail level, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "detail_level"
            }
          },
          {
            "name": "imagesLimit",
            "in": "query",
            "required": false,
            "description": "The photo limit, in its older spelling. Use `images_limit`.",
            "schema": {
              "type": "integer",
              "description": "The photo limit, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "images_limit"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelIdsRequest"
              },
              "examples": {
                "one_hotel": {
                  "value": {
                    "hotel_ids": [
                      "test_hotel_do_not_book"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The documents found, as a bare JSON array.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HotelRawDocument"
                      },
                      "description": "Raw documents."
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HotelMini"
                      },
                      "description": "Mini cards."
                    }
                  ]
                },
                "examples": {
                  "raw": {
                    "value": [
                      {
                        "id": "test_hotel_do_not_book",
                        "hid": 8473727,
                        "name": "Test Hotel (Do Not Book) test",
                        "kind": "Apartment",
                        "hotel_chain": "No chain",
                        "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                        "postal_code": null,
                        "latitude": 14.079872,
                        "longitude": -87.21677,
                        "region": {
                          "id": 3421,
                          "country_code": "HN",
                          "iata": "TGU",
                          "name": "Tegucigalpa",
                          "type": "City",
                          "type_v2": "city"
                        },
                        "star_rating": 0,
                        "star_certificate": null,
                        "email": "test@example.com",
                        "phone": "+15555550123",
                        "check_in_time": "14:00:00",
                        "check_out_time": "12:00:00",
                        "front_desk_time_start": "00:00:00",
                        "front_desk_time_end": "00:00:00",
                        "images": [
                          "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                        ],
                        "amenity_groups": [
                          {
                            "amenities": [
                              "24-hour reception",
                              "Early check-in",
                              "Late check-out"
                            ],
                            "non_free_amenities": [],
                            "group_name": "General"
                          }
                        ],
                        "serp_filters": [
                          "has_internet",
                          "has_parking",
                          "kitchen"
                        ],
                        "description_struct": [
                          {
                            "title": "Location",
                            "paragraphs": [
                              "A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
                            ]
                          }
                        ],
                        "metapolicy_struct": {
                          "internet": [
                            {
                              "inclusion": "included",
                              "price": "0.00",
                              "currency": null,
                              "price_unit": "unspecified",
                              "internet_type": "unspecified",
                              "work_area": "hotel"
                            }
                          ],
                          "meal": [
                            {
                              "inclusion": "not_included",
                              "price": "300.00",
                              "currency": "USD",
                              "meal_type": "breakfast"
                            }
                          ],
                          "children_meal": [
                            {
                              "inclusion": "not_included",
                              "price": "200.00",
                              "currency": "USD",
                              "meal_type": "breakfast",
                              "age_start": 0,
                              "age_end": 5
                            }
                          ],
                          "extra_bed": [
                            {
                              "inclusion": "not_included",
                              "price": "100.00",
                              "currency": "ALL",
                              "price_unit": "per_guest_per_night",
                              "amount": 1
                            }
                          ],
                          "cot": [
                            {
                              "inclusion": "not_included",
                              "price": "0.00",
                              "currency": "USD",
                              "price_unit": "per_room_per_night",
                              "amount": 1
                            }
                          ],
                          "pets": [
                            {
                              "inclusion": "not_included",
                              "price": "560.00",
                              "currency": "USD",
                              "price_unit": "per_room_per_stay",
                              "pets_type": "unspecified"
                            }
                          ],
                          "shuttle": [
                            {
                              "inclusion": "not_included",
                              "price": "100.00",
                              "currency": "EUR",
                              "shuttle_type": "one_way",
                              "destination_type": "train"
                            }
                          ],
                          "parking": [
                            {
                              "inclusion": "not_included",
                              "price": "100.00",
                              "currency": "USD",
                              "price_unit": "per_car_per_night",
                              "territory_type": "unspecified"
                            }
                          ],
                          "children": [
                            {
                              "age_start": 0,
                              "age_end": 3,
                              "price": "500.00",
                              "currency": "CHF",
                              "extra_bed": "available"
                            }
                          ],
                          "visa": {
                            "visa_support": "support_enable"
                          },
                          "deposit": [
                            {
                              "availability": "available",
                              "price": "123.00",
                              "currency": "HNL",
                              "price_unit": "per_room_per_stay",
                              "pricing_method": "fixed",
                              "payment_type": "cash",
                              "deposit_type": "unspecified"
                            }
                          ],
                          "no_show": {
                            "availability": "available",
                            "time": "09:00:00",
                            "day_period": "after_midday"
                          },
                          "add_fee": [],
                          "check_in_check_out": []
                        },
                        "metapolicy_extra_info": "Front desk is open 24/7.",
                        "room_groups": [
                          {
                            "room_group_id": 95,
                            "name": "Standard Double room full double bed",
                            "name_struct": {
                              "main_name": "Standard Double room",
                              "bedding_type": "full double bed",
                              "bathroom": "shared bathroom"
                            },
                            "rg_ext": {
                              "class": 3,
                              "quality": 2,
                              "sex": 0,
                              "bathroom": 1,
                              "bedding": 3,
                              "family": 0,
                              "capacity": 2,
                              "club": 0,
                              "bedrooms": 0,
                              "balcony": 0,
                              "floor": 0,
                              "view": 0
                            },
                            "room_amenities": [
                              "bath",
                              "heating"
                            ],
                            "images": [
                              "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                            ]
                          }
                        ],
                        "facts": {
                          "floors_number": null,
                          "rooms_number": null,
                          "year_built": null,
                          "year_renovated": null,
                          "electricity": {
                            "frequency": [
                              60
                            ],
                            "voltage": null,
                            "sockets": [
                              "a",
                              "b"
                            ]
                          },
                          "register": null
                        },
                        "payment_methods": [
                          "visa",
                          "maestro",
                          "cash"
                        ],
                        "keys_pickup": {
                          "type": "lockbox",
                          "phone": "+15555550123",
                          "email": "test@example.com",
                          "apartment_office_address": null,
                          "apartment_extra_information": "The keys are in the box by the front door.",
                          "is_contactless": true
                        },
                        "is_closed": false,
                        "is_gender_specification_required": false,
                        "deleted": false,
                        "images_ext": [
                          {
                            "url": "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
                            "category_slug": "others"
                          },
                          {
                            "url": "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG",
                            "category_slug": "outside"
                          }
                        ],
                        "policy_struct": [
                          {
                            "title": "Meals",
                            "paragraphs": [
                              "Price of an additional breakfast: 300.00 USD per person."
                            ]
                          }
                        ]
                      }
                    ]
                  },
                  "minimal": {
                    "value": [
                      {
                        "id": "test_hotel_do_not_book",
                        "hid": 8473727,
                        "name": "Test Hotel (Do Not Book) test",
                        "kind": "Apartment",
                        "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                        "latitude": 14.079872,
                        "longitude": -87.21677,
                        "postal_code": null,
                        "region": {
                          "id": 3421,
                          "country_code": "HN",
                          "iata": "TGU",
                          "name": "Tegucigalpa",
                          "type": "City",
                          "type_v2": "city"
                        },
                        "star_rating": 0,
                        "images": [
                          "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
                          "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG"
                        ],
                        "serp_filters": [
                          "has_internet",
                          "has_parking",
                          "has_spa",
                          "has_pets",
                          "has_jacuzzi",
                          "kitchen"
                        ],
                        "amenity_groups": [
                          {
                            "amenities": [
                              "24-hour reception",
                              "Early check-in",
                              "Late check-out"
                            ],
                            "non_free_amenities": [],
                            "group_name": "General"
                          },
                          {
                            "amenities": [
                              "Concierge services",
                              "Dry-cleaning",
                              "Shoe shine"
                            ],
                            "non_free_amenities": [
                              "Dry-cleaning",
                              "Shoe shine"
                            ],
                            "group_name": "Services and amenities"
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not a list of hotel ids, an id is malformed, there are more than 200 ids, or `detail_level` or `images_limit` is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_body": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_body",
                          "type": "validation_error",
                          "title": "Invalid body",
                          "message": "The request body must be a JSON list of hotel id strings.",
                          "field": "body",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_body"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_body",
                        "message": "The request body must be a JSON list of hotel id strings.",
                        "field": "body"
                      }
                    }
                  },
                  "invalid_images_limit": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_images_limit",
                          "type": "validation_error",
                          "title": "Invalid images limit",
                          "message": "'images_limit' must be an integer from 0 to 30.",
                          "field": "images_limit",
                          "details": {
                            "min": 0,
                            "max": 30
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_images_limit"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_images_limit",
                        "message": "'images_limit' must be an integer from 0 to 30.",
                        "field": "images_limit"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Mini and full hotel data",
            "path": "/docs/hotels/hotel-data"
          }
        ],
        "x-sample-query": "detail_level=minimal&images_limit=1",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/get_hotel_info": {
      "post": {
        "operationId": "getHotelInfo",
        "tags": [
          "Hotels"
        ],
        "summary": "Read one hotel for the hotel page",
        "description": "Returns the content of a hotel for the page the user opens before checkout: description, photos, every room type, policies, facts and contact details, in camelCase (`HotelInfo`). It is the only content call that asks the supplier when FX-Port does not hold the hotel yet, and only for a single id (`hotel_id`): use it for the ids of `missing_hotel_ids`. With `hotel_ids` it returns only the hotels already cached and leaves out the others; an unknown single id answers `404 hotel_not_found`. With `detail_level=minimal` the answer is the compact card instead, which is what a result list needs. A hotel is looked up by its FX-Port id, then by its numeric supplier id (`hid`). Cached content is English whatever `language` you send; `language` only applies to the live fetch. The full document is 5 to 143 KB per hotel (26 KB in the middle, 38 KB on average): cache it by hotel id.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelInfoRequest"
              },
              "examples": {
                "one_hotel": {
                  "value": {
                    "hotel_id": "test_hotel_do_not_book",
                    "language": "en"
                  }
                },
                "mini_cards": {
                  "value": {
                    "hotel_ids": [
                      "test_hotel_do_not_book",
                      "hotel_honduras_maya_6"
                    ],
                    "detail_level": "minimal",
                    "images_limit": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The hotels that were found, in `data`. An empty list when none of several ids is cached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelInfoResponse"
                },
                "examples": {
                  "full": {
                    "value": {
                      "environment": "sandbox",
                      "data": [
                        {
                          "id": "test_hotel_do_not_book",
                          "hid": 8473727,
                          "name": "Test Hotel (Do Not Book) test",
                          "kind": "Apartment",
                          "hotelChain": "No chain",
                          "isClosed": false,
                          "isGenderSpecificationRequired": false,
                          "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                          "postalCode": null,
                          "latitude": 14.079872,
                          "longitude": -87.21677,
                          "region": {
                            "id": 3421,
                            "name": "Tegucigalpa",
                            "countryCode": "HN",
                            "iata": "TGU",
                            "type": "City"
                          },
                          "starRating": 0,
                          "starCertificate": null,
                          "contact": {
                            "email": "test@example.com",
                            "phone": "+15555550123"
                          },
                          "checkIn": "14:00:00",
                          "checkOut": "12:00:00",
                          "frontDeskHours": {
                            "start": "00:00:00",
                            "end": "00:00:00"
                          },
                          "images": [
                            "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                          ],
                          "amenityGroups": [
                            {
                              "groupName": "General",
                              "amenities": [
                                "24-hour reception",
                                "Early check-in",
                                "Late check-out"
                              ],
                              "paidAmenities": []
                            }
                          ],
                          "serpFilters": [
                            "has_internet",
                            "has_parking",
                            "kitchen"
                          ],
                          "descriptionStruct": [
                            {
                              "title": "Location",
                              "paragraphs": [
                                "A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
                              ],
                              "html": "<h3>Location</h3><p>A perfect fit for a big group of travellers ...</p>"
                            }
                          ],
                          "metapolicyExtraInfo": "Front desk is open 24/7.",
                          "metapolicyStruct": {
                            "addFee": [],
                            "checkInCheckOut": [],
                            "children": [
                              {
                                "age_start": 0,
                                "age_end": 3,
                                "price": "500.00",
                                "currency": "CHF",
                                "extra_bed": "available"
                              }
                            ],
                            "childrenMeal": [
                              {
                                "inclusion": "not_included",
                                "price": "200.00",
                                "currency": "USD",
                                "meal_type": "breakfast",
                                "age_start": 0,
                                "age_end": 5
                              }
                            ],
                            "cot": [
                              {
                                "inclusion": "not_included",
                                "price": "0.00",
                                "currency": "USD",
                                "price_unit": "per_room_per_night",
                                "amount": 1
                              }
                            ],
                            "deposit": [
                              {
                                "availability": "available",
                                "price": "123.00",
                                "currency": "HNL",
                                "price_unit": "per_room_per_stay",
                                "pricing_method": "fixed",
                                "payment_type": "cash",
                                "deposit_type": "unspecified"
                              }
                            ],
                            "extraBed": [
                              {
                                "inclusion": "not_included",
                                "price": "100.00",
                                "currency": "ALL",
                                "price_unit": "per_guest_per_night",
                                "amount": 1
                              }
                            ],
                            "internet": [
                              {
                                "inclusion": "included",
                                "price": "0.00",
                                "currency": null,
                                "price_unit": "unspecified",
                                "internet_type": "unspecified",
                                "work_area": "hotel"
                              }
                            ],
                            "meal": [
                              {
                                "inclusion": "not_included",
                                "price": "300.00",
                                "currency": "USD",
                                "meal_type": "breakfast"
                              }
                            ],
                            "noShow": {
                              "availability": "available",
                              "time": "09:00:00",
                              "day_period": "after_midday"
                            },
                            "parking": [
                              {
                                "inclusion": "not_included",
                                "price": "100.00",
                                "currency": "USD",
                                "price_unit": "per_car_per_night",
                                "territory_type": "unspecified"
                              }
                            ],
                            "pets": [
                              {
                                "inclusion": "not_included",
                                "price": "560.00",
                                "currency": "USD",
                                "price_unit": "per_room_per_stay",
                                "pets_type": "unspecified"
                              }
                            ],
                            "shuttle": [
                              {
                                "inclusion": "not_included",
                                "price": "100.00",
                                "currency": "EUR",
                                "shuttle_type": "one_way",
                                "destination_type": "train"
                              }
                            ],
                            "visa": {
                              "visa_support": "support_enable"
                            }
                          },
                          "roomGroups": [
                            {
                              "roomGroupId": 95,
                              "name": "Standard Double room full double bed",
                              "nameStruct": {
                                "mainName": "Standard Double room",
                                "beddingType": "full double bed",
                                "bathroom": "shared bathroom"
                              },
                              "images": [
                                "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                              ],
                              "roomAmenities": [
                                "bath",
                                "heating"
                              ],
                              "roomClass": "room",
                              "quality": "standard",
                              "bathroom": "private-bathroom",
                              "hasPrivateBathroom": true,
                              "capacity": 2,
                              "balcony": false,
                              "view": null,
                              "floor": null,
                              "bedrooms": null,
                              "beddingType": "double-bed",
                              "isFamily": false,
                              "isClub": false,
                              "genderRestriction": null
                            }
                          ],
                          "facts": {
                            "floorsNumber": null,
                            "roomsNumber": null,
                            "yearBuilt": null,
                            "yearRenovated": null,
                            "electricity": {
                              "voltage": [],
                              "frequency": [
                                60
                              ],
                              "sockets": [
                                "a",
                                "b"
                              ]
                            }
                          },
                          "paymentMethods": [
                            "visa",
                            "maestro",
                            "cash"
                          ],
                          "keysPickup": {
                            "type": "lockbox",
                            "isContactless": true,
                            "email": "test@example.com",
                            "phone": "+15555550123",
                            "officeAddress": null,
                            "extraInfo": "The keys are in the box by the front door."
                          }
                        }
                      ]
                    }
                  },
                  "minimal": {
                    "value": {
                      "environment": "sandbox",
                      "data": [
                        {
                          "id": "test_hotel_do_not_book",
                          "hid": 8473727,
                          "name": "Test Hotel (Do Not Book) test",
                          "kind": "Apartment",
                          "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
                          "latitude": 14.079872,
                          "longitude": -87.21677,
                          "postal_code": null,
                          "region": {
                            "id": 3421,
                            "country_code": "HN",
                            "iata": "TGU",
                            "name": "Tegucigalpa",
                            "type": "City",
                            "type_v2": "city"
                          },
                          "star_rating": 0,
                          "images": [
                            "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
                            "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG"
                          ],
                          "serp_filters": [
                            "has_internet",
                            "has_parking",
                            "has_spa",
                            "has_pets",
                            "has_jacuzzi",
                            "kitchen"
                          ],
                          "amenity_groups": [
                            {
                              "amenities": [
                                "24-hour reception",
                                "Early check-in",
                                "Late check-out"
                              ],
                              "non_free_amenities": [],
                              "group_name": "General"
                            },
                            {
                              "amenities": [
                                "Concierge services",
                                "Dry-cleaning",
                                "Shoe shine"
                              ],
                              "non_free_amenities": [
                                "Dry-cleaning",
                                "Shoe shine"
                              ],
                              "group_name": "Services and amenities"
                            }
                          ]
                        }
                      ]
                    }
                  },
                  "empty": {
                    "value": {
                      "environment": "sandbox",
                      "data": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`hotel_id` and `hotel_ids` are both missing, an id is malformed, there are more than 200 ids, or `detail_level` or `images_limit` is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Hotel IDs are required (hotelId or hotelIds)",
                          "field": "hotel_ids",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Hotel IDs are required (hotelId or hotelIds)",
                        "field": "hotel_ids"
                      }
                    }
                  },
                  "invalid_hotel_ids": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_hotel_ids",
                          "type": "validation_error",
                          "title": "Invalid hotel ids",
                          "message": "'hotel_ids' must be a list of hotel ids.",
                          "field": "hotel_ids",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_hotel_ids"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_hotel_ids",
                        "message": "'hotel_ids' must be a list of hotel ids.",
                        "field": "hotel_ids"
                      }
                    }
                  },
                  "too_many_ids": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "too_many_ids",
                          "type": "validation_error",
                          "title": "Too many ids",
                          "message": "At most 200 hotel ids per request.",
                          "field": "hotel_ids",
                          "details": {
                            "max_items": 200
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#too_many_ids"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "too_many_ids",
                        "message": "At most 200 hotel ids per request.",
                        "field": "hotel_ids"
                      }
                    }
                  },
                  "invalid_images_limit": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_images_limit",
                          "type": "validation_error",
                          "title": "Invalid images limit",
                          "message": "'images_limit' must be an integer from 0 to 30.",
                          "field": "images_limit",
                          "details": {
                            "min": 0,
                            "max": 30
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_images_limit"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_images_limit",
                        "message": "'images_limit' must be an integer from 0 to 30.",
                        "field": "images_limit"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A single `hotel_id` that neither FX-Port nor the supplier knows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "hotel_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "hotel_not_found",
                          "type": "not_found",
                          "title": "Hotel not found",
                          "message": "No hotel with this id exists at the supplier.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#hotel_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "hotel_not_found",
                        "message": "No hotel with this id exists at the supplier."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "The hotel supplier is receiving too many requests (only when FX-Port had to ask it for a single hotel). Retry in a minute with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_rate_limited": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_rate_limited",
                          "type": "supplier_error",
                          "title": "Supplier rate limited",
                          "message": "The hotel supplier is receiving too many requests. Retry in a minute.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_rate_limited"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 429
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_rate_limited",
                        "message": "The hotel supplier is receiving too many requests. Retry in a minute."
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The hotel supplier could not process the request, or answered in a form that could not be used. Retry with backoff; the cause is not your request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_error": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_error",
                          "type": "supplier_error",
                          "title": "Supplier error",
                          "message": "The hotel supplier could not process the request right now. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 502
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_error",
                        "message": "The hotel supplier could not process the request right now. Retry in a few minutes."
                      }
                    }
                  },
                  "supplier_invalid_response": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_invalid_response",
                          "type": "supplier_error",
                          "title": "Supplier invalid response",
                          "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_invalid_response"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 502
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_invalid_response",
                        "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "The hotel supplier could not be reached or did not answer in time, or its lane is full (`supplier_busy`: nothing was sent, retry after `Retry-After`). Only a single `hotel_id` that FX-Port does not hold yet makes this call to the supplier.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_unavailable",
                          "type": "supplier_error",
                          "title": "Supplier unavailable",
                          "message": "The hotel supplier could not be reached. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_unavailable",
                        "message": "The hotel supplier could not be reached. Retry in a few minutes."
                      }
                    }
                  },
                  "supplier_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_timeout",
                          "type": "supplier_error",
                          "title": "Supplier timeout",
                          "message": "The hotel supplier did not answer in time, so the request may or may not have been processed: read the booking's status before you retry.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_timeout",
                        "message": "The hotel supplier did not answer in time, so the request may or may not have been processed: read the booking's status before you retry."
                      }
                    }
                  },
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3,
                            "reason": "busy",
                            "lane": "rates"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Hotel details",
            "path": "/docs/hotels/hotel-details"
          },
          {
            "label": "Mini and full hotel data",
            "path": "/docs/hotels/hotel-data"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/get_rates": {
      "post": {
        "operationId": "getHotelRates",
        "tags": [
          "Hotels"
        ],
        "summary": "Get the rooms and rates of one hotel",
        "description": "Returns every room and rate of one hotel for a stay and an occupancy, each with its meal plan, cancellation schedule and price. Take the `bookHash` of the rate the user chose and send it to `pre_book`. Only prepaid (deposit) rates are returned, in no particular order: sort by `pricing.totalPrice` yourself. Prices are for the whole stay and all rooms, in the currency of your agency. The hashes are valid for 30 minutes. A hotel that is unknown, closed, not available for the dates or not available for the residency answers `200` with `data.hotelId` `null` and an empty `rates`, not an error: check `rates.length`. The call takes 0.5 to 5 seconds. Identical calls of your agency that arrive at the same moment are merged into one supplier call; each caller still gets its own `request_id` and headers. Do not send `currency`. A value written in another well-defined way is read and reported in `warnings` (`coerced_value`), as in `search_advanced`; what cannot be read keeps its error code. In the sandbox the rates of any hotel are real; only the test hotels can be booked, and `test_hotel` has rates for the residencies `dz`, `ru` and `us` only.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelRatesRequest"
              },
              "examples": {
                "one_room": {
                  "value": {
                    "hotel_id": "test_hotel_do_not_book",
                    "checkin": "2026-12-01",
                    "checkout": "2026-12-04",
                    "residency": "gb",
                    "language": "en",
                    "guests": [
                      {
                        "adults": 2,
                        "children": []
                      }
                    ]
                  }
                },
                "two_rooms_with_a_child": {
                  "value": {
                    "hotel_id": "test_hotel_do_not_book",
                    "checkin": "2026-12-10",
                    "checkout": "2026-12-12",
                    "residency": "gb",
                    "language": "en",
                    "guests": [
                      {
                        "adults": 2,
                        "children": [
                          8
                        ]
                      },
                      {
                        "adults": 1,
                        "children": []
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rates of the hotel. `rates` is empty when there is none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelRatesResponse"
                },
                "examples": {
                  "rates": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "hotelId": "test_hotel_do_not_book",
                        "hid": 8473727,
                        "rates": [
                          {
                            "bookHash": "h-14574656-7ce9-5a21-b673-8654c5ebd416",
                            "matchHash": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c",
                            "room": {
                              "name": "Standard room",
                              "beddingType": "single bed",
                              "miscRoomType": null,
                              "capacity": null,
                              "bathroom": "private-bathroom",
                              "hasPrivateBathroom": true,
                              "balcony": false,
                              "roomClass": "room",
                              "quality": "standard",
                              "view": null,
                              "floor": null,
                              "bedrooms": null,
                              "isFamily": false,
                              "isClub": false,
                              "genderRestriction": null,
                              "nonSmoking": true
                            },
                            "meal": {
                              "type": "nomeal",
                              "included": false,
                              "childMealIncluded": false
                            },
                            "dailyPrices": [
                              1,
                              1,
                              1
                            ],
                            "allotment": 77,
                            "isPackage": false,
                            "anyResidency": true,
                            "deposit": null,
                            "noShow": null,
                            "cancellationPolicy": {
                              "freeCancellationBefore": "2026-12-01T21:00:00",
                              "policies": [
                                {
                                  "from": null,
                                  "until": "2026-12-01T21:00:00",
                                  "originalAmount": 0,
                                  "originalCurrency": "EUR",
                                  "currency": "DZD"
                                },
                                {
                                  "from": "2026-12-01T21:00:00",
                                  "until": null,
                                  "originalAmount": 3,
                                  "originalCurrency": "EUR",
                                  "currency": "DZD"
                                }
                              ]
                            },
                            "pricing": {
                              "supplierCurrency": "EUR",
                              "agencyCurrency": "DZD",
                              "originalPrice": 3,
                              "agencyCommission": 2.97,
                              "totalPrice": 1484.97,
                              "taxes": {
                                "cityTax": {
                                  "includedInPrice": false,
                                  "amount": 23.1,
                                  "currency": "HNL"
                                },
                                "electricityFee": {
                                  "includedInPrice": true,
                                  "amount": 0.6,
                                  "currency": "EUR"
                                },
                                "resortFee": {
                                  "includedInPrice": true,
                                  "amount": 0.68,
                                  "currency": "EUR"
                                }
                              }
                            },
                            "exchangeRate": 285
                          }
                        ]
                      },
                      "processingTime": 0.335
                    }
                  },
                  "no_availability": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "hotelId": null,
                        "hid": null,
                        "rates": []
                      },
                      "processingTime": 0.16
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid. `errors[0].code` says which field and why: a date that is not a date, a stay of more than 30 nights, a room with no adult ... A required field that is missing answers `missing_field`: `errors[0].field` is its name and `errors[0].details.missing_fields` lists all of them when several are missing. A `currency` field answers `bad_request`: prices come in the currency of your agency. A search that the supplier itself refuses answers `400` as well, with the supplier's own code in `errors[0].code` and the type `supplier_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "stay_too_long": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "stay_too_long",
                          "type": "validation_error",
                          "title": "Stay too long",
                          "message": "A stay can be at most 30 nights ('checkout' is 35 nights after 'checkin').",
                          "field": "checkout",
                          "details": {
                            "max_nights": 30
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#stay_too_long"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "stay_too_long",
                        "message": "A stay can be at most 30 nights ('checkout' is 35 nights after 'checkin').",
                        "field": "checkout"
                      }
                    }
                  },
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required fields: ['guests']",
                          "field": "guests",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required fields: ['guests']",
                        "field": "guests"
                      }
                    }
                  },
                  "several_missing_fields": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required fields: ['checkin', 'guests']",
                          "field": "checkin",
                          "details": {
                            "missing_fields": [
                              "checkin",
                              "guests"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required fields: ['checkin', 'guests']",
                        "field": "checkin",
                        "details": {
                          "missing_fields": [
                            "checkin",
                            "guests"
                          ]
                        }
                      }
                    }
                  },
                  "currency_sent": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "bad_request",
                          "type": "validation_error",
                          "title": "Bad request",
                          "message": "Do not pass 'currency'. Currency is resolved automatically from the authenticated agency.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#bad_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The hotel supplier answered in a form that could not be used. Retry with backoff; the cause is not your request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_invalid_response": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_invalid_response",
                          "type": "supplier_error",
                          "title": "Supplier invalid response",
                          "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_invalid_response"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 502
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_invalid_response",
                        "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "The hotel supplier could not be reached or did not answer in time, or its lane is full (`supplier_busy`: nothing was sent, retry after `Retry-After`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_unavailable",
                          "type": "supplier_error",
                          "title": "Supplier unavailable",
                          "message": "The hotel supplier could not be reached. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_unavailable",
                        "message": "The hotel supplier could not be reached. Retry in a few minutes."
                      }
                    }
                  },
                  "supplier_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_timeout",
                          "type": "supplier_error",
                          "title": "Supplier timeout",
                          "message": "The hotel supplier did not answer in time, so the request may or may not have been processed: read the booking's status before you retry.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_timeout",
                        "message": "The hotel supplier did not answer in time, so the request may or may not have been processed: read the booking's status before you retry."
                      }
                    }
                  },
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3,
                            "reason": "busy",
                            "lane": "rates"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Get hotel rates",
            "path": "/docs/hotels/rates"
          },
          {
            "label": "Hotel pricing",
            "path": "/docs/hotels/pricing"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/pre_book": {
      "post": {
        "operationId": "preBookHotel",
        "tags": [
          "Hotels"
        ],
        "summary": "Lock a rate before booking",
        "description": "Locks the rate you chose: the supplier re-checks the price and opens an order, and FX-Port keeps the priced offer for one hour under the `partnerOrderId` of the answer. Book it with `book_hotel`, passing that id as `booking_id`. If the price rose since `get_rates` by more than `price_increase_percent` the call fails with `rate_not_found`: call `get_rates` again. If it rose by less, `priceChanged` is `true` and `data` holds the new price. Calling `pre_book` again with the same rate and the same `price_increase_percent` within a minute returns the same answer (the same `partnerOrderId`, no second order at the supplier); another percentage is a new request. Take 4 to 5 seconds. Always check `formData.orderId`: `null` means the supplier opened no order. In the sandbox a hotel other than a supplier test hotel answers `409 hotel_not_bookable_in_sandbox` with the list of test hotels.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelPreBookRequest"
              },
              "examples": {
                "lock": {
                  "value": {
                    "book_hash": "h-14574656-7ce9-5a21-b673-8654c5ebd416",
                    "price_increase_percent": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rate is locked. Keep `partnerOrderId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelPreBookResponse"
                },
                "examples": {
                  "locked": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "status": "ok",
                      "partnerOrderId": "HMVXX4ZQ3",
                      "priceChanged": false,
                      "data": {
                        "hotelId": "test_hotel_do_not_book",
                        "hid": 8473727,
                        "rates": [
                          {
                            "bookHash": "p-58bfbfa0-fd1c-4be9-8a2a-54942cae6627",
                            "matchHash": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c",
                            "room": {
                              "name": "Standard room",
                              "beddingType": "single bed",
                              "miscRoomType": null,
                              "capacity": null,
                              "bathroom": "private-bathroom",
                              "hasPrivateBathroom": true,
                              "balcony": false,
                              "roomClass": "room",
                              "quality": "standard",
                              "view": null,
                              "floor": null,
                              "bedrooms": null,
                              "isFamily": false,
                              "isClub": false,
                              "genderRestriction": null,
                              "nonSmoking": true
                            },
                            "meal": {
                              "type": "nomeal",
                              "included": false,
                              "childMealIncluded": false
                            },
                            "dailyPrices": [
                              1,
                              1,
                              1
                            ],
                            "allotment": 77,
                            "isPackage": false,
                            "anyResidency": true,
                            "deposit": null,
                            "noShow": null,
                            "cancellationPolicy": {
                              "freeCancellationBefore": "2026-12-01T21:00:00",
                              "policies": [
                                {
                                  "from": null,
                                  "until": "2026-12-01T21:00:00",
                                  "originalAmount": 0,
                                  "originalCurrency": "EUR",
                                  "currency": "DZD"
                                },
                                {
                                  "from": "2026-12-01T21:00:00",
                                  "until": null,
                                  "originalAmount": 3,
                                  "originalCurrency": "EUR",
                                  "currency": "DZD"
                                }
                              ]
                            },
                            "pricing": {
                              "supplierCurrency": "EUR",
                              "agencyCurrency": "DZD",
                              "originalPrice": 3,
                              "agencyCommission": 2.97,
                              "totalPrice": 1484.97,
                              "taxes": {
                                "cityTax": {
                                  "includedInPrice": false,
                                  "amount": 23.1,
                                  "currency": "HNL"
                                },
                                "electricityFee": {
                                  "includedInPrice": true,
                                  "amount": 0.6,
                                  "currency": "EUR"
                                },
                                "resortFee": {
                                  "includedInPrice": true,
                                  "amount": 0.68,
                                  "currency": "EUR"
                                }
                              }
                            },
                            "exchangeRate": 285
                          }
                        ]
                      },
                      "formData": {
                        "partnerOrderId": "HMVXX4ZQ3",
                        "orderId": 757581298,
                        "itemId": 232430616,
                        "paymentTypes": [
                          {
                            "amount": "3.00",
                            "currencyCode": "EUR",
                            "isNeedCreditCard": false,
                            "isNeedCvc": false,
                            "type": "deposit"
                          },
                          {
                            "amount": "3.00",
                            "currencyCode": "USD",
                            "isNeedCreditCard": true,
                            "isNeedCvc": true,
                            "type": "now"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`book_hash` is missing or malformed, or the supplier does not know the rate (expired, or the price rose too much). The same call can also answer `400 supplier_invalid_response`: the supplier priced the rate but returned no usable offer (call `get_rates` again).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required field: hash",
                          "field": "book_hash",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required field: hash",
                        "field": "book_hash"
                      }
                    }
                  },
                  "invalid_rate_hash": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_rate_hash",
                          "type": "validation_error",
                          "title": "Invalid rate hash",
                          "message": "'book_hash' must be a rate hash taken from get_rates (got 'abc').",
                          "field": "book_hash",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_rate_hash"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_rate_hash",
                        "message": "'book_hash' must be a rate hash taken from get_rates (got 'abc').",
                        "field": "book_hash"
                      }
                    }
                  },
                  "rate_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "rate_not_found",
                          "type": "supplier_error",
                          "title": "Rate not found",
                          "message": "Rate not found",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#rate_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency or the IP address is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read+Write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Sandbox only: the hotel is not a supplier test hotel, so it cannot be booked. `errors[0].details.test_hotels` lists the hotels that can.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "hotel_not_bookable_in_sandbox": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "hotel_not_bookable_in_sandbox",
                          "type": "conflict",
                          "title": "Hotel not bookable in sandbox",
                          "message": "Only the supplier test hotels can be booked in sandbox: test_hotel_do_not_book (any residency) or test_hotel (residency dz, ru or us).",
                          "field": "hotel_id",
                          "details": {
                            "test_hotels": [
                              {
                                "id": "test_hotel_do_not_book",
                                "hid": 8473727,
                                "residency": "any"
                              },
                              {
                                "id": "test_hotel",
                                "hid": 8526976,
                                "residency": [
                                  "dz",
                                  "ru",
                                  "us"
                                ]
                              }
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#hotel_not_bookable_in_sandbox"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 409
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "hotel_not_bookable_in_sandbox",
                        "message": "Only the supplier test hotels can be booked in sandbox: test_hotel_do_not_book (any residency) or test_hotel (residency dz, ru or us).",
                        "field": "hotel_id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The hotel supplier answered in a form that could not be used. Nothing is locked: retry in a few minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_invalid_response": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_invalid_response",
                          "type": "supplier_error",
                          "title": "Supplier invalid response",
                          "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_invalid_response"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 502
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_invalid_response",
                        "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes."
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "The supplier is at capacity for this kind of call. The call was refused before anything was sent: nothing was searched, locked or booked, no booking exists and nothing is debited. Retry after the seconds in `Retry-After` (and in `errors[0].details.retry_after`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3,
                            "reason": "busy",
                            "lane": "rates"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Pre-book a rate",
            "path": "/docs/hotels/pre-book"
          },
          {
            "label": "Sandbox testing",
            "path": "/docs/hotels/sandbox-testing"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/book_hotel": {
      "post": {
        "operationId": "bookHotel",
        "tags": [
          "Hotels"
        ],
        "summary": "Book a locked rate",
        "description": "Books the offer locked by `pre_book` and confirms it: there is no hold or pay-later option for API keys, and your agency is debited as part of the booking, when it is confirmed (the sandbox debits nothing). Send the `booking_id` (the `partnerOrderId` of `pre_book`), the guests of each room and the contact of the booker; the price, the hotel and the dates come from the locked offer, and the pre-booked rate and hotel are always the ones booked: a `book_hash` or `hotel_id` in the body is optional and never an error (a value that is not the offer's is ignored, with an `ignored_field` warning). The answer is `pending`: the supplier confirms in the background, usually 4 to 14 seconds later. Read `GET /bookings/{booking_id}?product=hotels` every 2 seconds until `status` is `confirmed` (or `failed`), then fetch the voucher. FX-Port sends no webhooks. Call `book_hotel` once per `booking_id`: a second call answers `409 duplicate_booking` and does not reach the supplier. After a timeout or a `5xx`, read the booking before you do anything else. A child must be a guest with `is_child` true and an `age`. `commission_override` sets your commission for this booking. In the sandbox every booking is a real order on the supplier's test contract: cancel it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelBookRequest"
              },
              "examples": {
                "two_adults": {
                  "value": {
                    "booking_id": "HMVXX4ZQ3",
                    "rooms": [
                      {
                        "guests": [
                          {
                            "first_name": "Jane",
                            "last_name": "Smith"
                          },
                          {
                            "first_name": "John",
                            "last_name": "Smith"
                          }
                        ]
                      }
                    ],
                    "user": {
                      "email": "traveler@example.com",
                      "phone": "+15555550123"
                    },
                    "send_email": false
                  }
                },
                "family_in_two_rooms": {
                  "value": {
                    "booking_id": "HMVXX4ZQ3",
                    "rooms": [
                      {
                        "guests": [
                          {
                            "first_name": "Anna",
                            "last_name": "Smith"
                          },
                          {
                            "first_name": "Ben",
                            "last_name": "Smith"
                          },
                          {
                            "first_name": "Cleo",
                            "last_name": "Smith",
                            "is_child": true,
                            "age": 8
                          }
                        ]
                      },
                      {
                        "guests": [
                          {
                            "first_name": "Dan",
                            "last_name": "Jones"
                          }
                        ]
                      }
                    ],
                    "user": {
                      "email": "traveler@example.com",
                      "phone": "+15555550123",
                      "comment": "Late arrival"
                    },
                    "send_email": false
                  }
                },
                "with_commission": {
                  "value": {
                    "booking_id": "HMVXX4ZQ3",
                    "rooms": [
                      {
                        "guests": [
                          {
                            "first_name": "Jane",
                            "last_name": "Smith"
                          },
                          {
                            "first_name": "John",
                            "last_name": "Smith"
                          }
                        ]
                      }
                    ],
                    "user": {
                      "email": "traveler@example.com",
                      "phone": "+15555550123"
                    },
                    "send_email": false,
                    "commission_override": {
                      "type": "fixed",
                      "value": 50
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The supplier accepted the booking: it is `pending`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelBookingConfirmation"
                },
                "examples": {
                  "pending": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "status": "pending",
                      "bookingId": "HMVXX4ZQ3",
                      "partnerOrderId": "HMVXX4ZQ3",
                      "hotelId": "test_hotel_do_not_book",
                      "checkinDate": "2026-12-01",
                      "checkoutDate": "2026-12-04",
                      "pricing": {
                        "totalPrice": 1484.97,
                        "agencyCommission": 2.97,
                        "currency": "DZD",
                        "supplierCurrency": "EUR",
                        "originalPrice": 3
                      },
                      "postProcessing": {
                        "bookingDbId": "24fec1e5-a09f-48ec-8050-1adaab0a8de1",
                        "webhookSent": false,
                        "bucketLink": null
                      },
                      "processingTime": 5.449,
                      "balanceDebited": false,
                      "transactionCreated": false,
                      "payoutCreated": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is invalid (including a `booking_mode` other than `standard`), the body names two booking ids, or the supplier refused the booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required field: rooms",
                          "field": "rooms",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required field: rooms",
                        "field": "rooms"
                      }
                    }
                  },
                  "invalid_rooms": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_rooms",
                          "type": "validation_error",
                          "title": "Invalid rooms",
                          "message": "'rooms' must be a non-empty list: one {guests: [...]} object per room.",
                          "field": "rooms",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_rooms"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_rooms",
                        "message": "'rooms' must be a non-empty list: one {guests: [...]} object per room.",
                        "field": "rooms"
                      }
                    }
                  },
                  "invalid_send_email": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_send_email",
                          "type": "validation_error",
                          "title": "Invalid send email",
                          "message": "'send_email' must be true or false (got 'maybe').",
                          "field": "send_email",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_send_email"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_send_email",
                        "message": "'send_email' must be true or false (got 'maybe').",
                        "field": "send_email"
                      }
                    }
                  },
                  "invalid_is_child": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_is_child",
                          "type": "validation_error",
                          "title": "Invalid is child",
                          "message": "'rooms[0].guests[1].is_child' must be true or false (got 'maybe').",
                          "field": "rooms[0].guests[1].is_child",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_is_child"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_is_child",
                        "message": "'rooms[0].guests[1].is_child' must be true or false (got 'maybe').",
                        "field": "rooms[0].guests[1].is_child"
                      }
                    }
                  },
                  "invalid_request": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_request",
                          "type": "validation_error",
                          "title": "Invalid request",
                          "message": "'partner.partner_order_id' differs from 'booking_id': send the booking id once, or the same value twice.",
                          "field": "partner.partner_order_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_request",
                        "message": "'partner.partner_order_id' differs from 'booking_id': send the booking id once, or the same value twice.",
                        "field": "partner.partner_order_id"
                      }
                    }
                  },
                  "incorrect_guests_number": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "incorrect_guests_number",
                          "type": "supplier_error",
                          "title": "Incorrect guests number",
                          "message": "Incorrect guests number",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#incorrect_guests_number"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "order_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "order_not_found",
                          "type": "supplier_error",
                          "title": "Order not found",
                          "message": "Order not found",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#order_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Live only: the balance of your agency is lower than what the booking debits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "insufficient_balance": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "insufficient_balance",
                          "type": "payment_error",
                          "title": "Insufficient balance",
                          "message": "The agency balance is lower than the amount to debit.",
                          "field": null,
                          "details": {
                            "available": 120,
                            "required": 1482,
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#insufficient_balance"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 402
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "insufficient_balance",
                        "message": "The agency balance is lower than the amount to debit."
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency or the IP address is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read+Write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The booking cannot be made in the state it is in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate_booking": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "duplicate_booking",
                          "type": "conflict",
                          "title": "Duplicate booking",
                          "message": "A booking already exists for this partner_order_id: do not call book_hotel again, read its status with GET /api/v1/bookings/{booking_id}?product=hotels.",
                          "field": "booking_id",
                          "details": {
                            "booking_id": "HMVXX4ZQ3",
                            "status": "pending"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#duplicate_booking"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 409
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "duplicate_booking",
                        "message": "A booking already exists for this partner_order_id: do not call book_hotel again, read its status with GET /api/v1/bookings/{booking_id}?product=hotels.",
                        "field": "booking_id"
                      }
                    }
                  },
                  "cached_offer_required": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "cached_offer_required",
                          "type": "conflict",
                          "title": "Cached offer required",
                          "message": "Trusted cached hotel offer not found. Re-run pre_book before booking so the backend can verify the exact debit amount.",
                          "field": null,
                          "details": {
                            "partner_order_id": "HMVXX4ZQ3"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#cached_offer_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 409
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "cached_offer_required",
                        "message": "Trusted cached hotel offer not found. Re-run pre_book before booking so the backend can verify the exact debit amount."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The supplier accepted the booking but FX-Port could not finish recording it. Do not book again: read the booking. Any other `500 internal_error`, for example when the supplier does not answer, leaves the booking in an unknown state too: read it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "post_booking_failed": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "post_booking_failed",
                          "type": "internal_error",
                          "title": "Post booking failed",
                          "message": "The booking was accepted but its post-processing failed; read its status with GET /api/v1/bookings/{booking_id}?product=hotels.",
                          "field": null,
                          "details": {
                            "booking_id": "HMVXX4ZQ3"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#post_booking_failed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 500
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "post_booking_failed",
                        "message": "The booking was accepted but its post-processing failed; read its status with GET /api/v1/bookings/{booking_id}?product=hotels."
                      }
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "The supplier answered in a form that could not be read. The booking may exist: read its status before you repeat the call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_invalid_response": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_invalid_response",
                          "type": "supplier_error",
                          "title": "Supplier invalid response",
                          "message": "The hotel supplier returned an unreadable answer to the booking request, so the booking may or may not have been made: read its status with GET /api/v1/bookings/{booking_id}?product=hotels before you retry.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_invalid_response"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 502
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Another booking of your agency in the same currency is still running (`balance_lock_timeout`), or the booking lane of the supplier is full (`supplier_busy`). Both were refused before anything was sent to the supplier: no booking exists and nothing is debited, so you may retry after the seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "balance_lock_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "balance_lock_timeout",
                          "type": "internal_error",
                          "title": "Balance lock timeout",
                          "message": "Another booking of this agency is still being processed; retry in a few seconds.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#balance_lock_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "balance_lock_timeout",
                        "message": "Another booking of this agency is still being processed; retry in a few seconds."
                      }
                    }
                  },
                  "supplier_busy": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_busy",
                          "type": "supplier_error",
                          "title": "Supplier busy",
                          "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3,
                            "reason": "busy",
                            "lane": "book"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_busy"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_busy",
                        "message": "The supplier is temporarily at capacity; retry after the time given in the Retry-After header."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Book a hotel",
            "path": "/docs/hotels/book"
          },
          {
            "label": "Guests and rooms",
            "path": "/docs/hotels/guests-and-rooms"
          },
          {
            "label": "Get a hotel booking",
            "path": "/docs/hotels/get-booking"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/cancel_hotel": {
      "post": {
        "operationId": "cancelHotel",
        "tags": [
          "Hotels"
        ],
        "summary": "Cancel a confirmed hotel booking",
        "description": "Cancels a booking of your agency and refunds it according to the cancellation schedule of the rate. Only a booking whose `status` is `confirmed` can be cancelled: right after `book_hotel` it is `pending`, so wait for `confirmed`. The booking becomes `refunded` (free cancellation), `partial_refund` (a penalty applied) or `cancelled` (nothing refunded). The call takes 1 to 4 seconds. The supplier occasionally takes 30 seconds and the call then fails with `503 supplier_timeout`: the cancellation may or may not have happened, so read the booking, and call again only if it is still `confirmed`. In the sandbox cancel every test booking: it is free until 21:00 UTC on the check-in day.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelCancelRequest"
              },
              "examples": {
                "cancel": {
                  "value": {
                    "booking_id": "HMVXX4ZQ3"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The booking is cancelled. `bookingStatus` and `refund` say what was given back.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelCancellation"
                },
                "examples": {
                  "free_cancellation": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "status": "ok",
                      "bookingId": "HMVXX4ZQ3",
                      "bookingStatus": "refunded",
                      "adminCancel": false,
                      "refund": {
                        "cancellationType": "free",
                        "type": "Free cancellation",
                        "agencyAmount": 1482,
                        "travelerAmount": 1484.97,
                        "currency": "DZD",
                        "providerAmount": 3,
                        "providerCurrency": "EUR"
                      },
                      "refundProcessed": true,
                      "webhookSent": true,
                      "processingTime": 3.488
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`booking_id` is missing or malformed, or the booking is not `confirmed`. A cancellation that the supplier itself refuses answers `400` as well, with the supplier's own code in `errors[0].code` and the type `supplier_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_status": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_status",
                          "type": "validation_error",
                          "title": "Invalid status",
                          "message": "Booking is 'pending', can only cancel 'confirmed' bookings",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_status"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_status",
                        "message": "Booking is 'pending', can only cancel 'confirmed' bookings"
                      }
                    }
                  },
                  "missing_field": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "Missing required field: booking_id",
                          "field": "booking_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "missing_field",
                        "message": "Missing required field: booking_id",
                        "field": "booking_id"
                      }
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "'booking_id' must be 1 to 256 characters: letters, digits and '_ . : -' (got 'abc def').",
                          "field": "booking_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "'booking_id' must be 1 to 256 characters: letters, digits and '_ . : -' (got 'abc def').",
                        "field": "booking_id"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the booking belongs to another agency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "read_write_permission_required": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read+Write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "read_write_permission_required",
                        "message": "This endpoint requires an API key with read_write permission."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "forbidden",
                          "type": "permission_error",
                          "title": "Forbidden",
                          "message": "This booking does not belong to your agency. Only the agency that made the booking can cancel it.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#forbidden"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "forbidden",
                        "message": "This booking does not belong to your agency. Only the agency that made the booking can cancel it."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No booking of your agency has this id in this environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "Booking HZZZZZZZZ not found",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "Booking HZZZZZZZZ not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The supplier answered in a form that could not be used. The cancellation may or may not have happened: read the booking, and call again only if it is still `confirmed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_invalid_response": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_invalid_response",
                          "type": "supplier_error",
                          "title": "Supplier invalid response",
                          "message": "The hotel supplier returned an answer that could not be used. Retry in a few minutes.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_invalid_response"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 502
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "The supplier did not answer in time. The cancellation may or may not have happened: read the booking, and call again only if it is still `confirmed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "supplier_timeout",
                          "type": "supplier_error",
                          "title": "Supplier timeout",
                          "message": "The hotel supplier did not answer in time, so the request may or may not have been processed: read the booking's status before you retry.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#supplier_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "supplier_timeout",
                        "message": "The hotel supplier did not answer in time, so the request may or may not have been processed: read the booking's status before you retry."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Cancel a hotel booking",
            "path": "/docs/hotels/cancel"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/hotels/get_voucher": {
      "get": {
        "operationId": "getHotelVoucher",
        "tags": [
          "Hotels"
        ],
        "summary": "Get the voucher PDF links of a booking",
        "description": "Returns the links of the voucher PDFs of a booking of your agency: one with the price and one without it, to hand to the traveler. The links are permanent public URLs without a signature: anyone who has one can open the voucher, so do not post it where it should not be read. The voucher is made after the booking is `confirmed`, usually at the same moment and sometimes up to 10 seconds later: until then the call answers `404` with the code `pdf_not_found`; try again in 2 seconds. The links stay available after a cancellation. The same links are in `voucherUrl` and `voucherUrlNoPrice` of the booking detail.",
        "parameters": [
          {
            "name": "booking_id",
            "in": "query",
            "required": true,
            "description": "The id of the booking: the `bookingId` of `book_hotel`.",
            "schema": {
              "type": "string",
              "description": "The booking id."
            },
            "example": "HMVXX4ZQ3"
          },
          {
            "name": "partner_order_id",
            "in": "query",
            "required": false,
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "schema": {
              "type": "string",
              "description": "The booking id, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "booking_id"
            }
          },
          {
            "name": "partnerOrderId",
            "in": "query",
            "required": false,
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "schema": {
              "type": "string",
              "description": "The booking id, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "booking_id"
            }
          },
          {
            "name": "bookingId",
            "in": "query",
            "required": false,
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "schema": {
              "type": "string",
              "description": "The booking id, in its older spelling.",
              "deprecated": true,
              "x-replaced-by": "booking_id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The voucher links.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelVoucher"
                },
                "examples": {
                  "confirmed": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "bookingId": "HMVXX4ZQ3",
                        "voucherUrl": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                        "voucherUrlWithPrice": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                        "voucherUrlNoPrice": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith-no-price.pdf",
                        "status": "confirmed"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The booking belongs to another agency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "forbidden": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "forbidden",
                          "type": "permission_error",
                          "title": "Forbidden",
                          "message": "This booking does not belong to your agency. Only the agency that made the booking can retrieve its voucher.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#forbidden"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "forbidden",
                        "message": "This booking does not belong to your agency. Only the agency that made the booking can retrieve its voucher."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No booking has this id (`booking_not_found`), or the booking exists and its voucher is not made yet (`pdf_not_found`: try again in 2 seconds).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "Booking not found",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "Booking not found"
                      }
                    }
                  },
                  "pdf_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "pdf_not_found",
                          "type": "not_found",
                          "title": "Pdf not found",
                          "message": "The voucher is not available yet: it is made shortly after the booking is confirmed. Retry in a few seconds.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#pdf_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "pdf_not_found",
                        "message": "The voucher is not available yet: it is made shortly after the booking is confirmed. Retry in a few seconds."
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`booking_id` is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation_error": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Field required",
                          "field": "booking_id",
                          "details": {
                            "type": "missing"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The service is overloaded for a moment (the database pool is full). Nothing was read: retry after the seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Get the hotel voucher",
            "path": "/docs/hotels/voucher"
          }
        ],
        "x-sample-query": "booking_id=HMVXX4ZQ3",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/packages": {
      "get": {
        "operationId": "listEsimPackages",
        "tags": [
          "eSIMs"
        ],
        "summary": "List eSIM packages",
        "description": "List the eSIM packages your agency can sell, with prices in your agency currency.\n\nFilter by `country` (a slug such as `france`). The unfiltered catalog is about 4 MB, has no pagination and is the slowest call of the eSIM API, so send `country` whenever you can and accept compressed responses. English text is served from a cache that is refreshed about every 50 minutes. Another `locale` is fetched from the supplier on its first call (about 10 seconds) and then cached for an hour.\n\nRegional and worldwide bundles are `global` packages whose `country_slug` is the region (for example `europe`). A `202` means the catalog is still loading after a restart: wait a few seconds and send the call again.\n\nFour amounts describe each package: `base_price`, `retail_price`, `balance_debit_amount` and `agency_commission`. Read the pricing guide before you compute anything from them.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "Country slug, for example `france` or `turkey`. Regions have their own slug, for example `europe`. A country name as people write it is read as its slug (`United States` is `united-states`, `Bosnia & Herzegovina` is `bosnia-and-herzegovina`); case and accents do not matter. An unknown slug or a name that matches no slug returns an empty list.",
            "schema": {
              "type": "string",
              "description": "A country or region slug.",
              "maxLength": 64,
              "example": "france"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Scope of the package: `local` is one country, `global` is every regional and worldwide bundle. Any other well-formed value returns an empty list: there is no `regional` value.",
            "schema": {
              "type": "string",
              "description": "`local` or `global`.",
              "maxLength": 32,
              "example": "local"
            }
          },
          {
            "name": "filter[type]",
            "in": "query",
            "required": false,
            "description": "Previous name of `type`. It still works and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "`local` or `global`.",
              "maxLength": 32,
              "deprecated": true,
              "x-replaced-by": "type"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Language of the text fields. Without it the `Accept-Language` header is used, then `en`. Any spelling of a locale is read and only its language counts: `fr`, `fr-CA`, `zh-Hans`, `pt_BR`, `fr_FR.UTF-8`, or a whole `Accept-Language` value such as `fr-FR,fr;q=0.9` (the first language is used). Supported: `en`, `fr`, `ar`, `de`, `es`, `it`, `ja`, `ko`, `pl`, `pt`, `ro`, `ru`, `sq`, `tr`, `vi`, `zh`. Another well-formed language is answered in English and the response carries a `coerced_value` warning.",
            "schema": {
              "type": "string",
              "description": "A language code or locale.",
              "maxLength": 80,
              "example": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The packages that match the filters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimPackagesResponse"
                },
                "examples": {
                  "france": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "endpoint": "/packages",
                      "data": [
                        {
                          "id": "france-in-7days-1gb",
                          "package_id": "france-in-7days-1gb",
                          "type": "sim",
                          "price": 402.5,
                          "base_price": 402.5,
                          "amount": 1024,
                          "day": 7,
                          "is_unlimited": false,
                          "title": "1 GB - 7 days",
                          "is_fair_usage_policy": false,
                          "fair_usage_policy": null,
                          "data": "1 GB",
                          "voice": null,
                          "text": null,
                          "country_name": "France",
                          "country_code": "FR",
                          "country_slug": "france",
                          "country_image_url": "https://cdn-revamp.airalo.com/images/9753dedb-d495-47cf-b6e4-82e555564743.png",
                          "operator_name": "KPN France Data only",
                          "operator_id": 1704,
                          "operator_style": "dark",
                          "operator_gradient_start": "#F1D4FB",
                          "operator_gradient_end": "#EEC0FF",
                          "operator_type": "local",
                          "operator_is_prepaid": false,
                          "operator_esim_type": "Prepaid",
                          "operator_warning": null,
                          "operator_apn_type": "automatic",
                          "operator_is_roaming": true,
                          "operator_info": [
                            "Data-only eSIM.",
                            "Rechargeable online with no expiry."
                          ],
                          "operator_image_url": "https://cdn-revamp.airalo.com/images/01b47d41-fbb4-4032-8f5a-d1ca6a0f3688.png",
                          "operator_plan_type": "data",
                          "operator_activation_policy": "first-usage",
                          "operator_is_kyc_verify": false,
                          "operator_rechargeability": true,
                          "operator_other_info": "Covers metropolitan (mainland) France and Corsica. Does not include overseas territories (Guadeloupe, Martinique, Reunion, ...",
                          "operator_coverages": [
                            {
                              "name": "FR",
                              "code": "FR",
                              "networks": [
                                {
                                  "name": "Orange",
                                  "types": [
                                    "5G"
                                  ]
                                }
                              ]
                            }
                          ],
                          "operator_apn": {
                            "ios": {
                              "apn_type": "automatic",
                              "apn_value": null
                            },
                            "android": {
                              "apn_type": "automatic",
                              "apn_value": null
                            }
                          },
                          "is_regional": false,
                          "is_global": false,
                          "retail_price": 468.84,
                          "total_price": 468.84,
                          "balance_debit_amount": 418.6,
                          "b2b_price": 418.6,
                          "agency_commission": "12 %",
                          "commission_rate": 12,
                          "currency": "DZD",
                          "supplier_currency": "USD",
                          "exchange_rate": 250
                        },
                        {
                          "id": "france-in-15days-2gb",
                          "package_id": "france-in-15days-2gb",
                          "type": "sim",
                          "price": 547.5,
                          "base_price": 547.5,
                          "amount": 2048,
                          "day": 15,
                          "is_unlimited": false,
                          "title": "2 GB - 15 days",
                          "is_fair_usage_policy": false,
                          "fair_usage_policy": null,
                          "data": "2 GB",
                          "voice": null,
                          "text": null,
                          "country_name": "France",
                          "country_code": "FR",
                          "country_slug": "france",
                          "country_image_url": "https://cdn-revamp.airalo.com/images/9753dedb-d495-47cf-b6e4-82e555564743.png",
                          "operator_name": "KPN France Data only",
                          "operator_id": 1704,
                          "operator_style": "dark",
                          "operator_gradient_start": "#F1D4FB",
                          "operator_gradient_end": "#EEC0FF",
                          "operator_type": "local",
                          "operator_is_prepaid": false,
                          "operator_esim_type": "Prepaid",
                          "operator_warning": null,
                          "operator_apn_type": "automatic",
                          "operator_is_roaming": true,
                          "operator_info": [
                            "Data-only eSIM.",
                            "Rechargeable online with no expiry."
                          ],
                          "operator_image_url": "https://cdn-revamp.airalo.com/images/01b47d41-fbb4-4032-8f5a-d1ca6a0f3688.png",
                          "operator_plan_type": "data",
                          "operator_activation_policy": "first-usage",
                          "operator_is_kyc_verify": false,
                          "operator_rechargeability": true,
                          "operator_other_info": "Covers metropolitan (mainland) France and Corsica. Does not include overseas territories (Guadeloupe, Martinique, Reunion, ...",
                          "operator_coverages": [
                            {
                              "name": "FR",
                              "code": "FR",
                              "networks": [
                                {
                                  "name": "Orange",
                                  "types": [
                                    "5G"
                                  ]
                                }
                              ]
                            }
                          ],
                          "operator_apn": {
                            "ios": {
                              "apn_type": "automatic",
                              "apn_value": null
                            },
                            "android": {
                              "apn_type": "automatic",
                              "apn_value": null
                            }
                          },
                          "is_regional": false,
                          "is_global": false,
                          "retail_price": 637.73,
                          "total_price": 637.73,
                          "balance_debit_amount": 569.4,
                          "b2b_price": 569.4,
                          "agency_commission": "12 %",
                          "commission_rate": 12,
                          "currency": "DZD",
                          "supplier_currency": "USD",
                          "exchange_rate": 250
                        }
                      ],
                      "meta": {
                        "total_packages": 12,
                        "filters_applied": {
                          "country": "france",
                          "type": "local"
                        },
                        "locale": "en",
                        "agency_currency": "DZD",
                        "currency": "DZD",
                        "supplier_currency": "USD",
                        "exchange_rate": 250,
                        "commission_percentage": "12 %",
                        "commission_rate": 12
                      }
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "The catalog is still loading after a restart. `success` is `false` and `data` is empty: retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimLoadingResponse"
                },
                "examples": {
                  "loading": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "message": "No cached packages available. Package fetcher may still be loading initial data.",
                      "data": [],
                      "meta": {}
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_country": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_country",
                          "type": "validation_error",
                          "title": "Invalid country",
                          "message": "country must be a country slug such as france (received 'France!').",
                          "field": "country",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_country"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "invalid_locale": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_locale",
                          "type": "validation_error",
                          "title": "Invalid locale",
                          "message": "locale must be a language code such as en, fr or ar (received '12').",
                          "field": "locale",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_locale"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Package catalog",
            "path": "/docs/esims/packages"
          },
          {
            "label": "eSIM pricing",
            "path": "/docs/esims/pricing"
          }
        ],
        "x-sample-query": "country=france&type=local",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/compatible-devices": {
      "get": {
        "operationId": "listEsimCompatibleDevices",
        "tags": [
          "eSIMs"
        ],
        "summary": "List eSIM compatible devices",
        "description": "List the devices known to support eSIM, to check a traveler's phone before the sale.\n\nThe list has about 630 entries and is not paginated. It is cached and refreshed about once a week; `meta.cached_at` tells when. Match on `model` (the manufacturer's identifier) or `name`; the case of `brand` differs between entries.\n\nA `202` means the list is still loading after a restart and a `503` that the device fetcher is not running yet: wait a few seconds and retry.",
        "responses": {
          "200": {
            "description": "The devices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimCompatibleDevicesResponse"
                },
                "examples": {
                  "devices": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "model": "T803D",
                          "os": "android",
                          "brand": "Alcatel",
                          "name": "V3 Ultra"
                        },
                        {
                          "model": "iPhone15,2",
                          "os": "ios",
                          "brand": "Apple",
                          "name": "iPhone 14 Pro"
                        }
                      ],
                      "meta": {
                        "total_devices": 631,
                        "cached_at": "2026-11-01T15:07:43.957236",
                        "cache_age_days": 0.01
                      }
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "The list is still loading. `success` is `false` and `data` is empty: retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimLoadingResponse"
                },
                "examples": {
                  "loading": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "message": "No cached devices available. Device fetcher may still be loading initial data.",
                      "data": []
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The device fetcher is not available yet. Retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimLoadingResponse"
                },
                "examples": {
                  "unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "message": "Devices fetcher not available",
                      "data": []
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Package catalog",
            "path": "/docs/esims/packages"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/orders": {
      "post": {
        "operationId": "createEsimOrder",
        "tags": [
          "eSIMs"
        ],
        "summary": "Create an eSIM order",
        "description": "Create one eSIM, debit your agency balance (live only) and return the eSIM with its activation data.\n\nPrices are computed on the server. `quantity` and every price field of the body are ignored (the response lists them in `warnings`), and every order creates exactly one eSIM: call the endpoint once per eSIM. The names of the traveler are optional and are not restricted to ASCII.\n\nSend `Content-Type: application/json` (in any case). A form-encoded body also works, but a JSON body sent without the JSON content type is read as an empty form and answered with `missing_field`.\n\nThere is no idempotency key. While an order for a package is being processed, another order for the same package is refused with `duplicate_request`; on live so is an order for a package when the most recent confirmed order of your agency is for that same package and is less than 60 seconds old, whoever the traveler is. If a call times out, look for the booking with `GET /esims/bookings?search=<traveler email>` before you send the order again.\n\nIf the supplier refuses the order the response has the supplier's status, nothing is debited and a `failed` booking is saved. In the sandbox the supplier creates a real test eSIM with a dummy activation code, no balance is debited, and the response (including its error bodies) carries `balanceDebited`, `transactionCreated` and `payoutCreated`, all `false`, with a snake_case twin each.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EsimOrderRequest"
              },
              "examples": {
                "traveler": {
                  "value": {
                    "package_id": "france-in-7days-1gb",
                    "first_name": "John",
                    "last_name": "Smith",
                    "email": "traveler@example.com",
                    "phone": "+15555550123",
                    "description": "Order for John Smith"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order was placed. The response has status 200, not 201.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimOrderResponse"
                },
                "examples": {
                  "sandbox": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "order": {
                          "code": "20261101-106852",
                          "order_id": "20261101-106852",
                          "package_id": "france-in-7days-1gb",
                          "type": "sim",
                          "description": "Order for John Smith",
                          "esim_type": "local",
                          "created_at": "2026-11-01 15:23:22",
                          "package": "KPN France Data only-1 GB - 7 days",
                          "data": "1 GB",
                          "validity": 7,
                          "price": 468.84,
                          "currency": "DZD",
                          "total_price": 468.84,
                          "retail_price": 468.84,
                          "agency_currency": "DZD",
                          "supplier_currency": "USD",
                          "exchange_rate": 250,
                          "voice": null,
                          "text": null,
                          "coverage": ""
                        },
                        "sim": {
                          "iccid": "8900000540463077957",
                          "created_at": "2026-11-01 15:23:22",
                          "lpa": "lpa.airalo.com",
                          "imsis": null,
                          "qrcode": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                          "qrcode_url": "https://sandbox.airalo.com/qr?expires=1879860202&id=473564&signature=...",
                          "direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                          "apn_type": "automatic",
                          "apn_value": "N/A",
                          "apn": {
                            "ios": {
                              "apn_type": "automatic",
                              "apn_value": "N/A"
                            },
                            "android": {
                              "apn_type": "automatic",
                              "apn_value": "N/A"
                            }
                          },
                          "is_roaming": true,
                          "confirmation_code": null,
                          "msisdn": null,
                          "sharing": {
                            "access_code": 8803,
                            "link": "https://esims.cloud/v8hy7-usgkq6wg"
                          }
                        },
                        "iccid": "8900000540463077957"
                      },
                      "booking_id": "FX-1793546602916",
                      "message": "eSIM order placed successfully",
                      "balanceDebited": false,
                      "balance_debited": false,
                      "transactionCreated": false,
                      "transaction_created": false,
                      "payoutCreated": false,
                      "payout_created": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid: a required field is missing or a value has the wrong format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_package_id": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "package_id is required",
                          "field": "package_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "invalid_email": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_email",
                          "type": "validation_error",
                          "title": "Invalid email",
                          "message": "email is not a valid email address",
                          "field": "email",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_email"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "missing_phone": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "phone is required",
                          "field": "phone",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "invalid_agent_id": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_agent_id",
                          "type": "validation_error",
                          "title": "Invalid agent id",
                          "message": "agent_id is not a user of your agency.",
                          "field": "agent_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_agent_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The agency balance is lower than the amount to debit. Live only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "insufficient_balance": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "insufficient_balance",
                          "type": "payment_error",
                          "title": "Insufficient balance",
                          "message": "Agency balance is insufficient. Available: 300.0 DZD, Required: 418.6 DZD.",
                          "field": null,
                          "details": {
                            "available": 300,
                            "required": 418.6,
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#insufficient_balance"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 402
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency is not active for this environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "read_key": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No package has this id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "package_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "not_found",
                          "title": "Not found",
                          "message": "Package no-such-package-1gb not found in cache. Packages may still be loading — retry in a moment.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "An order for this package is already being processed, or (live) the latest confirmed order of your agency is for the same package and is less than 60 seconds old.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate_request": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "duplicate_request",
                          "type": "conflict",
                          "title": "Duplicate request",
                          "message": "An identical order is already being processed. Please wait before retrying.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#duplicate_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 409
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Two causes. `rate_limit_exceeded`: more requests than the booking group allows per minute. `concurrency_limit_exceeded`: too many orders of your key or agency are being processed at the same time. Nothing was ordered: retry after the seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limit_exceeded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "rate_limit_exceeded",
                          "type": "rate_limit_error",
                          "title": "Rate limit exceeded",
                          "message": "Too many requests hit the API too quickly. Please retry your request after the time specified in the `ratelimit-reset` header.",
                          "field": null,
                          "details": {
                            "retry_after": 12
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#rate_limit_exceeded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 429
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "concurrency_limit_exceeded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "concurrency_limit_exceeded",
                          "type": "rate_limit_error",
                          "title": "Concurrency limit exceeded",
                          "message": "Too many requests of this kind are already running for this API key; wait for one to finish and retry.",
                          "field": null,
                          "details": {
                            "retry_after": 2
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#concurrency_limit_exceeded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 429
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The supplier cannot be reached, another balance operation of your agency is still running, or the service is busy. Retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_unavailable",
                          "type": "internal_error",
                          "title": "Service unavailable",
                          "message": "Airalo token unavailable",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#service_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "balance_lock_timeout": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "balance_lock_timeout",
                          "type": "internal_error",
                          "title": "Balance lock timeout",
                          "message": "Another operation on this balance is in progress. Please retry in a few seconds.",
                          "field": null,
                          "details": {
                            "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#balance_lock_timeout"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Create an eSIM order",
            "path": "/docs/esims/orders"
          },
          {
            "label": "eSIM pricing",
            "path": "/docs/esims/pricing"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/topup-packages/{iccid}": {
      "get": {
        "operationId": "listEsimTopupPackages",
        "tags": [
          "eSIMs"
        ],
        "summary": "List topup packages of an eSIM",
        "description": "List the packages that can top up one eSIM of your agency, with prices in your agency currency.\n\nAlways call this before you create a topup: the packages depend on the eSIM, and the `package_id` you send to `POST /esims/topups` must come from this list. An empty `data.data` means the eSIM cannot be topped up.\n\nThe list is cached for five minutes per eSIM and language (`cached` and `cache_age_seconds` say so). `force=true` skips the cache. In the topup list `price` is the retail amount; the amount that is debited is `balance_debit_amount`.",
        "parameters": [
          {
            "name": "iccid",
            "in": "path",
            "required": true,
            "description": "The eSIM to top up. It must be an eSIM that your agency ordered.",
            "schema": {
              "type": "string",
              "description": "The ICCID of the eSIM: 18 to 22 digits. It may be written in groups of spaces or hyphens (`8944 4654 0000 0123 456`): the digits are read.",
              "example": "8900000540463077957"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "description": "Skip the five-minute cache and ask the supplier again. An empty value means not sent.",
            "schema": {
              "type": "boolean",
              "description": "`true` to skip the cache.",
              "default": false
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Language of the text fields. Without it the `Accept-Language` header is used, then `en`. Any spelling of a locale is read and only its language counts: `fr`, `fr-CA`, `zh-Hans`, `pt_BR`, `fr_FR.UTF-8`, or a whole `Accept-Language` value such as `fr-FR,fr;q=0.9` (the first language is used). Supported: `en`, `fr`, `ar`, `de`, `es`, `it`, `ja`, `ko`, `pl`, `pt`, `ro`, `ru`, `sq`, `tr`, `vi`, `zh`. Another well-formed language is answered in English and the response carries a `coerced_value` warning.",
            "schema": {
              "type": "string",
              "description": "A language code or locale.",
              "maxLength": 80,
              "example": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The topup packages of the eSIM.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimTopupPackagesResponse"
                },
                "examples": {
                  "packages": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "data": [
                          {
                            "id": "france-in-7days-1gb-topup",
                            "package_id": "france-in-7days-1gb-topup",
                            "type": "topup",
                            "price": 468.84,
                            "amount": 1024,
                            "day": 7,
                            "is_unlimited": false,
                            "title": "1 GB - 7 days",
                            "data": "1 GB",
                            "short_info": null,
                            "voice": null,
                            "text": null,
                            "prices": {
                              "recommended_retail_price": {
                                "EUR": 3.56,
                                "GBP": 3,
                                "USD": 4
                              }
                            },
                            "base_price": 402.5,
                            "commission": 50.24,
                            "total_price": 468.84,
                            "retail_price": 468.84,
                            "balance_debit_amount": 418.6,
                            "b2b_price": 418.6,
                            "agency_commission": "12 %",
                            "commission_rate": 12,
                            "currency": "DZD",
                            "supplier_currency": "USD"
                          },
                          {
                            "id": "france-in-15days-2gb-topup",
                            "package_id": "france-in-15days-2gb-topup",
                            "type": "topup",
                            "price": 637.73,
                            "amount": 2048,
                            "day": 15,
                            "is_unlimited": false,
                            "title": "2 GB - 15 days",
                            "data": "2 GB",
                            "short_info": null,
                            "voice": null,
                            "text": null,
                            "prices": {
                              "recommended_retail_price": {
                                "EUR": 5.34,
                                "GBP": 4.5,
                                "USD": 6
                              }
                            },
                            "base_price": 547.5,
                            "commission": 68.33,
                            "total_price": 637.73,
                            "retail_price": 637.73,
                            "balance_debit_amount": 569.4,
                            "b2b_price": 569.4,
                            "agency_commission": "12 %",
                            "commission_rate": 12,
                            "currency": "DZD",
                            "supplier_currency": "USD"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The ICCID or the locale is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_iccid": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_iccid",
                          "type": "validation_error",
                          "title": "Invalid iccid",
                          "message": "iccid must be 18-22 digits (received '123').",
                          "field": "iccid",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_iccid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The ICCID is not an eSIM of your agency in this environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "iccid_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "iccid_not_found",
                          "type": "not_found",
                          "title": "Iccid not found",
                          "message": "No eSIM with this ICCID was found for your account.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#iccid_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The supplier cannot be reached. Retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_unavailable",
                          "type": "internal_error",
                          "title": "Service unavailable",
                          "message": "Airalo token unavailable",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#service_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Top up an eSIM",
            "path": "/docs/esims/topups"
          },
          {
            "label": "Supplier continuity",
            "path": "/docs/esims/supplier-continuity"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/topups": {
      "post": {
        "operationId": "createEsimTopup",
        "tags": [
          "eSIMs"
        ],
        "summary": "Top up an eSIM",
        "description": "Add a topup package to an eSIM of your agency and debit your agency balance (live only).\n\nCall `GET /esims/topup-packages/{iccid}` first and send one of its `package_id` values: a package that was not returned for this eSIM is refused. `email` and `phone` are required and kept on the booking; the email is not sent to the supplier on a topup. The ICCID must be an eSIM that your agency ordered.\n\nThe same rules as for an order apply: JSON body, server-side prices, no idempotency key, `duplicate_request` while the same topup is running or (live) when a topup of the same ICCID was confirmed in the last 60 seconds. If the supplier refuses the topup nothing is debited and a `failed` booking is saved. The sandbox debits nothing, returns a `balance` of `0.0`, and adds the `balanceDebited`, `transactionCreated` and `payoutCreated` flags.\n\nThe booking of a topup is listed by `GET /esims/bookings/{iccid}/topup-history`, not by `GET /esims/bookings`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EsimTopupRequest"
              },
              "examples": {
                "traveler": {
                  "value": {
                    "package_id": "france-in-7days-1gb-topup",
                    "iccid": "8900000540463077957",
                    "first_name": "John",
                    "last_name": "Smith",
                    "email": "traveler@example.com",
                    "phone": "+15555550123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The topup was applied. The response has status 200, not 201.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimTopupResponse"
                },
                "examples": {
                  "sandbox": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "topup": {
                          "code": "20261101-106853",
                          "order_id": "20261101-106853",
                          "package_id": "france-in-7days-1gb-topup",
                          "package": "KPN France Data only-1 GB - 7 days",
                          "data": "1 GB",
                          "validity": 7,
                          "price": 468.84,
                          "currency": "DZD",
                          "total_price": 468.84,
                          "retail_price": 468.84,
                          "agency_currency": "DZD",
                          "supplier_currency": "USD",
                          "exchange_rate": 250
                        },
                        "iccid": "8900000540463077957",
                        "balance": {
                          "previous": 0,
                          "current": 0,
                          "currency": "DZD"
                        }
                      },
                      "booking_id": "FX-1793546625391",
                      "message": "eSIM topup completed successfully",
                      "balanceDebited": false,
                      "balance_debited": false,
                      "transactionCreated": false,
                      "transaction_created": false,
                      "payoutCreated": false,
                      "payout_created": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid: a required field is missing or a value has the wrong format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_iccid": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "missing_field",
                          "type": "validation_error",
                          "title": "Missing field",
                          "message": "package_id and iccid are required",
                          "field": "iccid",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#missing_field"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "invalid_iccid": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_iccid",
                          "type": "validation_error",
                          "title": "Invalid iccid",
                          "message": "iccid must be 18-22 digits (received '123').",
                          "field": "iccid",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_iccid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The agency balance is lower than the amount to debit. Live only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "insufficient_balance": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "insufficient_balance",
                          "type": "payment_error",
                          "title": "Insufficient balance",
                          "message": "Agency balance is insufficient. Available: 300.0 DZD, Required: 418.6 DZD.",
                          "field": null,
                          "details": {
                            "available": 300,
                            "required": 418.6,
                            "currency": "DZD"
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#insufficient_balance"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 402
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key has read permission only, or the agency is not active for this environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "read_key": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "read_write_permission_required",
                          "type": "permission_error",
                          "title": "Read write permission required",
                          "message": "This endpoint requires an API key with read_write permission.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#read_write_permission_required"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 403
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The ICCID is not an eSIM of your agency, or the package is not a topup package of this eSIM.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "iccid_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "iccid_not_found",
                          "type": "not_found",
                          "title": "Iccid not found",
                          "message": "No eSIM with this ICCID was found for your account.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#iccid_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "package_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "not_found",
                          "title": "Not found",
                          "message": "Package no-such-topup not found or has no valid price",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The same topup (eSIM and package) is already being processed, or (live) a topup of this ICCID was confirmed less than 60 seconds ago.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate_request": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "duplicate_request",
                          "type": "conflict",
                          "title": "Duplicate request",
                          "message": "A duplicate topup was detected for this eSIM. If intentional, please wait 60 seconds before retrying.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#duplicate_request"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 409
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Two causes. `rate_limit_exceeded`: more requests than the booking group allows per minute. `concurrency_limit_exceeded`: too many topups of your key or agency are being processed at the same time. Nothing was topped up: retry after the seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limit_exceeded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "rate_limit_exceeded",
                          "type": "rate_limit_error",
                          "title": "Rate limit exceeded",
                          "message": "Too many requests hit the API too quickly. Please retry your request after the time specified in the `ratelimit-reset` header.",
                          "field": null,
                          "details": {
                            "retry_after": 12
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#rate_limit_exceeded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 429
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "concurrency_limit_exceeded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "concurrency_limit_exceeded",
                          "type": "rate_limit_error",
                          "title": "Concurrency limit exceeded",
                          "message": "Too many requests of this kind are already running for this API key; wait for one to finish and retry.",
                          "field": null,
                          "details": {
                            "retry_after": 2
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#concurrency_limit_exceeded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 429
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The supplier cannot be reached, another balance operation of your agency is still running, or the service is busy. Retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_unavailable",
                          "type": "internal_error",
                          "title": "Service unavailable",
                          "message": "Airalo token unavailable",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#service_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read_write",
        "x-guides": [
          {
            "label": "Top up an eSIM",
            "path": "/docs/esims/topups"
          },
          {
            "label": "Supplier continuity",
            "path": "/docs/esims/supplier-continuity"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/usage/{iccid}": {
      "get": {
        "operationId": "getEsimUsage",
        "tags": [
          "eSIMs"
        ],
        "summary": "Get the usage of an eSIM",
        "description": "Get the remaining and the total data, voice and SMS of one eSIM of your agency, with its status and expiry.\n\nUsage is not real time. The last answer is kept for 15 minutes per eSIM (`cached` and `cache_age_seconds`), and `force=true` asks the supplier again. Use `force` after a topup or when a traveler is waiting, not to poll: the supplier limits how often usage can be read. If the supplier cannot answer, the last known usage is returned with `stale: true` and a `stale_reason`; without a last known value the call fails with the supplier's status, and `retry_after` says how long to wait when the supplier was rate limited.\n\n`remaining` and `total` are in megabytes. In the sandbox the usage of a new eSIM is the full allowance and does not change after a topup.",
        "parameters": [
          {
            "name": "iccid",
            "in": "path",
            "required": true,
            "description": "The eSIM to read. It must be an eSIM that your agency ordered.",
            "schema": {
              "type": "string",
              "description": "The ICCID of the eSIM: 18 to 22 digits. It may be written in groups of spaces or hyphens (`8944 4654 0000 0123 456`): the digits are read.",
              "example": "8900000540463077957"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "description": "Skip the 15-minute cache and ask the supplier again. An empty value means not sent.",
            "schema": {
              "type": "boolean",
              "description": "`true` to skip the cache.",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The usage of the eSIM.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimUsageResponse"
                },
                "examples": {
                  "fresh": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "data": {
                          "remaining": 1024,
                          "total": 1024,
                          "expired_at": "2026-11-08 15:23:29",
                          "is_unlimited": false,
                          "status": "ACTIVE",
                          "remaining_voice": 0,
                          "remaining_text": 0,
                          "total_voice": 0,
                          "total_text": 0
                        },
                        "meta": {
                          "message": "success"
                        }
                      }
                    }
                  },
                  "cached": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "data": {
                          "remaining": 1024,
                          "total": 1024,
                          "expired_at": "2026-11-08 15:23:29",
                          "is_unlimited": false,
                          "status": "ACTIVE",
                          "remaining_voice": 0,
                          "remaining_text": 0,
                          "total_voice": 0,
                          "total_text": 0
                        },
                        "meta": {
                          "message": "success"
                        }
                      },
                      "cached": true,
                      "cache_age_seconds": 0
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The ICCID is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_iccid": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_iccid",
                          "type": "validation_error",
                          "title": "Invalid iccid",
                          "message": "iccid must be 18-22 digits (received '123').",
                          "field": "iccid",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_iccid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The ICCID is not an eSIM of your agency in this environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "iccid_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "iccid_not_found",
                          "type": "not_found",
                          "title": "Iccid not found",
                          "message": "No eSIM with this ICCID was found for your account.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#iccid_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "The supplier is rate limiting usage reads and there is no last known value. Wait `retry_after` seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rate_limited": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "rate_limit_exceeded",
                          "type": "rate_limit_error",
                          "title": "Rate limit exceeded",
                          "message": "Rate limited",
                          "field": null,
                          "details": {
                            "retry_after": 60
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#rate_limit_exceeded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 429
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "eSIM usage",
            "path": "/docs/esims/usage"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/usage/bulk": {
      "get": {
        "operationId": "getEsimUsageBulk",
        "tags": [
          "eSIMs"
        ],
        "summary": "Get the usage of several eSIMs",
        "description": "Get the usage of several eSIMs of your agency in one call.\n\nAlways send `iccids` (comma separated, at most 50 for an API key). Without it the call reads the 50 newest eSIM orders of your agency in this environment and the supplier is asked one eSIM after another, which can take several seconds. ICCIDs that are not yours are left out of the answer, and each ICCID appears once.\n\nEach entry has its own `status`: read it per entry. The same 15-minute cache, `force` and stale values as the single call apply per eSIM. An order whose eSIM the supplier has not returned yet has an ICCID that starts with `PENDING-` and the status `PENDING`.",
        "parameters": [
          {
            "name": "iccids",
            "in": "query",
            "required": false,
            "description": "ICCIDs separated by commas (semicolons and line breaks work too), at most 50 for an API key. The parameter may also be repeated (`iccids=a&iccids=b`) or written `iccids[]=a&iccids[]=b`, and an ICCID may be written in groups of spaces or hyphens. Without it the 50 newest eSIM orders of your agency are read.",
            "schema": {
              "type": "string",
              "description": "Comma separated ICCIDs.",
              "example": "8900000540463077957"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "description": "Skip the 15-minute cache for every eSIM and ask the supplier again. An empty value means not sent.",
            "schema": {
              "type": "boolean",
              "description": "`true` to skip the cache.",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One entry per ICCID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimUsageBulkResponse"
                },
                "examples": {
                  "bulk": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "iccid": "8900000540463077957",
                          "data": {
                            "data": {
                              "remaining": 1024,
                              "total": 1024,
                              "expired_at": "2026-11-08 15:23:29",
                              "is_unlimited": false,
                              "status": "ACTIVE",
                              "remaining_voice": 0,
                              "remaining_text": 0,
                              "total_voice": 0,
                              "total_text": 0
                            },
                            "meta": {
                              "message": "success"
                            }
                          },
                          "status": "success"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "An ICCID in `iccids` is not valid, or there are more than 50.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_iccid": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_iccid",
                          "type": "validation_error",
                          "title": "Invalid iccid",
                          "message": "iccids must be 18-22 digits (received '123').",
                          "field": "iccids",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_iccid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "too_many_iccids": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "too_many_iccids",
                          "type": "validation_error",
                          "title": "Too many iccids",
                          "message": "iccids accepts at most 50 ICCIDs per request.",
                          "field": "iccids",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#too_many_iccids"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "eSIM usage",
            "path": "/docs/esims/usage"
          }
        ],
        "x-sample-query": "iccids=8900000540463077957",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/bookings": {
      "get": {
        "operationId": "listEsimBookings",
        "tags": [
          "eSIMs"
        ],
        "summary": "List eSIM bookings",
        "description": "List the eSIM orders of your agency in this environment, newest first, with search and pagination.\n\nOnly orders (`type` `esim`) are listed: topups are returned by `GET /esims/bookings/{iccid}/topup-history`, and both are listed by the account history. A failed attempt is listed with the status `failed`.\n\n`search` matches part of the booking id, the traveler's first or last name, the email, the package name or the ICCID (not the supplier order code or the phone). Characters that are not letters, digits or `@.-_+':/&` are treated as spaces, so any text is safe. `pagination.total` counts the bookings that match, `search` included.\n\nAn API key gets at most 200 bookings per request: `all=true` returns pages of 200 (follow `pagination.has_more` and `page`) and the response carries a `coerced_value` warning.",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Part of a booking id, name, email, package name or ICCID. At most 100 characters.",
            "schema": {
              "type": "string",
              "description": "A search term.",
              "maxLength": 100,
              "example": "FX-1793546602916"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "The page, starting at 1. An empty value means not sent.",
            "schema": {
              "type": "integer",
              "description": "A page number.",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Bookings per page, from 1 to 200. An empty value means not sent.",
            "schema": {
              "type": "integer",
              "description": "A page size.",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "all",
            "in": "query",
            "required": false,
            "description": "`true` for pages of 200 bookings (the default `limit` becomes 200). An empty value means not sent.",
            "schema": {
              "type": "boolean",
              "description": "Return the largest pages.",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of eSIM bookings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimBookingsResponse"
                },
                "examples": {
                  "page": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "id": "639417b4-b103-4ece-bd5d-850315322278",
                          "booking_id": "FX-1793546602916",
                          "order_id": "20261101-106852",
                          "status": "confirmed",
                          "currency": "DZD",
                          "total_price": 468.84,
                          "agency_commission": 50.24,
                          "type": "esim",
                          "package_name": "1 GB - 7 days • France",
                          "iccid": "8900000540463077957",
                          "customer_email": "traveler@example.com",
                          "customer_first_name": "John",
                          "customer_last_name": "Smith",
                          "customer_phone": "+15555550123",
                          "provider_name": "Airalo",
                          "supplier_id": "airalo",
                          "created_at": "2026-11-01T15:23:22.961228+00:00",
                          "updated_at": "2026-11-01T15:23:22.961228+00:00",
                          "environment": "sandbox"
                        }
                      ],
                      "pagination": {
                        "total": 61,
                        "page": 1,
                        "limit": 1,
                        "pages": 61,
                        "total_pages": 61,
                        "has_more": true
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`search` is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_search": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_search",
                          "type": "validation_error",
                          "title": "Invalid search",
                          "message": "search must be at most 100 characters.",
                          "field": "search",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_search"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`page` or `limit` is not an integer in its range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "limit_too_large": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Input should be less than or equal to 200",
                          "field": "limit",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "eSIM bookings",
            "path": "/docs/esims/bookings"
          }
        ],
        "x-sample-query": "limit=1",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/bookings/{iccid}/topup-history": {
      "get": {
        "operationId": "listEsimTopupHistory",
        "tags": [
          "eSIMs"
        ],
        "summary": "List the topups of an eSIM",
        "description": "List the topups of one eSIM, newest first. Each entry has the shape of a booking with `type` `topup`.\n\nThe list has no pagination. It is empty when the eSIM has no topup or is not yours. This is the only eSIM endpoint that lists topups; the account history lists them next to the other bookings.",
        "parameters": [
          {
            "name": "iccid",
            "in": "path",
            "required": true,
            "description": "The eSIM whose topups to list.",
            "schema": {
              "type": "string",
              "description": "The ICCID of the eSIM: 18 to 22 digits. It may be written in groups of spaces or hyphens (`8944 4654 0000 0123 456`): the digits are read.",
              "example": "8900000540463077957"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The topups of the eSIM.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimTopupHistoryResponse"
                },
                "examples": {
                  "topups": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "id": "bd6ff7d7-392f-4d56-8dea-4f0f3dff0af9",
                          "booking_id": "FX-1793546625391",
                          "order_id": "20261101-106853",
                          "status": "confirmed",
                          "currency": "DZD",
                          "total_price": 468.84,
                          "agency_commission": 50.24,
                          "type": "topup",
                          "package_name": "1 GB - 7 days",
                          "iccid": "8900000540463077957",
                          "customer_email": "traveler@example.com",
                          "customer_first_name": "John",
                          "customer_last_name": "Smith",
                          "customer_phone": "+15555550123",
                          "provider_name": "Airalo",
                          "supplier_id": "airalo",
                          "created_at": "2026-11-01T15:23:45.424526+00:00",
                          "updated_at": "2026-11-01T15:23:45.424526+00:00",
                          "environment": "sandbox"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The ICCID is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_iccid": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_iccid",
                          "type": "validation_error",
                          "title": "Invalid iccid",
                          "message": "iccid is not a valid ICCID (received '!').",
                          "field": "iccid",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_iccid"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "eSIM bookings",
            "path": "/docs/esims/bookings"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/bookings/{booking_id}/verify": {
      "get": {
        "operationId": "verifyEsimBooking",
        "tags": [
          "eSIMs"
        ],
        "summary": "Check that an eSIM booking is yours",
        "description": "Tell whether an eSIM booking belongs to your agency, before you build a link or a screen around its `booking_id`.\n\nThe answer is `isOwner` (twin `is_owner`). A booking of another agency and a booking that does not exist answer the same, `false` with status 200, so the call cannot be used to find out which references exist. Only an owner gets `userAgencyId` and `bookingAgencyId`, both your own agency id. The booking must be an eSIM booking of this environment (a sandbox key never sees a live booking).",
        "parameters": [
          {
            "name": "booking_id",
            "in": "path",
            "required": true,
            "description": "The booking reference to check.",
            "schema": {
              "type": "string",
              "description": "A booking reference such as `FX-1793546602916`: at most 100 characters, letters, digits and `._:=+~-`.",
              "example": "FX-1793546602916",
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Whether the booking is yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimBookingOwnership"
                },
                "examples": {
                  "owner": {
                    "value": {
                      "environment": "sandbox",
                      "isOwner": true,
                      "is_owner": true,
                      "userAgencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                      "bookingAgencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d"
                    }
                  },
                  "not_owner": {
                    "value": {
                      "environment": "sandbox",
                      "isOwner": false,
                      "is_owner": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The booking reference is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "booking_id is not a valid booking reference (received '!').",
                          "field": "booking_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "eSIM bookings",
            "path": "/docs/esims/bookings"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/bookings/{booking_id}/setup-instructions": {
      "get": {
        "operationId": "getEsimSetupInstructions",
        "tags": [
          "eSIMs"
        ],
        "summary": "Get the setup data of a booking",
        "description": "Get the original order of an eSIM booking: the eSIM with its activation data and the installation steps as HTML, to build your own confirmation or setup page.\n\nUse the `booking_id` of the order (`FX-` followed by a number). It works for orders only: a topup booking has no setup data. The activation data is sensitive: show it only to the traveler who owns the eSIM.\n\nThis endpoint replaces the older `GET /esims/bookings/{booking_id}/setup-url`, which is deprecated: it answers with the install links only and a `deprecated_endpoint` warning.",
        "parameters": [
          {
            "name": "booking_id",
            "in": "path",
            "required": true,
            "description": "The booking reference returned when the eSIM was ordered.",
            "schema": {
              "type": "string",
              "description": "A booking reference such as `FX-1793546602916`: at most 100 characters, letters, digits and `._:=+~-`.",
              "example": "FX-1793546602916",
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The setup data of the booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimSetupInstructionsResponse"
                },
                "examples": {
                  "order": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "code": "20261101-106852",
                        "order_id": "20261101-106852",
                        "currency": "DZD",
                        "package_id": "france-in-7days-1gb",
                        "quantity": 1,
                        "type": "sim",
                        "description": "Order for John Smith",
                        "esim_type": "local",
                        "validity": 7,
                        "package": "KPN France Data only-1 GB - 7 days",
                        "data": "1 GB",
                        "price": 468.84,
                        "created_at": "2026-11-01 15:23:22",
                        "manual_installation": "<p><b>eSIM name:</b> Élan</p><p><b>Coverage: </b>France</p><p><b>To manually activate the eSIM on your eSIM capable device:</b></p><ol><li>Settings > Cellular/Mobile > Add Cellular/Mobile Plan.</li>...</ol>",
                        "qrcode_installation": "<p><b>eSIM name:</b> Élan</p><p><b>Coverage: </b>France</p><p><b>To activate the eSIM by scanning the QR code on your eSIM capable device you need to print or display this QR code on another device:</b></p>...",
                        "installation_guides": {
                          "en": "https://www.airalo.com/help/getting-started-with-airalo"
                        },
                        "text": null,
                        "voice": null,
                        "sims": [
                          {
                            "created_at": "2026-11-01 15:23:22",
                            "iccid": "8900000540463077957",
                            "lpa": "lpa.airalo.com",
                            "imsis": null,
                            "qrcode": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                            "qrcode_url": "https://sandbox.airalo.com/qr?expires=1879860202&id=473564&signature=...",
                            "apn_type": "automatic",
                            "apn_value": "N/A",
                            "is_roaming": true,
                            "confirmation_code": null,
                            "apn": {
                              "ios": {
                                "apn_type": "automatic",
                                "apn_value": "N/A"
                              },
                              "android": {
                                "apn_type": "automatic",
                                "apn_value": "N/A"
                              }
                            },
                            "msisdn": null,
                            "direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                            "direct_android_installation_url": "https://esimsetup.android.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                            "sharing": {
                              "access_code": 8803,
                              "link": "https://esims.cloud/v8hy7-usgkq6wg"
                            }
                          }
                        ],
                        "total_price": 468.84,
                        "retail_price": 468.84,
                        "agency_currency": "DZD"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The booking reference is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "booking_id is not a valid booking reference (received '!').",
                          "field": "booking_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No eSIM booking of your agency has this reference, or it is a topup and has no setup data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No eSIM booking was found for this booking_id.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  },
                  "topup_booking": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "not_found",
                          "type": "not_found",
                          "title": "Not found",
                          "message": "Setup data not available for this booking",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Setup and installation instructions",
            "path": "/docs/esims/instructions"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/esims/instructions/{iccid}": {
      "get": {
        "operationId": "getEsimInstructions",
        "tags": [
          "eSIMs"
        ],
        "summary": "Get eSIM installation instructions",
        "description": "Get step-by-step installation instructions for one eSIM, per operating system, in the language of the request.\n\nThe steps are generic text of the supplier: they say the activation details are \"available on the app\". Show the QR code and the activation data of the order next to the steps in your own product. The response also contains the activation data of the eSIM, so show it only to the traveler who owns it.",
        "parameters": [
          {
            "name": "iccid",
            "in": "path",
            "required": true,
            "description": "The eSIM to explain. It must be an eSIM that your agency ordered.",
            "schema": {
              "type": "string",
              "description": "The ICCID of the eSIM: 18 to 22 digits. It may be written in groups of spaces or hyphens (`8944 4654 0000 0123 456`): the digits are read.",
              "example": "8900000540463077957"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Language of the text fields. Without it the `Accept-Language` header is used, then `en`. Any spelling of a locale is read and only its language counts: `fr`, `fr-CA`, `zh-Hans`, `pt_BR`, `fr_FR.UTF-8`, or a whole `Accept-Language` value such as `fr-FR,fr;q=0.9` (the first language is used). Supported: `en`, `fr`, `ar`, `de`, `es`, `it`, `ja`, `ko`, `pl`, `pt`, `ro`, `ru`, `sq`, `tr`, `vi`, `zh`. Another well-formed language is answered in English and the response carries a `coerced_value` warning.",
            "schema": {
              "type": "string",
              "description": "A language code or locale.",
              "maxLength": 80,
              "example": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The instructions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EsimInstructionsResponse"
                },
                "examples": {
                  "english": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "data": {
                          "instructions": {
                            "language": "EN",
                            "ios": [
                              {
                                "model": null,
                                "version": "15.0,14.0,13.0,12.0",
                                "direct_apple_installation_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                                "installation_via_qr_code": {
                                  "steps": {
                                    "1": "Go to “Settings”, tap “Cellular/Mobile Data”, then tap “Add Cellular/Data Plan” on your device.",
                                    "2": "Scan the QR code available on the  app, then tap “Add Cellular/Data Plan”.",
                                    "3": "Choose a label for your new eSIM plan."
                                  },
                                  "qr_code_data": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                                  "qr_code_url": "https://sandbox.airalo.com/qr?expires=1879860202&id=473564&signature=..."
                                },
                                "installation_manual": {
                                  "steps": {
                                    "1": "Go to “Settings”, tap “Cellular/Mobile Data”, then tap “Add Cellular/Data Plan” on your device.",
                                    "2": "Tap “Enter Details Manually” and enter the SM-DP+ Address and Activation Code available on the  app by copying them, tap “Next”, then tap “Add Cellular/Data Plan”."
                                  },
                                  "smdp_address_and_activation_code": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                                  "smdp_address": "lpa.airalo.com",
                                  "activation_code": "DUMMY-2610301358-iKFxr-44741"
                                },
                                "network_setup": {
                                  "steps": {
                                    "1": "Go to “Cellular/Mobile Data”, then select the recently downloaded eSIM on your device. Enable the “Turn On This Line” toggle, then select your new eSIM plan for cellular/mobile data.",
                                    "2": "Tap “Cellular/Mobile Data Network”, then enter the APN available on the  app in all APN fields by copying it."
                                  },
                                  "apn_type": "automatic",
                                  "apn_value": "N/A",
                                  "is_roaming": true
                                }
                              }
                            ],
                            "android": [
                              {
                                "model": null,
                                "version": null,
                                "direct_android_installation_url": "https://esimsetup.android.com/esim_qrcode_provisioning?carddata=LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                                "installation_via_qr_code": {
                                  "steps": {
                                    "1": "Go to “Settings”, tap “Connections”, then tap “SIM card manager” on your device.",
                                    "2": "Tap “Add mobile plan”, then tap “Scan carrier QR code”.",
                                    "3": "Scan the QR code available on the  app, then tap “Confirm”."
                                  },
                                  "qr_code_data": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741",
                                  "qr_code_url": "https://sandbox.airalo.com/qr?expires=1879860202&id=473564&signature=..."
                                },
                                "installation_manual": {
                                  "steps": {
                                    "1": "Go to “Settings”, tap “Connections”, then tap “SIM card manager” on your device.",
                                    "2": "Tap “Add mobile plan”, then tap “Scan carrier QR code”.",
                                    "3": "Tap “Enter activation code”."
                                  },
                                  "smdp_address_and_activation_code": "LPA:1$lpa.airalo.com$DUMMY-2610301358-iKFxr-44741"
                                },
                                "network_setup": {
                                  "steps": {
                                    "1": "Go to “SIM card manager”, then turn on your eSIM by enabling the toggle, then tap “OK” on your device.",
                                    "2": "Select your eSIM for mobile data."
                                  },
                                  "apn_type": "automatic",
                                  "apn_value": "N/A",
                                  "is_roaming": true
                                }
                              }
                            ]
                          }
                        },
                        "meta": {
                          "message": "success"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The ICCID or the locale is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_locale": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_locale",
                          "type": "validation_error",
                          "title": "Invalid locale",
                          "message": "locale must be a language code such as en, fr or ar (received '12').",
                          "field": "locale",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_locale"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The ICCID is not an eSIM of your agency in this environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "iccid_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "iccid_not_found",
                          "type": "not_found",
                          "title": "Iccid not found",
                          "message": "No eSIM with this ICCID was found for your account.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#iccid_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The supplier cannot be reached. Retry in a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "supplier_unavailable": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_unavailable",
                          "type": "internal_error",
                          "title": "Service unavailable",
                          "message": "Airalo token unavailable",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#service_unavailable"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2"
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Setup and installation instructions",
            "path": "/docs/esims/instructions"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/balances": {
      "get": {
        "operationId": "listBalances",
        "tags": [
          "Account"
        ],
        "summary": "List balances",
        "description": "List the balance of your agency in each currency it holds.\n\nKeep the `id` of a balance: it is the `balance_id` of the balance history. Never send `agency_id`: the agency comes from the key and an API key that sends it is refused with `agency_id_not_allowed`.\n\nThe sandbox has no balances: `data` is empty and `meta` says so (`financialOperationsEnabled` is `false`). The live example below shows the shape of a live answer with made-up amounts.",
        "responses": {
          "200": {
            "description": "The balances of your agency, ordered by currency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountBalancesResponse"
                },
                "examples": {
                  "sandbox": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [],
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox",
                        "financialOperationsEnabled": false,
                        "financial_operations_enabled": false,
                        "message": "Sandbox bookings do not debit live balances; no sandbox balance ledger is exposed."
                      }
                    }
                  },
                  "live": {
                    "value": {
                      "environment": "live",
                      "success": true,
                      "data": [
                        {
                          "id": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "currency_code": "DZD",
                          "currency": "DZD",
                          "available_balance": 412500,
                          "created_at": "2025-08-22T10:24:06.533800+00:00",
                          "updated_at": "2026-10-17T04:45:15.697817+00:00"
                        },
                        {
                          "id": "a71c9e34-2b18-40df-9c77-6b0a1f2e5d40",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "currency_code": "EUR",
                          "currency": "EUR",
                          "available_balance": 18750.5,
                          "created_at": "2025-08-22T10:24:06.533800+00:00",
                          "updated_at": "2026-10-16T22:10:03.120400+00:00"
                        }
                      ],
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "live"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "An API key sent `agency_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "agency_id_not_allowed": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "agency_id_not_allowed",
                          "type": "validation_error",
                          "title": "Agency id not allowed",
                          "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter.",
                          "field": "agency_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#agency_id_not_allowed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "agency_id_not_allowed",
                        "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The service is shedding load for a moment (its database pool is full). Nothing was read: retry after the seconds in `Retry-After` (2 to 5).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                        "retry_after": 3,
                        "errors": [
                          {
                            "code": "service_overloaded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                            "title": "Service temporarily overloaded",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                          "status": 503,
                          "retry_after": 3
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "List balances",
            "path": "/docs/account/balances"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/balances/{balance_id}/history": {
      "get": {
        "operationId": "getBalanceHistory",
        "tags": [
          "Account"
        ],
        "summary": "Get the history of a balance",
        "description": "List the money movements recorded against one balance: top-ups, booking debits and refunds, newest first.\n\nEach entry has a `source`. `transactions` are the authoritative balance movements: sum only these for reconciliation. `flight_balance_operations` and `hotel_balance_operations` are audit records of one booking (initial debit, exchange fee, adjustment, refund); a single action produces both a transaction and an operation. eSIM orders and topups have no audit source: their debits are transactions.\n\n`type` filters on the `type` of the entry and is exact and case sensitive: `DEBIT` matches transactions (upper case) and `debit` matches operations (lower case). Paging is built from the newest 500 entries of each source, so older entries are not reachable: `pagination.total` counts the matching entries, but follow `has_more` to know whether another page can be served.\n\nIn the sandbox the answer is always empty. On live a `balance_id` that is not a balance of your agency answers `balance_not_found`.",
        "parameters": [
          {
            "name": "balance_id",
            "in": "path",
            "required": true,
            "description": "The `id` of a balance returned by `GET /balances`.",
            "schema": {
              "type": "string",
              "description": "A balance id.",
              "format": "uuid",
              "example": "db002f7a-c136-4f32-b19d-38a2542f2af9"
            }
          },
          {
            "name": "date_from",
            "in": "query",
            "required": false,
            "description": "Inclusive start. Any ISO 8601 spelling is read: a date (`2026-07-01` or `20260701`, taken as 00:00:00 UTC) or a datetime (`2026-07-01T10:30:00Z`) with 1 to 9 fraction digits and an offset written `Z`, `+01:00` or `+0100`. A datetime without an offset is read as UTC. Years 1900 to 2200. In a URL write a `+` as `%2B`.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "example": "2026-11-01"
            }
          },
          {
            "name": "date_to",
            "in": "query",
            "required": false,
            "description": "Inclusive end, in the same spellings as `date_from`. A bare date means the end of that day (23:59:59.999999 UTC). It must not be before `date_from`.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "example": "2026-11-30"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_from`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_from"
            }
          },
          {
            "name": "from_date",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_from`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_from"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_to`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_to"
            }
          },
          {
            "name": "to_date",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_to`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_to"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Keep only entries of this type. Exact and case sensitive: `CREDIT`, `DEBIT` or `REFUND` for transactions; `credit`, `debit` or `refund` for operations.",
            "schema": {
              "type": "string",
              "description": "A movement type.",
              "maxLength": 32,
              "example": "DEBIT"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "The page, starting at 1 (at most 10000). An empty value means not sent.",
            "schema": {
              "type": "integer",
              "description": "A page number.",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Entries per page, from 1 to 100. An empty value means not sent.",
            "schema": {
              "type": "integer",
              "description": "A page size.",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of movements.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceHistoryResponse"
                },
                "examples": {
                  "sandbox": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [],
                      "pagination": {
                        "page": 1,
                        "limit": 5,
                        "total": 0,
                        "total_pages": 0,
                        "hasMore": false,
                        "has_more": false
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox",
                        "financialOperationsEnabled": false,
                        "financial_operations_enabled": false,
                        "message": "Sandbox bookings skip debits, refunds, transactions, payouts, and balance operations."
                      }
                    }
                  },
                  "live": {
                    "value": {
                      "environment": "live",
                      "success": true,
                      "data": [
                        {
                          "source": "transactions",
                          "id": "3e8b1c02-d7a4-4f5e-9b2a-1c0d8e7f3a91",
                          "type": "CREDIT",
                          "amount": 2500,
                          "currency": "DZD",
                          "description": "Balance top-up",
                          "balanceId": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                          "balance_id": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                          "createdAt": "2026-10-11T09:15:00.000000+00:00",
                          "created_at": "2026-10-11T09:15:00.000000+00:00"
                        },
                        {
                          "source": "flight_balance_operations",
                          "id": "658185df-234c-43ba-8175-ba9a5abe6007",
                          "type": "debit",
                          "amount": 87.5,
                          "currency": "DZD",
                          "description": "Exchange fee debit for flight booking Y7PAEU",
                          "bookingRecordId": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
                          "booking_record_id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
                          "createdAt": "2026-10-09T14:30:00.000000+00:00",
                          "created_at": "2026-10-09T14:30:00.000000+00:00"
                        },
                        {
                          "source": "transactions",
                          "id": "31936df0-299d-429e-b0f5-056a6c0587cc",
                          "type": "DEBIT",
                          "amount": 4925.44,
                          "currency": "DZD",
                          "description": "Booking confirmation: flight Y7PAEU (ALG → ORN)",
                          "balanceId": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                          "balance_id": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                          "createdAt": "2026-10-01T15:42:34.272792+00:00",
                          "created_at": "2026-10-01T15:42:34.272792+00:00"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "limit": 20,
                        "total": 3,
                        "total_pages": 1,
                        "hasMore": false,
                        "has_more": false
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "balanceId": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                        "balance_id": "db002f7a-c136-4f32-b19d-38a2542f2af9",
                        "balanceCurrency": "DZD",
                        "balance_currency": "DZD",
                        "environment": "live"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A date is not valid, the range is reversed, or an API key sent `agency_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_date": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_date",
                          "type": "validation_error",
                          "title": "Invalid date",
                          "message": "Date parameters must be ISO-8601 values, for example 2026-05-08 or 2026-05-08T10:30:00Z.",
                          "field": "date_from",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_date"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_date",
                        "message": "Date parameters must be ISO-8601 values, for example 2026-05-08 or 2026-05-08T10:30:00Z."
                      }
                    }
                  },
                  "invalid_date_range": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_date_range",
                          "type": "validation_error",
                          "title": "Invalid date range",
                          "message": "date_from must not be after date_to.",
                          "field": "date_from",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_date_range"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_date_range",
                        "message": "date_from must not be after date_to."
                      }
                    }
                  },
                  "agency_id_not_allowed": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "agency_id_not_allowed",
                          "type": "validation_error",
                          "title": "Agency id not allowed",
                          "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter.",
                          "field": "agency_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#agency_id_not_allowed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "agency_id_not_allowed",
                        "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No balance of your agency has this id (live).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "balance_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "balance_not_found",
                          "type": "not_found",
                          "title": "Balance not found",
                          "message": "No balance found for ID 'db002f7a-c136-4f32-b19d-38a2542f2af9'.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#balance_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "balance_not_found",
                        "message": "No balance found for ID 'db002f7a-c136-4f32-b19d-38a2542f2af9'."
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`page` or `limit` is not an integer in its range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "limit_too_large": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Input should be less than or equal to 100",
                          "field": "limit",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "validation_error",
                        "message": "Input should be less than or equal to 100"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The service is shedding load for a moment (its database pool is full). Nothing was read: retry after the seconds in `Retry-After` (2 to 5).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                        "retry_after": 3,
                        "errors": [
                          {
                            "code": "service_overloaded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                            "title": "Service temporarily overloaded",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                          "status": 503,
                          "retry_after": 3
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Balance history",
            "path": "/docs/account/balance-history"
          }
        ],
        "x-sample-query": "limit=5",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/history": {
      "get": {
        "operationId": "listBookingHistory",
        "tags": [
          "Account"
        ],
        "summary": "List booking history",
        "description": "List the bookings of your agency across flights, hotels, eSIMs and transfers, newest first, as short summaries.\n\nUse it for status tracking, reconciliation and dashboards, and `GET /bookings/{booking_id}` for one booking in full. The list is limited to the environment of your key. eSIM orders and topups are both listed (`type` tells them apart).\n\n`pagination.total` counts the bookings that match. Each product contributes at most its 200 newest bookings to the pages, so at most 800 can be reached with `product=all`: follow `has_more`, and narrow the list with `date_from` and `date_to` to reach older bookings.\n\n`status` is an exact match on the status of the booking, in any case; spaces and hyphens are read as underscores (`On Hold` is `on_hold`). `pending` is accepted as another name of `awaiting_payment`, a held flight that is not paid. An unknown status returns an empty list.",
        "parameters": [
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Keep one product. Case does not matter. The singular names (`flight`, `hotel`, `esim`, `transfer`) are read as the plural.",
            "schema": {
              "type": "string",
              "description": "A product, in any case.",
              "enum": [
                "all",
                "esims",
                "flights",
                "hotels",
                "transfers"
              ],
              "default": "all"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Keep bookings with this status, for example `confirmed`, `awaiting_payment` (or `pending`), `cancelled`, `failed`, `refunded`. Case does not matter; spaces and hyphens are read as underscores (`On Hold` is `on_hold`). At most 32 characters.",
            "schema": {
              "type": "string",
              "description": "A status name.",
              "maxLength": 32,
              "example": "confirmed"
            }
          },
          {
            "name": "date_from",
            "in": "query",
            "required": false,
            "description": "Inclusive start. Any ISO 8601 spelling is read: a date (`2026-07-01` or `20260701`, taken as 00:00:00 UTC) or a datetime (`2026-07-01T10:30:00Z`) with 1 to 9 fraction digits and an offset written `Z`, `+01:00` or `+0100`. A datetime without an offset is read as UTC. Years 1900 to 2200. In a URL write a `+` as `%2B`.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "example": "2026-11-01"
            }
          },
          {
            "name": "date_to",
            "in": "query",
            "required": false,
            "description": "Inclusive end, in the same spellings as `date_from`. A bare date means the end of that day (23:59:59.999999 UTC). It must not be before `date_from`.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "example": "2026-11-30"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_from`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_from"
            }
          },
          {
            "name": "from_date",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_from`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_from"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_to`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_to"
            }
          },
          {
            "name": "to_date",
            "in": "query",
            "required": false,
            "description": "Previous name of `date_to`. It still works, with the same spellings, and the response carries a `deprecated_field` warning.",
            "schema": {
              "type": "string",
              "description": "A date or datetime.",
              "maxLength": 40,
              "deprecated": true,
              "x-replaced-by": "date_to"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "The page, starting at 1 (at most 10000). An empty value means not sent.",
            "schema": {
              "type": "integer",
              "description": "A page number.",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Entries per page, from 1 to 100. An empty value means not sent.",
            "schema": {
              "type": "integer",
              "description": "A page size.",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of booking summaries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingHistoryResponse"
                },
                "examples": {
                  "esims": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "product": "esims",
                          "id": "bd6ff7d7-392f-4d56-8dea-4f0f3dff0af9",
                          "bookingId": "FX-1793546625391",
                          "booking_id": "FX-1793546625391",
                          "status": "confirmed",
                          "environment": "sandbox",
                          "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agentId": null,
                          "agent_id": null,
                          "currency": "DZD",
                          "totalPrice": 468.84,
                          "total_price": 468.84,
                          "netPrice": 418.6,
                          "b2b_price": 418.6,
                          "agencyCommission": 50.24,
                          "agency_commission": 50.24,
                          "providerName": "Airalo",
                          "provider_name": "Airalo",
                          "supplierId": "airalo",
                          "supplier_id": "airalo",
                          "createdAt": "2026-11-01T15:23:45.424526+00:00",
                          "created_at": "2026-11-01T15:23:45.424526+00:00",
                          "updatedAt": "2026-11-01T15:23:45.424526+00:00",
                          "updated_at": "2026-11-01T15:23:45.424526+00:00",
                          "orderId": "20261101-106853",
                          "order_id": "20261101-106853",
                          "type": "topup",
                          "packageName": "1 GB - 7 days",
                          "package_name": "1 GB - 7 days",
                          "iccid": "8900000540463077957",
                          "customerEmail": "traveler@example.com",
                          "customer_email": "traveler@example.com",
                          "customer_first_name": "John",
                          "customer_last_name": "Smith",
                          "customer_phone": "+15555550123"
                        },
                        {
                          "product": "esims",
                          "id": "639417b4-b103-4ece-bd5d-850315322278",
                          "bookingId": "FX-1793546602916",
                          "booking_id": "FX-1793546602916",
                          "status": "confirmed",
                          "environment": "sandbox",
                          "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agentId": null,
                          "agent_id": null,
                          "currency": "DZD",
                          "totalPrice": 468.84,
                          "total_price": 468.84,
                          "netPrice": 418.6,
                          "b2b_price": 418.6,
                          "agencyCommission": 50.24,
                          "agency_commission": 50.24,
                          "providerName": "Airalo",
                          "provider_name": "Airalo",
                          "supplierId": "airalo",
                          "supplier_id": "airalo",
                          "createdAt": "2026-11-01T15:23:22.961228+00:00",
                          "created_at": "2026-11-01T15:23:22.961228+00:00",
                          "updatedAt": "2026-11-01T15:23:22.961228+00:00",
                          "updated_at": "2026-11-01T15:23:22.961228+00:00",
                          "orderId": "20261101-106852",
                          "order_id": "20261101-106852",
                          "type": "esim",
                          "packageName": "1 GB - 7 days • France",
                          "package_name": "1 GB - 7 days • France",
                          "iccid": "8900000540463077957",
                          "customerEmail": "traveler@example.com",
                          "customer_email": "traveler@example.com",
                          "customer_first_name": "John",
                          "customer_last_name": "Smith",
                          "customer_phone": "+15555550123"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "limit": 2,
                        "total": 80,
                        "total_pages": 40,
                        "hasMore": true,
                        "has_more": true
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox",
                        "product": "esims"
                      }
                    }
                  },
                  "flights_held": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "product": "flights",
                          "id": "8723790a-0417-46bb-8247-07254b214a4c",
                          "bookingId": "Y7INFJ",
                          "booking_id": "Y7INFJ",
                          "status": "awaiting_payment",
                          "environment": "sandbox",
                          "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agentId": null,
                          "agent_id": null,
                          "currency": "DZD",
                          "totalPrice": 32593.93,
                          "total_price": 32593.93,
                          "netPrice": 32561.36,
                          "b2b_price": 32561.36,
                          "agencyCommission": 32.57,
                          "agency_commission": 32.57,
                          "providerName": "Amadeus Algeria",
                          "provider_name": "Amadeus Algeria",
                          "supplierId": "amadeus_aqc_dz_1",
                          "supplier_id": "amadeus_aqc_dz_1",
                          "createdAt": "2026-11-01T14:31:20.248661+00:00",
                          "created_at": "2026-11-01T14:31:20.248661+00:00",
                          "updatedAt": "2026-11-01T14:31:22.611902+00:00",
                          "updated_at": "2026-11-01T14:31:22.611902+00:00",
                          "departureAirport": "ALG",
                          "arrivalAirport": "CDG",
                          "departureDate": "2026-12-15T07:15:00",
                          "arrivalDate": "2026-12-15T09:45:00",
                          "ticketNumber": null,
                          "requireAcd": false,
                          "require_acd": false,
                          "voucherUrl": "https://files.example.com/flight-bookings/Y7INFJ-JOHN-SMITH.pdf",
                          "voucherUrlWithPrice": "https://files.example.com/flight-bookings/Y7INFJ-JOHN-SMITH.pdf",
                          "voucherUrlNoPrice": "https://files.example.com/flight-bookings/Y7INFJ-JOHN-SMITH-no-price.pdf"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "limit": 1,
                        "total": 2,
                        "total_pages": 2,
                        "hasMore": true,
                        "has_more": true
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox",
                        "product": "flights"
                      }
                    }
                  },
                  "hotels": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "product": "hotels",
                          "id": "34a08cf3-dff6-45c6-bdae-882061aa78f5",
                          "bookingId": "HMVXX4ZQ3",
                          "booking_id": "HMVXX4ZQ3",
                          "status": "refunded",
                          "environment": "sandbox",
                          "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agentId": null,
                          "agent_id": null,
                          "currency": "DZD",
                          "totalPrice": 1484.97,
                          "total_price": 1484.97,
                          "netPrice": 1482,
                          "b2b_price": 1482,
                          "agencyCommission": 2.97,
                          "agency_commission": 2.97,
                          "providerName": "ETG - RateHawk",
                          "provider_name": "ETG - RateHawk",
                          "supplierId": "ratehawk",
                          "supplier_id": "ratehawk",
                          "createdAt": "2026-11-01T14:22:00.924125+00:00",
                          "created_at": "2026-11-01T14:22:00.924125+00:00",
                          "updatedAt": "2026-11-01T14:22:32.992144+00:00",
                          "updated_at": "2026-11-01T14:22:32.992144+00:00",
                          "hotelId": "test_hotel_do_not_book",
                          "hotelName": "Test Hotel (Do Not Book) test",
                          "checkinDate": "2026-12-01",
                          "checkoutDate": "2026-12-04",
                          "bookingMode": "standard",
                          "voucherUrl": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                          "voucherUrlWithPrice": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                          "voucherUrlNoPrice": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith-no-price.pdf"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "limit": 1,
                        "total": 86,
                        "total_pages": 86,
                        "hasMore": true,
                        "has_more": true
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox",
                        "product": "hotels"
                      }
                    }
                  },
                  "transfers": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": [
                        {
                          "product": "transfers",
                          "id": "4745c761-0737-421a-aaab-c90f9c0aca8f",
                          "bookingId": "VIA-1782555657-QCF",
                          "booking_id": "VIA-1782555657-QCF",
                          "status": "refunded",
                          "environment": "sandbox",
                          "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                          "agentId": "5d6e7f80-91a2-4b3c-8d4e-5f6071829304",
                          "agent_id": "5d6e7f80-91a2-4b3c-8d4e-5f6071829304",
                          "currency": "DZD",
                          "totalPrice": 15124.38,
                          "total_price": 15124.38,
                          "netPrice": 15002.38,
                          "b2b_price": 15002.38,
                          "agencyCommission": 122,
                          "agency_commission": 122,
                          "providerName": "AirportTransfer.com",
                          "provider_name": "AirportTransfer.com",
                          "supplierId": null,
                          "supplier_id": null,
                          "createdAt": "2026-06-27T10:20:59.188372+00:00",
                          "created_at": "2026-06-27T10:20:59.188372+00:00",
                          "updatedAt": "2026-06-27T10:29:28.684+00:00",
                          "updated_at": "2026-06-27T10:29:28.684+00:00",
                          "arrivalDate": "2026-08-14T16:00:00+00:00",
                          "flightNumber": "593",
                          "voucherUrl": null,
                          "receiptUrl": "https://files.example.com/transfer-bookings/VIA-1782555657-QCF.pdf"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "limit": 1,
                        "total": 2,
                        "total_pages": 2,
                        "hasMore": true,
                        "has_more": true
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox",
                        "product": "transfers"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`product` is not allowed, a date or the status is not valid, the range is reversed, or an API key sent `agency_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_product": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_product",
                          "type": "validation_error",
                          "title": "Invalid product",
                          "message": "product must be one of all, esims, flights, hotels, transfers.",
                          "field": "product",
                          "details": {
                            "allowed": [
                              "all",
                              "esims",
                              "flights",
                              "hotels",
                              "transfers"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_product"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_product",
                        "message": "product must be one of all, esims, flights, hotels, transfers."
                      }
                    }
                  },
                  "invalid_date": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_date",
                          "type": "validation_error",
                          "title": "Invalid date",
                          "message": "Date parameters must be ISO-8601 values, for example 2026-05-08 or 2026-05-08T10:30:00Z.",
                          "field": "date_from",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_date"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_date",
                        "message": "Date parameters must be ISO-8601 values, for example 2026-05-08 or 2026-05-08T10:30:00Z."
                      }
                    }
                  },
                  "invalid_date_range": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_date_range",
                          "type": "validation_error",
                          "title": "Invalid date range",
                          "message": "date_from must not be after date_to.",
                          "field": "date_from",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_date_range"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_date_range",
                        "message": "date_from must not be after date_to."
                      }
                    }
                  },
                  "agency_id_not_allowed": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "agency_id_not_allowed",
                          "type": "validation_error",
                          "title": "Agency id not allowed",
                          "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter.",
                          "field": "agency_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#agency_id_not_allowed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "agency_id_not_allowed",
                        "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`page` or `limit` is not an integer in its range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "limit_too_large": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "validation_error",
                          "type": "validation_error",
                          "title": "Validation error",
                          "message": "Input should be less than or equal to 100",
                          "field": "limit",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#validation_error"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 422
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "validation_error",
                        "message": "Input should be less than or equal to 100"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The service is shedding load for a moment (its database pool is full). Nothing was read: retry after the seconds in `Retry-After` (2 to 5).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                        "retry_after": 3,
                        "errors": [
                          {
                            "code": "service_overloaded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                            "title": "Service temporarily overloaded",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                          "status": 503,
                          "retry_after": 3
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Booking history",
            "path": "/docs/account/history"
          }
        ],
        "x-sample-query": "product=esims&limit=2",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/bookings/{booking_id}": {
      "get": {
        "operationId": "getAccountBooking",
        "tags": [
          "Account"
        ],
        "summary": "Get a booking",
        "description": "Get one booking of your agency, whatever its product: the summary of the history, the stored record and, for flights and hotels, the price breakdown and the balance operations.\n\nThe reference is looked up in these fields, in this order, and the first booking found is returned. Flights: `booking_id` (the record locator), the supplier order id, `id`. Hotels: `booking_id`, `id`. eSIMs: `booking_id` (`FX-...`), the supplier order code, the ICCID, `id`. Transfers: `booking_id`, `id`. Without `product` the products are tried in the order flights, hotels, eSIMs, transfers.\n\nPrefer the internal `id` (a UUID): record locators are not unique across suppliers. An ICCID matches the original order of the eSIM, not its topups: look a topup up by its `booking_id` or its `id`. A reference that does not match the `product` you ask for is `booking_not_found`.",
        "parameters": [
          {
            "name": "booking_id",
            "in": "path",
            "required": true,
            "description": "The booking reference: the internal `id` or the public reference of the product (see above).",
            "schema": {
              "type": "string",
              "description": "A booking reference.",
              "maxLength": 100,
              "example": "FX-1793546602916"
            }
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "description": "Look in one product only, which is faster and avoids a match in another product. Case does not matter. The singular names (`flight`, `hotel`, `esim`, `transfer`) are read as the plural.",
            "schema": {
              "type": "string",
              "description": "A product, in any case.",
              "enum": [
                "all",
                "esims",
                "flights",
                "hotels",
                "transfers"
              ],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The booking. The keys after `updatedAt` and in `record` depend on the product.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountBookingDetailResponse"
                },
                "examples": {
                  "esims": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "product": "esims",
                        "id": "639417b4-b103-4ece-bd5d-850315322278",
                        "bookingId": "FX-1793546602916",
                        "booking_id": "FX-1793546602916",
                        "status": "confirmed",
                        "environment": "sandbox",
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agentId": null,
                        "agent_id": null,
                        "currency": "DZD",
                        "totalPrice": 468.84,
                        "total_price": 468.84,
                        "netPrice": 418.6,
                        "b2b_price": 418.6,
                        "agencyCommission": 50.24,
                        "agency_commission": 50.24,
                        "providerName": "Airalo",
                        "provider_name": "Airalo",
                        "supplierId": "airalo",
                        "supplier_id": "airalo",
                        "createdAt": "2026-11-01T15:23:22.961228+00:00",
                        "created_at": "2026-11-01T15:23:22.961228+00:00",
                        "updatedAt": "2026-11-01T15:23:22.961228+00:00",
                        "updated_at": "2026-11-01T15:23:22.961228+00:00",
                        "orderId": "20261101-106852",
                        "order_id": "20261101-106852",
                        "type": "esim",
                        "packageName": "1 GB - 7 days • France",
                        "package_name": "1 GB - 7 days • France",
                        "iccid": "8900000540463077957",
                        "customerEmail": "traveler@example.com",
                        "customer_email": "traveler@example.com",
                        "customer_first_name": "John",
                        "customer_last_name": "Smith",
                        "customer_phone": "+15555550123",
                        "record": {
                          "id": "639417b4-b103-4ece-bd5d-850315322278",
                          "booking_id": "FX-1793546602916",
                          "order_id": "20261101-106852",
                          "status": "confirmed",
                          "currency": "DZD",
                          "total_price": 468.84,
                          "agency_commission": 50.24,
                          "type": "esim",
                          "package_name": "1 GB - 7 days • France",
                          "iccid": "8900000540463077957",
                          "customer_email": "traveler@example.com",
                          "customer_first_name": "John",
                          "customer_last_name": "Smith",
                          "customer_phone": "+15555550123",
                          "provider_name": "Airalo",
                          "supplier_id": "airalo",
                          "created_at": "2026-11-01T15:23:22.961228+00:00",
                          "updated_at": "2026-11-01T15:23:22.961228+00:00",
                          "environment": "sandbox"
                        }
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox"
                      }
                    }
                  },
                  "flights": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "product": "flights",
                        "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
                        "bookingId": "Y7PAEU",
                        "booking_id": "Y7PAEU",
                        "status": "confirmed",
                        "environment": "sandbox",
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agentId": null,
                        "agent_id": null,
                        "currency": "DZD",
                        "totalPrice": 4930.37,
                        "total_price": 4930.37,
                        "netPrice": 4925.44,
                        "b2b_price": 4925.44,
                        "agencyCommission": 4.93,
                        "agency_commission": 4.93,
                        "providerName": "Amadeus Algeria",
                        "provider_name": "Amadeus Algeria",
                        "supplierId": "amadeus_aqc_dz_1",
                        "supplier_id": "amadeus_aqc_dz_1",
                        "createdAt": "2026-11-01T15:26:14.871035+00:00",
                        "created_at": "2026-11-01T15:26:14.871035+00:00",
                        "updatedAt": "2026-11-01T15:26:23.505356+00:00",
                        "updated_at": "2026-11-01T15:26:23.505356+00:00",
                        "departureAirport": "ALG",
                        "arrivalAirport": "ORN",
                        "departureDate": "2026-12-22T19:45:00",
                        "arrivalDate": "2026-12-22T20:45:00",
                        "ticketNumber": "124-2446977397",
                        "requireAcd": false,
                        "require_acd": false,
                        "voucherUrl": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed.pdf",
                        "voucherUrlWithPrice": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed.pdf",
                        "voucherUrlNoPrice": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed-no-price.pdf",
                        "record": {
                          "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
                          "booking_id": "Y7PAEU",
                          "status": "confirmed",
                          "currency": "DZD",
                          "total_price": 4930.37,
                          "agency_commission": 4.93,
                          "departure_airport": "ALG",
                          "arrival_airport": "ORN",
                          "departure_date": "2026-12-22T19:45:00",
                          "arrival_date": "2026-12-22T20:45:00",
                          "return_departure_date": null,
                          "return_arrival_date": null,
                          "included_checked_bags": "0",
                          "ticket_number": "124-2446977397",
                          "require_acd": false,
                          "bucket_link": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed.pdf",
                          "bucket_link_no_price": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed-no-price.pdf",
                          "refund_amount": null,
                          "refund_currency": null,
                          "refund_method": null,
                          "paid_at": "2026-11-01T15:26:14.842190+00:00",
                          "voided_at": null,
                          "created_at": "2026-11-01T15:26:14.871035+00:00",
                          "updated_at": "2026-11-01T15:26:23.505356+00:00",
                          "environment": "sandbox"
                        },
                        "pricing": [
                          {
                            "supplier_currency": "DZD",
                            "agency_currency": "DZD",
                            "base_price": 3160,
                            "taxes_and_fees": 1765.44,
                            "agency_commission": 4.93,
                            "final_price": 4930.37,
                            "b2b_price": 4925.44,
                            "created_at": "2026-11-01T15:26:22.176516+00:00",
                            "environment": "sandbox"
                          }
                        ],
                        "travelerPricing": [
                          {
                            "traveler_id": "1",
                            "traveler_type": "ADULT",
                            "fare_option": "STANDARD",
                            "supplier_currency": "DZD",
                            "agency_currency": "DZD",
                            "base": 3160,
                            "total": 4930.37,
                            "b2b_price": 4925.44,
                            "created_at": "2026-11-01T15:26:15.133066+00:00",
                            "environment": "sandbox"
                          }
                        ],
                        "traveler_pricing": [
                          {
                            "traveler_id": "1",
                            "traveler_type": "ADULT",
                            "fare_option": "STANDARD",
                            "supplier_currency": "DZD",
                            "agency_currency": "DZD",
                            "base": 3160,
                            "total": 4930.37,
                            "b2b_price": 4925.44,
                            "created_at": "2026-11-01T15:26:15.133066+00:00",
                            "environment": "sandbox"
                          }
                        ],
                        "balanceOperations": [],
                        "balance_operations": []
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox"
                      }
                    }
                  },
                  "hotels": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "product": "hotels",
                        "id": "34a08cf3-dff6-45c6-bdae-882061aa78f5",
                        "bookingId": "HMVXX4ZQ3",
                        "booking_id": "HMVXX4ZQ3",
                        "status": "refunded",
                        "environment": "sandbox",
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agentId": null,
                        "agent_id": null,
                        "currency": "DZD",
                        "totalPrice": 1484.97,
                        "total_price": 1484.97,
                        "netPrice": 1482,
                        "b2b_price": 1482,
                        "agencyCommission": 2.97,
                        "agency_commission": 2.97,
                        "providerName": "ETG - RateHawk",
                        "provider_name": "ETG - RateHawk",
                        "supplierId": "ratehawk",
                        "supplier_id": "ratehawk",
                        "createdAt": "2026-11-01T14:22:00.924125+00:00",
                        "created_at": "2026-11-01T14:22:00.924125+00:00",
                        "updatedAt": "2026-11-01T14:22:32.992144+00:00",
                        "updated_at": "2026-11-01T14:22:32.992144+00:00",
                        "hotelId": "test_hotel_do_not_book",
                        "hotelName": "Test Hotel (Do Not Book) test",
                        "checkinDate": "2026-12-01",
                        "checkoutDate": "2026-12-04",
                        "bookingMode": "standard",
                        "voucherUrl": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                        "voucherUrlWithPrice": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                        "voucherUrlNoPrice": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith-no-price.pdf",
                        "record": {
                          "id": "34a08cf3-dff6-45c6-bdae-882061aa78f5",
                          "booking_id": "HMVXX4ZQ3",
                          "status": "refunded",
                          "currency": "DZD",
                          "total_price": 1484.97,
                          "agency_commission": 2.97,
                          "hotel_id": "test_hotel_do_not_book",
                          "hotel_name": "Test Hotel (Do Not Book) test",
                          "checkin_date": "2026-12-01",
                          "checkout_date": "2026-12-04",
                          "booking_mode": "standard",
                          "bucket_link": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith.pdf",
                          "bucket_link_no_price": "https://files.example.com/hotel-bookings/HMVXX4ZQ3-Jane-Smith-no-price.pdf",
                          "free_cancellation_deadline_at": null,
                          "paid_at": null,
                          "supplier_id": "ratehawk",
                          "created_at": "2026-11-01T14:22:00.924125+00:00",
                          "updated_at": "2026-11-01T14:22:32.992144+00:00",
                          "environment": "sandbox",
                          "refund_amount": 1482,
                          "refund_currency": "DZD"
                        },
                        "pricing": [
                          {
                            "id": "72db7216-177e-4c5b-bde6-74c5ca3c8db2",
                            "created_at": "2026-11-01T14:22:05.162117+00:00",
                            "agency_currency": "DZD",
                            "total_price": 1484.97,
                            "agency_commission": 2.97,
                            "environment": "sandbox",
                            "b2b_price": 1482
                          }
                        ],
                        "balanceOperations": [],
                        "balance_operations": []
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox"
                      }
                    }
                  },
                  "transfers": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "data": {
                        "product": "transfers",
                        "id": "4745c761-0737-421a-aaab-c90f9c0aca8f",
                        "bookingId": "VIA-1782555657-QCF",
                        "booking_id": "VIA-1782555657-QCF",
                        "status": "refunded",
                        "environment": "sandbox",
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agentId": "5d6e7f80-91a2-4b3c-8d4e-5f6071829304",
                        "agent_id": "5d6e7f80-91a2-4b3c-8d4e-5f6071829304",
                        "currency": "DZD",
                        "totalPrice": 15124.38,
                        "total_price": 15124.38,
                        "netPrice": 15002.38,
                        "b2b_price": 15002.38,
                        "agencyCommission": 122,
                        "agency_commission": 122,
                        "providerName": "AirportTransfer.com",
                        "provider_name": "AirportTransfer.com",
                        "supplierId": null,
                        "supplier_id": null,
                        "createdAt": "2026-06-27T10:20:59.188372+00:00",
                        "created_at": "2026-06-27T10:20:59.188372+00:00",
                        "updatedAt": "2026-06-27T10:29:28.684+00:00",
                        "updated_at": "2026-06-27T10:29:28.684+00:00",
                        "arrivalDate": "2026-08-14T16:00:00+00:00",
                        "flightNumber": "593",
                        "voucherUrl": null,
                        "receiptUrl": "https://files.example.com/transfer-bookings/VIA-1782555657-QCF.pdf",
                        "record": {
                          "id": "4745c761-0737-421a-aaab-c90f9c0aca8f",
                          "booking_id": "VIA-1782555657-QCF",
                          "status": "refunded",
                          "currency": "DZD",
                          "total_price": 15124.38,
                          "agency_commission": 122,
                          "arrival_date": "2026-08-14T16:00:00+00:00",
                          "flight_number": "593",
                          "provider_name": "AirportTransfer.com",
                          "bucket_link": "https://files.example.com/transfer-bookings/VIA-1782555657-QCF.pdf",
                          "voucher_link": null,
                          "refunded_amount": 15002.38,
                          "refunded_currency": "DZD",
                          "created_at": "2026-06-27T10:20:59.188372+00:00",
                          "updated_at": "2026-06-27T10:29:28.684+00:00",
                          "environment": "sandbox"
                        }
                      },
                      "meta": {
                        "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
                        "environment": "sandbox"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`product` is not allowed, the reference is not a booking id, or an API key sent `agency_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_product": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_product",
                          "type": "validation_error",
                          "title": "Invalid product",
                          "message": "product must be one of all, esims, flights, hotels, transfers.",
                          "field": "product",
                          "details": {
                            "allowed": [
                              "all",
                              "esims",
                              "flights",
                              "hotels",
                              "transfers"
                            ]
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_product"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_product",
                        "message": "product must be one of all, esims, flights, hotels, transfers."
                      }
                    }
                  },
                  "invalid_booking_id": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "invalid_booking_id",
                          "type": "validation_error",
                          "title": "Invalid booking id",
                          "message": "booking_id is not a valid booking reference (received '!').",
                          "field": "booking_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#invalid_booking_id"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "invalid_booking_id",
                        "message": "booking_id is not a valid booking reference (received '!')."
                      }
                    }
                  },
                  "agency_id_not_allowed": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "agency_id_not_allowed",
                          "type": "validation_error",
                          "title": "Agency id not allowed",
                          "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter.",
                          "field": "agency_id",
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#agency_id_not_allowed"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 400
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "agency_id_not_allowed",
                        "message": "External API keys resolve agency_id automatically; do not send agency_id as a query parameter."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No booking of your agency in this environment has this reference, or it belongs to another product than `product`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "booking_not_found": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "booking_not_found",
                          "type": "not_found",
                          "title": "Booking not found",
                          "message": "No sandbox booking found for 'FX-0000000000000'.",
                          "field": null,
                          "details": {},
                          "documentation_url": "https://fx-port.com/docs/errors#booking_not_found"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 404
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "booking_not_found",
                        "message": "No sandbox booking found for 'FX-0000000000000'."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The service is shedding load for a moment (its database pool is full). Nothing was read: retry after the seconds in `Retry-After` (2 to 5).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "service_overloaded": {
                    "value": {
                      "environment": "sandbox",
                      "success": false,
                      "errors": [
                        {
                          "code": "service_overloaded",
                          "type": "internal_error",
                          "title": "Service overloaded",
                          "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                          "field": null,
                          "details": {
                            "retry_after": 3
                          },
                          "documentation_url": "https://fx-port.com/docs/errors#service_overloaded"
                        }
                      ],
                      "meta": {
                        "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                        "status": 503
                      },
                      "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                      "detail": {
                        "error": "service_overloaded",
                        "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                        "retry_after": 3,
                        "errors": [
                          {
                            "code": "service_overloaded",
                            "documentation_url": "https://fx-port.com/docs/rate-limits",
                            "message": "The service is temporarily overloaded; retry after the time given in the Retry-After header.",
                            "title": "Service temporarily overloaded",
                            "type": "api_error"
                          }
                        ],
                        "meta": {
                          "request_id": "req_7f0c2e9a41b84d6e8a35c1d2",
                          "status": 503,
                          "retry_after": 3
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Booking detail",
            "path": "/docs/account/booking-detail"
          }
        ],
        "x-sample-query": "product=esims",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/suppliers": {
      "get": {
        "operationId": "listSuppliers",
        "tags": [
          "Suppliers"
        ],
        "summary": "List suppliers",
        "description": "List the suppliers that are enabled for your key, grouped by product: flights, hotels and eSIMs.\n\nA product with no enabled supplier is left out, and so are disabled suppliers. The list can change at any time: read it, do not hard-code it. Flight suppliers carry `routes`, `capabilities` and `void_deadline`; hotel and eSIM suppliers only `id`, `name`, `technology` and `description`.\n\n`void_deadline` is the next midnight in the supplier's time zone, in UTC, recalculated on every call: do not keep it across days. The supplier `id` is the one to send as `supplier_id` where an endpoint takes one. The bookings of your account carry their own `supplierId`, which can differ (`ratehawk` for `worldota_1`, `airalo` for `airalo_1`).",
        "responses": {
          "200": {
            "description": "The enabled suppliers, by product.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppliersResponse"
                },
                "examples": {
                  "sandbox": {
                    "value": {
                      "environment": "sandbox",
                      "success": true,
                      "products": [
                        {
                          "product": "flights",
                          "suppliers": [
                            {
                              "id": "amadeus_aqc_dz_1",
                              "name": "Amadeus Algeria",
                              "technology": "amadeus_aqc",
                              "routes": [
                                "DZ"
                              ],
                              "description": "Operates on routes from and to Algeria using a dedicated Algiers GDS office with negotiated fares. Benefits from an advantageous DZD exchange rate that typically results in significantly lower prices on outbound Algeria routes ...",
                              "capabilities": {
                                "hold": true,
                                "void": true,
                                "flexible_dates": true,
                                "ancillaries": false
                              },
                              "voidDeadline": "2026-11-01T23:00:00Z",
                              "void_deadline": "2026-11-01T23:00:00Z"
                            },
                            {
                              "id": "duffel_1",
                              "name": "Duffel",
                              "technology": "duffel",
                              "routes": [
                                "*"
                              ],
                              "description": "TravelPort-connected supplier with broad global route coverage across hundreds of airlines at published fares. Particularly competitive on European, Middle Eastern, and long-haul routes ...",
                              "capabilities": {
                                "hold": false,
                                "void": false,
                                "flexible_dates": false,
                                "ancillaries": true
                              },
                              "voidDeadline": "2026-11-02T00:00:00Z",
                              "void_deadline": "2026-11-02T00:00:00Z"
                            }
                          ]
                        },
                        {
                          "product": "hotels",
                          "suppliers": [
                            {
                              "id": "worldota_1",
                              "name": "RateHawk",
                              "technology": "worldota",
                              "description": "Aggregates hotel inventory from global hotel chains and independent properties across leisure and business destinations worldwide. Broad coverage across leisure and business destinations with real-time availability and net rates."
                            }
                          ]
                        },
                        {
                          "product": "esims",
                          "suppliers": [
                            {
                              "id": "airalo_1",
                              "name": "Airalo",
                              "technology": "airalo",
                              "description": "Global eSIM data provider with coverage across 190+ countries and regions. Data plans are activated instantly and the eSIM QR code is delivered directly to the customer's email address after purchase."
                            }
                          ]
                        }
                      ],
                      "meta": {
                        "environment": "sandbox",
                        "permissionRequired": "read",
                        "permission_required": "read"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, unknown or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More requests than the limit of this endpoint group per minute. Wait for the number of seconds in `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error on our side. Quote the `request_id` when you contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-permission": "read",
        "x-guides": [
          {
            "label": "Suppliers",
            "path": "/docs/suppliers"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Environment": {
        "type": "string",
        "enum": [
          "sandbox",
          "live"
        ],
        "description": "The environment of the key that made the call: `sandbox` for fxp_test_ keys, `live` for fxp_live_ keys. Present in every JSON body.",
        "example": "sandbox"
      },
      "Warning": {
        "type": "object",
        "description": "A notice about the request that did not stop it: a previous spelling of a field that still works, a field the endpoint ignores, or a value that was adjusted. Listed in `warnings` of the response; the headers `Deprecation`, `X-FX-Deprecated-Fields` and `X-FX-Ignored-Fields` carry the same information.",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "deprecated_field",
              "ignored_field",
              "coerced_value",
              "deprecated_value",
              "deprecated_endpoint"
            ],
            "description": "What the warning is about."
          },
          "field": {
            "type": "string",
            "description": "The request field the warning is about."
          },
          "replacement": {
            "type": "string",
            "description": "The name to use instead, for `deprecated_*` warnings."
          },
          "message": {
            "type": "string",
            "description": "A sentence that says what happened and what to do."
          },
          "documentation_url": {
            "type": "string",
            "description": "Where the naming rules are explained."
          }
        },
        "required": [
          "code"
        ]
      },
      "ErrorItem": {
        "type": "object",
        "description": "One error.",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable, machine-readable code in snake_case. Branch on this, never on the message.",
            "example": "invalid_airport_code"
          },
          "type": {
            "type": "string",
            "enum": [
              "authentication_error",
              "permission_error",
              "validation_error",
              "not_found",
              "conflict",
              "payment_error",
              "rate_limit_error",
              "supplier_error",
              "internal_error"
            ],
            "description": "The family of the error: tells you how to react."
          },
          "title": {
            "type": "string",
            "description": "A short label of the error."
          },
          "message": {
            "type": "string",
            "description": "A sentence for a human: what is wrong and how to fix it. Wording can change; the code does not."
          },
          "field": {
            "anyOf": [
              {
                "type": "string",
                "description": "The request field the error is about, when there is one."
              },
              {
                "type": "null"
              }
            ],
            "description": "The request field the error is about, when there is one."
          },
          "details": {
            "anyOf": [
              {
                "description": "Structured extras of the error (for example `available` and `required` for an insufficient balance).",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Structured extras of the error (for example `available` and `required` for an insufficient balance)."
          },
          "documentation_url": {
            "type": "string",
            "description": "The documentation of this error code."
          }
        },
        "required": [
          "code",
          "type",
          "message"
        ]
      },
      "Error": {
        "type": "object",
        "description": "The body of every failed call.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "success": {
            "type": "boolean",
            "description": "Always `false` for an error."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "The errors, most important first. Parse this array; the other keys are the older shapes of the same error and stay for existing integrations."
          },
          "meta": {
            "type": "object",
            "description": "The id of the request and the HTTP status, repeated for clients that only read the body.",
            "properties": {
              "request_id": {
                "type": "string",
                "description": "The id of the request."
              },
              "status": {
                "type": "integer",
                "description": "The HTTP status code."
              }
            },
            "required": [
              "request_id",
              "status"
            ]
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header. Quote it when you write to support."
          },
          "detail": {
            "description": "The older error body (a string, an object with `error` and `message`, or a list of validation errors).",
            "additionalProperties": true,
            "type": "object",
            "deprecated": true,
            "x-replaced-by": "errors"
          },
          "error": {
            "description": "The older error code or text.",
            "additionalProperties": true,
            "type": "object",
            "deprecated": true,
            "x-replaced-by": "errors[].code"
          },
          "message": {
            "type": "string",
            "description": "The older error message.",
            "deprecated": true,
            "x-replaced-by": "errors[].message"
          },
          "code": {
            "type": "string",
            "description": "The older error code.",
            "deprecated": true,
            "x-replaced-by": "errors[].code"
          }
        },
        "required": [
          "success",
          "errors",
          "request_id"
        ]
      },
      "FlightError": {
        "type": "object",
        "description": "The body of a failed call. Parse `errors[0].code`; the older keys stay for integrations written before it.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "success": {
            "type": "boolean",
            "description": "Always `false`."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "The unified error list: parse this. `errors[0].code` is the stable code, listed in the table of error codes."
          },
          "meta": {
            "type": "object",
            "description": "The id of the request and the HTTP status. Bodies of `book_flight` keep the capabilities of the supplier here too.",
            "properties": {
              "request_id": {
                "type": "string",
                "description": "The id of the request."
              },
              "status": {
                "type": "integer",
                "description": "The HTTP status code."
              }
            },
            "additionalProperties": true
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request. Quote it when you write to support."
          },
          "requestId": {
            "type": "string",
            "description": "The 8-character id of the search or booking request, in the older bodies of the flights endpoints.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "detail": {
            "anyOf": [
              {
                "type": "string",
                "description": "A text."
              },
              {
                "description": "An object with `error` (the code), `message` and sometimes `field`.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "array",
                "items": {
                  "description": "One problem: `type`, `loc`, `msg`.",
                  "additionalProperties": true,
                  "type": "object"
                }
              }
            ],
            "description": "The older body: a text, an object `{ error, message, field }` or the list of problems of a malformed query.",
            "deprecated": true,
            "x-replaced-by": "errors"
          },
          "error": {
            "anyOf": [
              {
                "type": "string",
                "description": "A text."
              },
              {
                "description": "An object.",
                "additionalProperties": true,
                "type": "object"
              }
            ],
            "description": "The older error code (`TRAVELER_RULE_VIOLATION`) or, for a supplier's refusal, an object `{ message, code, type, details }`.",
            "deprecated": true,
            "x-replaced-by": "errors[].code"
          },
          "message": {
            "type": "string",
            "description": "The older message of a flat body.",
            "deprecated": true,
            "x-replaced-by": "errors[].message"
          },
          "code": {
            "type": "string",
            "description": "The older code of a flat body.",
            "deprecated": true,
            "x-replaced-by": "errors[].code"
          },
          "violations": {
            "type": "array",
            "items": {
              "description": "One violated rule: `code`, `message`.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "`TRAVELER_RULE_VIOLATION` only: the rules the passengers break."
          },
          "supplierErrors": {
            "type": "array",
            "items": {
              "description": "One failed supplier: `supplierId`, `cause`, `message`.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "`all_suppliers_failed` only: why each supplier failed."
          }
        },
        "additionalProperties": true
      },
      "FlightPassengers": {
        "type": "object",
        "description": "How many travelers of each type. Every field is optional. At most 9 seated passengers (adults, seniors, young adults, children and seated infants together), and not more lap or seated infants than adults and seniors. Counts and ages are JSON integers: numeric text and integral floats (`\"2\"`, `2.0`) are read with a `coerced_value` warning, an empty text or `null` means not sent, and words, fractions, booleans, negatives and counts above 99 are refused.",
        "properties": {
          "adults": {
            "type": "integer",
            "description": "Adults (12 or older). The default is one adult when nothing is sent.",
            "minimum": 0,
            "maximum": 99
          },
          "seniors": {
            "type": "integer",
            "description": "Seniors.",
            "minimum": 0,
            "maximum": 99
          },
          "young_adults": {
            "type": "integer",
            "description": "Young adults (12 to 17).",
            "minimum": 0,
            "maximum": 99
          },
          "children": {
            "type": "integer",
            "description": "Children (2 to 11). Without `child_ages` every child is searched at age 11.",
            "minimum": 0,
            "maximum": 99
          },
          "seated_infants": {
            "type": "integer",
            "description": "Infants under 2 with their own seat.",
            "minimum": 0,
            "maximum": 99
          },
          "infants": {
            "type": "integer",
            "description": "Lap infants under 2: no seat, at most as many as adults and seniors.",
            "minimum": 0,
            "maximum": 99
          },
          "child_ages": {
            "type": "array",
            "items": {
              "type": "integer",
              "description": "An age from 2 to 11."
            },
            "description": "The age of each child. The ages win over the counts: a disagreement is adjusted and reported in a `coerced_value` warning. A text list (`\"7,9\"`) or one bare number is read as a list, also with a warning."
          },
          "young_adult_ages": {
            "type": "array",
            "items": {
              "type": "integer",
              "description": "An age from 12 to 17."
            },
            "description": "The age of each young adult."
          },
          "seated_infant_ages": {
            "type": "array",
            "items": {
              "type": "integer",
              "description": "An age from 0 to 1."
            },
            "description": "The age of each seated infant."
          },
          "infant_ages": {
            "type": "array",
            "items": {
              "type": "integer",
              "description": "An age from 0 to 1."
            },
            "description": "The age of each lap infant. Without it every lap infant is searched at age 0, with no warning."
          },
          "young": {
            "type": "integer",
            "description": "The previous spelling of `young_adults`: Young adults (12 to 17).",
            "deprecated": true,
            "x-replaced-by": "young_adults"
          },
          "youngAdults": {
            "type": "integer",
            "description": "The previous spelling of `young_adults`: Young adults (12 to 17).",
            "deprecated": true,
            "x-replaced-by": "young_adults"
          },
          "toddlers": {
            "type": "integer",
            "description": "The previous spelling of `seated_infants`: Infants under 2 with their own seat.",
            "deprecated": true,
            "x-replaced-by": "seated_infants"
          },
          "seatedInfants": {
            "type": "integer",
            "description": "The previous spelling of `seated_infants`: Infants under 2 with their own seat.",
            "deprecated": true,
            "x-replaced-by": "seated_infants"
          },
          "childAges": {
            "type": "array",
            "description": "The previous spelling of `child_ages`: The age of each child.",
            "deprecated": true,
            "x-replaced-by": "child_ages"
          },
          "youngAdultAges": {
            "type": "array",
            "description": "The previous spelling of `young_adult_ages`: The age of each young adult.",
            "deprecated": true,
            "x-replaced-by": "young_adult_ages"
          },
          "seatedInfantAges": {
            "type": "array",
            "description": "The previous spelling of `seated_infant_ages`: The age of each seated infant.",
            "deprecated": true,
            "x-replaced-by": "seated_infant_ages"
          },
          "infantAges": {
            "type": "array",
            "description": "The previous spelling of `infant_ages`: The age of each lap infant.",
            "deprecated": true,
            "x-replaced-by": "infant_ages"
          }
        }
      },
      "FlightSegmentRequest": {
        "type": "object",
        "description": "One flight of an open-jaw or multi-city search.",
        "properties": {
          "origin": {
            "type": "string",
            "description": "Departure IATA code (three letters). A segment takes one code, not a list.",
            "example": "ALG"
          },
          "destination": {
            "type": "string",
            "description": "Arrival IATA code. Must differ from `origin`.",
            "example": "CDG"
          },
          "departure_date": {
            "type": "string",
            "description": "Local departure date, `YYYY-MM-DD`, up to 365 days ahead. The dates must not go backwards.",
            "example": "2026-12-15"
          },
          "date": {
            "type": "string",
            "description": "The previous spelling of `departure_date`: Local departure date, `YYYY-MM-DD`, up to 365 days ahead.",
            "deprecated": true,
            "x-replaced-by": "departure_date"
          },
          "departureDate": {
            "type": "string",
            "description": "The previous spelling of `departure_date`: Local departure date, `YYYY-MM-DD`, up to 365 days ahead.",
            "deprecated": true,
            "x-replaced-by": "departure_date"
          },
          "departure": {
            "type": "string",
            "description": "The previous spelling of `departure_date`: Local departure date, `YYYY-MM-DD`, up to 365 days ahead.",
            "deprecated": true,
            "x-replaced-by": "departure_date"
          }
        },
        "required": [
          "origin",
          "destination",
          "departure_date"
        ]
      },
      "FlightLoyaltyAccount": {
        "type": "object",
        "description": "A frequent-flyer account, stored with the search and matched by passenger name at booking.",
        "properties": {
          "airline_iata_code": {
            "type": "string",
            "description": "IATA code of the airline of the programme."
          },
          "account_number": {
            "type": "string",
            "description": "The frequent-flyer number."
          },
          "first_name": {
            "type": "string",
            "description": "Given name of the member, matched with the passenger at booking."
          },
          "last_name": {
            "type": "string",
            "description": "Family name of the member."
          },
          "airlineIataCode": {
            "type": "string",
            "description": "The previous spelling of `airline_iata_code`: IATA code of the airline of the programme.",
            "deprecated": true,
            "x-replaced-by": "airline_iata_code"
          },
          "airline_code": {
            "type": "string",
            "description": "The previous spelling of `airline_iata_code`: IATA code of the airline of the programme.",
            "deprecated": true,
            "x-replaced-by": "airline_iata_code"
          },
          "airlineCode": {
            "type": "string",
            "description": "The previous spelling of `airline_iata_code`: IATA code of the airline of the programme.",
            "deprecated": true,
            "x-replaced-by": "airline_iata_code"
          },
          "programOwner": {
            "type": "string",
            "description": "The previous spelling of `airline_iata_code`: IATA code of the airline of the programme.",
            "deprecated": true,
            "x-replaced-by": "airline_iata_code"
          },
          "program_owner": {
            "type": "string",
            "description": "The previous spelling of `airline_iata_code`: IATA code of the airline of the programme.",
            "deprecated": true,
            "x-replaced-by": "airline_iata_code"
          },
          "accountNumber": {
            "type": "string",
            "description": "The previous spelling of `account_number`: The frequent-flyer number.",
            "deprecated": true,
            "x-replaced-by": "account_number"
          },
          "membershipNumber": {
            "type": "string",
            "description": "The previous spelling of `account_number`: The frequent-flyer number.",
            "deprecated": true,
            "x-replaced-by": "account_number"
          },
          "membership_number": {
            "type": "string",
            "description": "The previous spelling of `account_number`: The frequent-flyer number.",
            "deprecated": true,
            "x-replaced-by": "account_number"
          },
          "firstName": {
            "type": "string",
            "description": "The previous spelling of `first_name`: Given name of the member, matched with the passenger at booking.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "givenName": {
            "type": "string",
            "description": "The previous spelling of `first_name`: Given name of the member, matched with the passenger at booking.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "given_name": {
            "type": "string",
            "description": "The previous spelling of `first_name`: Given name of the member, matched with the passenger at booking.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "lastName": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name of the member.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "familyName": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name of the member.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "family_name": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name of the member.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "surname": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name of the member.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          }
        },
        "required": [
          "airline_iata_code",
          "account_number"
        ]
      },
      "FlightSearchFilters": {
        "type": "object",
        "description": "Airline filters in their older nested form (`filters`). The same three lists exist at the top level of the request, and the top-level fields win.",
        "properties": {
          "included_airlines": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An airline IATA code."
                }
              },
              {
                "type": "string",
                "description": "Airline codes separated by commas, semicolons, bars or spaces."
              }
            ],
            "description": "Up to 20 airline codes: only these carriers."
          },
          "excluded_airlines": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An airline IATA code."
                }
              },
              {
                "type": "string",
                "description": "Airline codes separated by commas, semicolons, bars or spaces."
              }
            ],
            "description": "Up to 20 airline codes to remove."
          },
          "preferred_airlines": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An airline IATA code."
                }
              },
              {
                "type": "string",
                "description": "Airline codes separated by commas, semicolons, bars or spaces."
              }
            ],
            "description": "Behaves exactly like `included_airlines`: a strict allow-list, not a ranking."
          },
          "airlineInclude": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "airline_include": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "includeAirlines": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "include_airlines": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "includedAirlines": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "include_airline": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "included_airline": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "airlineExclude": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "airline_exclude": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "excludeAirlines": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "exclude_airlines": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "excludedAirlines": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "exclude_airline": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "excluded_airline": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "airlines": {
            "description": "The previous spelling of `preferred_airlines`: Behaves exactly like `included_airlines`: a strict allow-list, not a ranking.",
            "deprecated": true,
            "x-replaced-by": "preferred_airlines"
          },
          "preferredAirlines": {
            "description": "The previous spelling of `preferred_airlines`: Behaves exactly like `included_airlines`: a strict allow-list, not a ranking.",
            "deprecated": true,
            "x-replaced-by": "preferred_airlines"
          }
        },
        "additionalProperties": true
      },
      "FlightSearchRequest": {
        "type": "object",
        "description": "The body of a search. Send `origin`, `destination` and `departure_date` (and `return_date` for a round trip), or `segments`, or `url`. Every other field is optional. A key that the endpoint does not read is ignored and reported as an `ignored_field` warning. Values a real client can send are read, with a `coerced_value` warning when their meaning had to be guessed; only garbage is refused, with a stable code. A legacy name sent together with its replacement is dropped when the values differ: the replacement wins and an `ignored_field` warning names the dropped one.",
        "properties": {
          "origin": {
            "type": "string",
            "description": "Departure IATA code: three ASCII letters, an airport (`ALG`) or a city (`PAR`). Lower case is read as upper case. Several airports: `\"ALG,ORN\"` or `[\"ALG\",\"ORN\"]`, up to five.",
            "example": "ALG"
          },
          "destination": {
            "type": "string",
            "description": "Arrival IATA code. Must differ from `origin`. Several airports are written like for `origin`.",
            "example": "CDG"
          },
          "departure_date": {
            "type": "string",
            "description": "`YYYY-MM-DD`, a real date from yesterday up to 365 days ahead (`date_too_far` beyond). `return_date` and every segment follow the same limits.",
            "example": "2026-12-15"
          },
          "return_date": {
            "type": "string",
            "description": "Makes the search a round trip. On or after `departure_date`."
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightSegmentRequest"
            },
            "description": "1 to 6 flights instead of `origin`, `destination` and `departure_date`: open-jaw and multi-city.",
            "maxItems": 6
          },
          "url": {
            "type": "string",
            "description": "A path-style search used by FX-Port's own site, for example `flights/ALG-CDG/2026-12-15/economy/1adults`. It wins over every route field. Prefer the fields above.",
            "maxLength": 512
          },
          "passengers": {
            "$ref": "#/components/schemas/FlightPassengers"
          },
          "cabin_class": {
            "type": "string",
            "enum": [
              "economy",
              "premium_economy",
              "business",
              "first",
              "any"
            ],
            "description": "The cabin. Case, spaces, dashes and underscores do not matter (`Premium Economy` is `premium_economy`); `premium`, `economy_class`, `business_class`, `first_class` and `premium_economy_class` are read as their cabin with a `coerced_value` warning, and an empty text means the default. Any other value (`coach`, `Y`) is `400 invalid_cabin_class`. Default `economy`.",
            "default": "economy"
          },
          "nonstop": {
            "type": "boolean",
            "description": "`true` returns non-stop offers only."
          },
          "refundable": {
            "type": "boolean",
            "description": "`true` asks the supplier for refundable fares. A hint, not a guarantee."
          },
          "checked_bags": {
            "type": "boolean",
            "description": "`true` asks for fares with a checked bag included. Ignored by Duffel."
          },
          "max_price": {
            "type": "number",
            "description": "A price ceiling. Not enforced in the sandbox. `0`, an empty text and `null` mean no ceiling: the field is ignored (`0` and an empty text add a `coerced_value` warning). Numeric text is read as a number with a warning; a negative or non-numeric value is `400 invalid_field_type`."
          },
          "included_airlines": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An airline IATA code."
                }
              },
              {
                "type": "string",
                "description": "Airline codes separated by commas, semicolons, bars or spaces."
              }
            ],
            "description": "Up to 20 airline codes: only these carriers."
          },
          "excluded_airlines": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An airline IATA code."
                }
              },
              {
                "type": "string",
                "description": "Airline codes separated by commas, semicolons, bars or spaces."
              }
            ],
            "description": "Up to 20 airline codes to remove."
          },
          "preferred_airlines": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An airline IATA code."
                }
              },
              {
                "type": "string",
                "description": "Airline codes separated by commas, semicolons, bars or spaces."
              }
            ],
            "description": "Behaves exactly like `included_airlines`: a strict allow-list, not a ranking."
          },
          "flexible": {
            "type": "boolean",
            "description": "Also returns the date matrix (`flexPrices`) from the suppliers that support it."
          },
          "flexible_only": {
            "type": "boolean",
            "description": "The matrix without offers. `POST /flights/flexible_search` sends it for you."
          },
          "from_cc": {
            "type": "string",
            "description": "ISO country code of the origin: exactly two letters. Used to choose suppliers."
          },
          "to_cc": {
            "type": "string",
            "description": "ISO country code of the destination: exactly two letters. Used to choose suppliers."
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "An ISO country code."
            },
            "description": "Up to 10 country codes of the route, to override the ones FX-Port infers.",
            "maxItems": 10
          },
          "filters": {
            "$ref": "#/components/schemas/FlightSearchFilters",
            "description": "The airline lists in their older nested form. Only the three airline lists are read from it; any other key is ignored. Prefer the top-level fields."
          },
          "supplier_id": {
            "type": "string",
            "description": "Restrict the search to one supplier id (a comma separated list is accepted). Strict: an unknown or disabled id is `400 invalid_supplier_selection`."
          },
          "supplier_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A supplier id."
            },
            "description": "Restrict the search to this set. Strict, up to 10.",
            "maxItems": 10
          },
          "preferred_suppliers": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A supplier id."
            },
            "description": "A hint: ids that are not usable are ignored and the automatic selection applies."
          },
          "max_suppliers": {
            "type": "integer",
            "description": "Keep only the first N selected suppliers, in priority order. `\"3\"` and `3.0` are read as 3 with a `coerced_value` warning; an empty text means no limit."
          },
          "loyalty_program_accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightLoyaltyAccount"
            },
            "description": "Up to 10 frequent-flyer accounts.",
            "maxItems": 10
          },
          "acd_exempt_company_id": {
            "type": "string",
            "description": "For agencies that settle in DZD and have an ACD-exempt company: its id, to price the fares without ACD."
          },
          "agent_id": {
            "type": "string",
            "description": "Your own id of the agent who searches; stored with the search and the booking."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "departureDate": {
            "type": "string",
            "description": "The previous spelling of `departure_date`: `YYYY-MM-DD`, a real date from yesterday up to 365 days ahead (`date_too_far` beyond).",
            "deprecated": true,
            "x-replaced-by": "departure_date"
          },
          "returnDate": {
            "type": "string",
            "description": "The previous spelling of `return_date`: Makes the search a round trip.",
            "deprecated": true,
            "x-replaced-by": "return_date"
          },
          "cabinClass": {
            "type": "string",
            "description": "The previous spelling of `cabin_class`: The cabin.",
            "deprecated": true,
            "x-replaced-by": "cabin_class"
          },
          "travelers": {
            "description": "The previous spelling of `passengers`: `passengers`.",
            "deprecated": true,
            "x-replaced-by": "passengers"
          },
          "direct": {
            "type": "boolean",
            "description": "The previous spelling of `nonstop`: `true` returns non-stop offers only.",
            "deprecated": true,
            "x-replaced-by": "nonstop"
          },
          "checkedBags": {
            "type": "boolean",
            "description": "The previous spelling of `checked_bags`: `true` asks for fares with a checked bag included.",
            "deprecated": true,
            "x-replaced-by": "checked_bags"
          },
          "maxPrice": {
            "type": "number",
            "description": "The previous spelling of `max_price`: A price ceiling.",
            "deprecated": true,
            "x-replaced-by": "max_price"
          },
          "flexibleOnly": {
            "type": "boolean",
            "description": "The previous spelling of `flexible_only`: The matrix without offers.",
            "deprecated": true,
            "x-replaced-by": "flexible_only"
          },
          "fromCc": {
            "type": "string",
            "description": "The previous spelling of `from_cc`: ISO country code of the origin: exactly two letters.",
            "deprecated": true,
            "x-replaced-by": "from_cc"
          },
          "toCc": {
            "type": "string",
            "description": "The previous spelling of `to_cc`: ISO country code of the destination: exactly two letters.",
            "deprecated": true,
            "x-replaced-by": "to_cc"
          },
          "supplierId": {
            "type": "string",
            "description": "The previous spelling of `supplier_id`: Restrict the search to one supplier id (a comma separated list is accepted).",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplierIds": {
            "type": "array",
            "description": "The previous spelling of `supplier_ids`: Restrict the search to this set.",
            "deprecated": true,
            "x-replaced-by": "supplier_ids"
          },
          "preferredSuppliers": {
            "type": "array",
            "description": "The previous spelling of `preferred_suppliers`: A hint: ids that are not usable are ignored and the automatic selection applies.",
            "deprecated": true,
            "x-replaced-by": "preferred_suppliers"
          },
          "maxSuppliers": {
            "type": "integer",
            "description": "The previous spelling of `max_suppliers`: Keep only the first N selected suppliers, in priority order.",
            "deprecated": true,
            "x-replaced-by": "max_suppliers"
          },
          "airlineInclude": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "airline_include": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "includeAirlines": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "include_airlines": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "includedAirlines": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "include_airline": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "included_airline": {
            "description": "The previous spelling of `included_airlines`: Up to 20 airline codes: only these carriers.",
            "deprecated": true,
            "x-replaced-by": "included_airlines"
          },
          "airlineExclude": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "airline_exclude": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "excludeAirlines": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "exclude_airlines": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "excludedAirlines": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "exclude_airline": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "excluded_airline": {
            "description": "The previous spelling of `excluded_airlines`: Up to 20 airline codes to remove.",
            "deprecated": true,
            "x-replaced-by": "excluded_airlines"
          },
          "airlines": {
            "description": "The previous spelling of `preferred_airlines`: Behaves exactly like `included_airlines`: a strict allow-list, not a ranking.",
            "deprecated": true,
            "x-replaced-by": "preferred_airlines"
          },
          "preferredAirlines": {
            "description": "The previous spelling of `preferred_airlines`: Behaves exactly like `included_airlines`: a strict allow-list, not a ranking.",
            "deprecated": true,
            "x-replaced-by": "preferred_airlines"
          },
          "agentId": {
            "type": "string",
            "description": "The previous spelling of `agent_id`: Your own id of the agent who searches; stored with the search and the booking.",
            "deprecated": true,
            "x-replaced-by": "agent_id"
          },
          "acdExemptCompanyId": {
            "type": "string",
            "description": "The previous spelling of `acd_exempt_company_id`: For agencies that settle in DZD and have an ACD-exempt company: its id, to price the fares without ACD.",
            "deprecated": true,
            "x-replaced-by": "acd_exempt_company_id"
          },
          "loyaltyProgramAccounts": {
            "type": "array",
            "description": "The previous spelling of `loyalty_program_accounts`: Up to 10 frequent-flyer accounts.",
            "deprecated": true,
            "x-replaced-by": "loyalty_program_accounts"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        },
        "examples": [
          {
            "origin": "ALG",
            "destination": "CDG",
            "departure_date": "2026-12-15",
            "cabin_class": "economy",
            "passengers": {
              "adults": 1
            }
          }
        ]
      },
      "FlightPriceRequest": {
        "type": "object",
        "description": "The offer to price.",
        "properties": {
          "search_id": {
            "type": "string",
            "description": "The `searchId` of the **supplier entry** the offer is in (`suppliers[n].searchId`), never the top-level `requestId`.",
            "example": "fs_dfx_88eb64c9-6c36-47e6-bdaa-9ad3c1377ac4"
          },
          "offer_id": {
            "type": "string",
            "description": "The `id` of the offer.",
            "example": "fx_offer_b7a95439"
          },
          "agency_id": {
            "type": "string",
            "description": "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": {
            "type": "string",
            "description": "The previous spelling of `search_id`: The `searchId` of the **supplier entry** the offer is in (`suppliers[n].searchId`), never the top-level `requestId`.",
            "deprecated": true,
            "x-replaced-by": "search_id"
          },
          "offerId": {
            "type": "string",
            "description": "The previous spelling of `offer_id`: The `id` of the offer.",
            "deprecated": true,
            "x-replaced-by": "offer_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        },
        "required": [
          "search_id",
          "offer_id"
        ]
      },
      "FlightIdentityDocument": {
        "type": "object",
        "description": "The passport of a traveler. Send it for every traveler: airlines can require it.",
        "properties": {
          "type": {
            "type": "string",
            "description": "`passport` (default)."
          },
          "number": {
            "type": "string",
            "description": "The document number: 1 to 40 letters, digits, spaces and `- . / _`. A JSON number is read as text with a `coerced_value` warning."
          },
          "issuing_country": {
            "type": "string",
            "description": "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": {
            "type": "string",
            "description": "`YYYY-MM-DD`."
          },
          "nationality": {
            "type": "string",
            "description": "Accepted and ignored: suppliers use the issuing country."
          },
          "issuingCountry": {
            "type": "string",
            "description": "The previous spelling of `issuing_country`: The country that issued the document: ISO 3166-1 alpha-2 (`DZ`).",
            "deprecated": true,
            "x-replaced-by": "issuing_country"
          },
          "issuing_country_code": {
            "type": "string",
            "description": "The previous spelling of `issuing_country`: The country that issued the document: ISO 3166-1 alpha-2 (`DZ`).",
            "deprecated": true,
            "x-replaced-by": "issuing_country"
          },
          "issuingCountryCode": {
            "type": "string",
            "description": "The previous spelling of `issuing_country`: The country that issued the document: ISO 3166-1 alpha-2 (`DZ`).",
            "deprecated": true,
            "x-replaced-by": "issuing_country"
          },
          "expiryDate": {
            "type": "string",
            "description": "The previous spelling of `expiry_date`: `YYYY-MM-DD`.",
            "deprecated": true,
            "x-replaced-by": "expiry_date"
          },
          "expires_on": {
            "type": "string",
            "description": "The previous spelling of `expiry_date`: `YYYY-MM-DD`.",
            "deprecated": true,
            "x-replaced-by": "expiry_date"
          },
          "documentNumber": {
            "type": "string",
            "description": "The previous spelling of `number`: The document number: 1 to 40 letters, digits, spaces and `- .",
            "deprecated": true,
            "x-replaced-by": "number"
          },
          "unique_identifier": {
            "type": "string",
            "description": "The previous spelling of `number`: The document number: 1 to 40 letters, digits, spaces and `- .",
            "deprecated": true,
            "x-replaced-by": "number"
          }
        }
      },
      "FlightPassengerInput": {
        "type": "object",
        "description": "One traveler of a booking. The list must match the passengers of the search and the offer's `travelerPricing`.",
        "properties": {
          "type": {
            "type": "string",
            "description": "`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": {
            "type": "string",
            "description": "`MALE` or `FEMALE` (`M` and `F` too), in any letter case. `X`, `U` or `UNSPECIFIED` for a passport marked X."
          },
          "first_name": {
            "type": "string",
            "description": "Given name as in the passport. Latin letters are what airlines need. Spaces and line breaks around it are removed before the checks.",
            "maxLength": 64
          },
          "last_name": {
            "type": "string",
            "description": "Family name as in the passport. Spaces and line breaks around it are removed before the checks.",
            "maxLength": 64
          },
          "date_of_birth": {
            "type": "string",
            "description": "`YYYY-MM-DD`, a real date that is not in the future (tomorrow is still accepted: time-zone slack)."
          },
          "identity_documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightIdentityDocument"
            },
            "description": "The passport, up to 5 documents.",
            "maxItems": 5
          },
          "id": {
            "type": "string",
            "description": "An id of yours for the passenger."
          },
          "firstName": {
            "type": "string",
            "description": "The previous spelling of `first_name`: Given name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "givenName": {
            "type": "string",
            "description": "The previous spelling of `first_name`: Given name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "given_name": {
            "type": "string",
            "description": "The previous spelling of `first_name`: Given name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "lastName": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "familyName": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "family_name": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "surname": {
            "type": "string",
            "description": "The previous spelling of `last_name`: Family name as in the passport.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "dateOfBirth": {
            "type": "string",
            "description": "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).",
            "deprecated": true,
            "x-replaced-by": "date_of_birth"
          },
          "bornOn": {
            "type": "string",
            "description": "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).",
            "deprecated": true,
            "x-replaced-by": "date_of_birth"
          },
          "born_on": {
            "type": "string",
            "description": "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).",
            "deprecated": true,
            "x-replaced-by": "date_of_birth"
          },
          "dob": {
            "type": "string",
            "description": "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).",
            "deprecated": true,
            "x-replaced-by": "date_of_birth"
          },
          "identityDocuments": {
            "type": "array",
            "description": "The previous spelling of `identity_documents`: The passport, up to 5 documents.",
            "deprecated": true,
            "x-replaced-by": "identity_documents"
          },
          "documents": {
            "type": "array",
            "description": "The previous spelling of `identity_documents`: The passport, up to 5 documents.",
            "deprecated": true,
            "x-replaced-by": "identity_documents"
          },
          "passengerType": {
            "type": "string",
            "description": "The previous spelling of `type`: `adult`, `senior`, `young_adult`, `child`, `seated_infant` or `infant` (a lap infant), in any letter case.",
            "deprecated": true,
            "x-replaced-by": "type"
          }
        },
        "required": [
          "type",
          "gender",
          "first_name",
          "last_name",
          "date_of_birth"
        ]
      },
      "FlightContactDetails": {
        "type": "object",
        "description": "How to reach the traveler. Both fields are required.",
        "properties": {
          "email": {
            "type": "string",
            "description": "The e-mail of the contact, 254 characters at most.",
            "example": "traveler@example.com"
          },
          "phone_number": {
            "type": "string",
            "description": "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`.",
            "example": "+15555550123"
          },
          "phone": {
            "type": "string",
            "description": "The previous spelling of `phone_number`: An international phone number (`+213...`): Duffel validates the format.",
            "deprecated": true,
            "x-replaced-by": "phone_number"
          },
          "phoneNumber": {
            "type": "string",
            "description": "The previous spelling of `phone_number`: An international phone number (`+213...`): Duffel validates the format.",
            "deprecated": true,
            "x-replaced-by": "phone_number"
          },
          "mobile": {
            "type": "string",
            "description": "The previous spelling of `phone_number`: An international phone number (`+213...`): Duffel validates the format.",
            "deprecated": true,
            "x-replaced-by": "phone_number"
          },
          "emailAddress": {
            "type": "string",
            "description": "The previous spelling of `email`: The e-mail of the contact, 254 characters at most.",
            "deprecated": true,
            "x-replaced-by": "email"
          }
        },
        "required": [
          "email",
          "phone_number"
        ]
      },
      "FlightCommissionOverride": {
        "type": "object",
        "description": "Replaces your agency commission for this booking: `pricing.agencyCommission` and `pricing.finalPrice` change, `b2bPrice` does not.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ],
            "description": "`percentage` is a share of `b2bPrice`; `fixed` is `value` times the number of travelers."
          },
          "value": {
            "type": "number",
            "description": "A number from 0. Numeric text such as `\"10\"` is read as a number with a `coerced_value` warning.",
            "minimum": 0
          }
        },
        "required": [
          "type",
          "value"
        ]
      },
      "BookFlightRequest": {
        "type": "object",
        "description": "The booking to create. Price the offer first: an offer that was never priced is `404 offer_not_priced`.",
        "properties": {
          "search_id": {
            "type": "string",
            "description": "The `searchId` of the supplier entry the priced offer came from."
          },
          "offer_id": {
            "type": "string",
            "description": "The `id` of the priced offer."
          },
          "booking_type": {
            "type": "string",
            "enum": [
              "issue",
              "hold"
            ],
            "description": "`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`.",
            "default": "issue"
          },
          "passengers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightPassengerInput"
            },
            "description": "One object per traveler, lap infants included. At most 18.",
            "maxItems": 18
          },
          "contact_details": {
            "$ref": "#/components/schemas/FlightContactDetails"
          },
          "send_email": {
            "type": "boolean",
            "description": "`false` suppresses the confirmation e-mail. Default `true`."
          },
          "commission_override": {
            "$ref": "#/components/schemas/FlightCommissionOverride"
          },
          "agent_id": {
            "type": "string",
            "description": "Your own id of the agent who books; stored on the booking."
          },
          "agency_id": {
            "type": "string",
            "description": "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": {
            "type": "string",
            "description": "The previous spelling of `search_id`: The `searchId` of the supplier entry the priced offer came from.",
            "deprecated": true,
            "x-replaced-by": "search_id"
          },
          "offerId": {
            "type": "string",
            "description": "The previous spelling of `offer_id`: The `id` of the priced offer.",
            "deprecated": true,
            "x-replaced-by": "offer_id"
          },
          "bookingType": {
            "type": "string",
            "description": "The previous spelling of `booking_type`: `issue` (default) for instant ticketing, `hold` for an unpaid reservation when `supportHold` is `true` after pricing.",
            "deprecated": true,
            "x-replaced-by": "booking_type"
          },
          "sendEmail": {
            "type": "boolean",
            "description": "The previous spelling of `send_email`: `false` suppresses the confirmation e-mail.",
            "deprecated": true,
            "x-replaced-by": "send_email"
          },
          "commissionOverride": {
            "description": "The previous spelling of `commission_override`: `commission_override`.",
            "deprecated": true,
            "x-replaced-by": "commission_override"
          },
          "agentId": {
            "type": "string",
            "description": "The previous spelling of `agent_id`: Your own id of the agent who books; stored on the booking.",
            "deprecated": true,
            "x-replaced-by": "agent_id"
          },
          "contact": {
            "description": "The previous spelling of `contact_details`: `contact_details`.",
            "deprecated": true,
            "x-replaced-by": "contact_details"
          },
          "contacts": {
            "description": "The previous spelling of `contact_details`: `contact_details`.",
            "deprecated": true,
            "x-replaced-by": "contact_details"
          },
          "contactDetails": {
            "description": "The previous spelling of `contact_details`: `contact_details`.",
            "deprecated": true,
            "x-replaced-by": "contact_details"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        },
        "required": [
          "search_id",
          "offer_id",
          "passengers",
          "contact_details"
        ]
      },
      "FlightBookingLookupRequest": {
        "type": "object",
        "description": "Which booking. Send one of `booking_id`, `order_id` and `pnr`. When several are sent with different values, `booking_id` wins and an `ignored_field` warning names the others.",
        "properties": {
          "booking_id": {
            "type": "string",
            "description": "The PNR, the supplier order id or the FX-Port booking UUID. Prefer the UUID or the order id: PNRs are not unique.",
            "example": "Y7PAEU"
          },
          "order_id": {
            "type": "string",
            "description": "Another name of `booking_id`."
          },
          "pnr": {
            "type": "string",
            "description": "Another name of `booking_id`."
          },
          "include_api_response": {
            "type": "boolean",
            "description": "`get_booking` only: `true` adds `booking.apiResponse`. Default `false`."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "bookingId": {
            "type": "string",
            "description": "The previous spelling of `booking_id`: The PNR, the supplier order id or the FX-Port booking UUID.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "orderId": {
            "type": "string",
            "description": "The previous spelling of `booking_id`: The PNR, the supplier order id or the FX-Port booking UUID.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "includeApiResponse": {
            "type": "boolean",
            "description": "The previous spelling of `include_api_response`: `get_booking` only: `true` adds `booking.apiResponse`.",
            "deprecated": true,
            "x-replaced-by": "include_api_response"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        }
      },
      "FlightOrderRequest": {
        "type": "object",
        "description": "Which order. Send one of `order_id`, `booking_id` and `pnr`: the first one present wins, in this order, and an `ignored_field` warning names another one that holds a different value.",
        "properties": {
          "order_id": {
            "type": "string",
            "description": "The supplier's order id (`booking.reference` of the booking response). **Preferred**: a PNR is not unique.",
            "example": "eJzTd9ePNA9wdA0FAAs8AmE"
          },
          "booking_id": {
            "type": "string",
            "description": "The PNR of the booking."
          },
          "pnr": {
            "type": "string",
            "description": "The airline record locator."
          },
          "supplier_id": {
            "type": "string",
            "description": "Only used when the booking has no supplier of its own."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "orderId": {
            "type": "string",
            "description": "The previous spelling of `order_id`: The supplier's order id (`booking.reference` of the booking response).",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "bookingId": {
            "type": "string",
            "description": "The previous spelling of `booking_id`: The PNR of the booking.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "supplierId": {
            "type": "string",
            "description": "The previous spelling of `supplier_id`: Only used when the booking has no supplier of its own.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        }
      },
      "FlightPayOrderRequest": {
        "type": "object",
        "description": "The order to pay. Send one of `order_id`, `booking_id` and `pnr` (the first one present wins, and an `ignored_field` warning names another one that holds a different value). Do not send `currency` or `amount`: they are resolved by the server (`currency` is `400 bad_request`).",
        "properties": {
          "order_id": {
            "type": "string",
            "description": "The supplier's order id (`booking.reference` of the booking response). **Preferred**.",
            "example": "eJzTd9ePNA9wdA0FAAs8AmE"
          },
          "booking_id": {
            "type": "string",
            "description": "The PNR of the booking."
          },
          "pnr": {
            "type": "string",
            "description": "The airline record locator."
          },
          "payment_type": {
            "type": "string",
            "enum": [
              "balance",
              "card",
              "arc_bsp_cash"
            ],
            "description": "Only Duffel reads it; Amadeus Algeria and Travelport pay from the balance whatever you send. Default `balance`.",
            "default": "balance"
          },
          "three_d_secure_session_id": {
            "type": "string",
            "description": "For a Duffel card payment that needs 3-D Secure."
          },
          "price_guaranteed": {
            "type": "boolean",
            "description": "Default `true`: pay the price of the hold. Leave it out."
          },
          "supplier_id": {
            "type": "string",
            "description": "Only used when the booking has no supplier of its own."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "orderId": {
            "type": "string",
            "description": "The previous spelling of `order_id`: The supplier's order id (`booking.reference` of the booking response).",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "bookingId": {
            "type": "string",
            "description": "The previous spelling of `booking_id`: The PNR of the booking.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "paymentType": {
            "type": "string",
            "description": "The previous spelling of `payment_type`: Only Duffel reads it; Amadeus Algeria and Travelport pay from the balance whatever you send.",
            "deprecated": true,
            "x-replaced-by": "payment_type"
          },
          "threeDSecureSessionId": {
            "type": "string",
            "description": "The previous spelling of `three_d_secure_session_id`: For a Duffel card payment that needs 3-D Secure.",
            "deprecated": true,
            "x-replaced-by": "three_d_secure_session_id"
          },
          "priceGuaranteed": {
            "type": "boolean",
            "description": "The previous spelling of `price_guaranteed`: Default `true`: pay the price of the hold.",
            "deprecated": true,
            "x-replaced-by": "price_guaranteed"
          },
          "supplierId": {
            "type": "string",
            "description": "The previous spelling of `supplier_id`: Only used when the booking has no supplier of its own.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        }
      },
      "FlightCancelQuoteRequest": {
        "type": "object",
        "description": "The issued Duffel order to quote. `order_id`, `booking_id` and `pnr` follow the same rule as for the other order calls: the first one present wins.",
        "properties": {
          "order_id": {
            "type": "string",
            "description": "The supplier's order id (`ord_...`). `booking_id` and `pnr` are accepted as other names; FX-Port sends the supplier's own id to Duffel.",
            "example": "ord_0000BC0VDONwA54LrfoCkm"
          },
          "booking_id": {
            "type": "string",
            "description": "Another name of `order_id`: the PNR."
          },
          "pnr": {
            "type": "string",
            "description": "Another name of `order_id`: the airline record locator."
          },
          "supplier_id": {
            "type": "string",
            "description": "Default `duffel_1`. A value that does not start with `duffel` is `400`."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "orderId": {
            "type": "string",
            "description": "The previous spelling of `order_id`: The supplier's order id (`ord_...`).",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "bookingId": {
            "type": "string",
            "description": "The previous spelling of `booking_id`: Another name of `order_id`: the PNR.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "supplierId": {
            "type": "string",
            "description": "The previous spelling of `supplier_id`: Default `duffel_1`.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        }
      },
      "FlightConfirmCancelRequest": {
        "type": "object",
        "description": "The quoted cancellation to confirm. `order_id`, `booking_id` and `pnr` follow the same rule as for the other order calls: the first one present wins. This cannot be undone.",
        "properties": {
          "cancellation_id": {
            "type": "string",
            "description": "The `ore_...` id of `cancel_quote` (`cancellation.cancellationId`).",
            "example": "ore_0000BC0VG9WBaFAZ83u3yy"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id of the booking (`booking_id` and `pnr` are accepted as other names). **Required**: it makes FX-Port check that the booking is yours and update it.",
            "example": "ord_0000BC0VDONwA54LrfoCkm"
          },
          "booking_id": {
            "type": "string",
            "description": "Another name of `order_id`."
          },
          "pnr": {
            "type": "string",
            "description": "Another name of `order_id`."
          },
          "supplier_id": {
            "type": "string",
            "description": "Default `duffel_1`."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "cancellationId": {
            "type": "string",
            "description": "The previous spelling of `cancellation_id`: The `ore_...` id of `cancel_quote` (`cancellation.cancellationId`).",
            "deprecated": true,
            "x-replaced-by": "cancellation_id"
          },
          "orderId": {
            "type": "string",
            "description": "The previous spelling of `order_id`: The supplier's order id of the booking (`booking_id` and `pnr` are accepted as other names).",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "bookingId": {
            "type": "string",
            "description": "The previous spelling of `booking_id`: Another name of `order_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "supplierId": {
            "type": "string",
            "description": "The previous spelling of `supplier_id`: Default `duffel_1`.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The previous spelling of `agency_id`: Not needed: the agency of a call is always the one of your API key.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        },
        "required": [
          "cancellation_id"
        ]
      },
      "FlightAirport": {
        "type": "object",
        "description": "One airport or city of the lookup. A real record has about 40 keys (the answer of an airport search service, forwarded as it is): only the ones listed here are meant for your use; the others can change.",
        "properties": {
          "ap": {
            "type": "string",
            "description": "The IATA code: use it as `origin` or `destination` in a search.",
            "example": "ALG"
          },
          "airportname": {
            "type": "string",
            "description": "The airport name in the requested locale."
          },
          "cityname": {
            "type": "string",
            "description": "City and country in the requested locale."
          },
          "cityonly": {
            "type": "string",
            "description": "The city alone."
          },
          "cc": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code."
          },
          "country": {
            "type": "string",
            "description": "The country name."
          },
          "timezone": {
            "type": "string",
            "description": "The IANA time zone."
          },
          "utc": {
            "type": "string",
            "description": "The UTC offset at the time of the query."
          },
          "lat": {
            "type": "number",
            "description": "Latitude."
          },
          "lng": {
            "type": "number",
            "description": "Longitude."
          },
          "displayname": {
            "type": "string",
            "description": "A full label for a result list."
          },
          "shortdisplayname": {
            "type": "string",
            "description": "A short label for the selected value."
          },
          "loctype": {
            "type": "string",
            "description": "`ap` for an airport, `city` for a city."
          },
          "isMetroOnly": {
            "type": "boolean",
            "description": "`true` for a city code that stands for all the airports of a city (`PAR`): valid as an `origin` or `destination`."
          }
        },
        "required": [
          "ap"
        ],
        "additionalProperties": true
      },
      "FlightTopAirportsResponse": {
        "type": "object",
        "description": "The most searched airports for the first suggestions of an empty search box.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "airports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightAirport"
            },
            "description": "The most searched airports, most searched first. Each record has the fields of an airport plus `payload_locale`, `popularity_score`, `popularity_scopes` (`locale` and `global`), `recent_score` and `last_seen_day`."
          },
          "context": {
            "type": "object",
            "description": "What the ranking used.",
            "properties": {
              "locale": {
                "type": "string",
                "description": "The language of the names, normalised."
              },
              "airport_type": {
                "type": "string",
                "enum": [
                  "from",
                  "to"
                ],
                "description": "Which ranking was used."
              },
              "agency_id": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "An agency scope."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "An internal ranking scope of FX-Port's own apps: `null` in your answers."
              },
              "market_region": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The region code."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The `market_region` you sent, in upper case; `null` when you sent none."
              }
            },
            "required": [
              "locale",
              "airport_type"
            ]
          }
        },
        "required": [
          "airports",
          "context"
        ]
      },
      "FlightAcdPricing": {
        "type": "object",
        "description": "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.",
        "properties": {
          "applied": {
            "type": "boolean",
            "description": "`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": {
            "type": "string",
            "enum": [
              "A",
              "B"
            ],
            "description": "Only on an ACD fare: the rule that adjusted the price. Informational."
          },
          "exempt": {
            "type": "boolean",
            "description": "`true` when the search named an ACD-exempt company of your agency: the fare is priced without ACD."
          },
          "reason": {
            "type": "string",
            "description": "Only on some ACD fares: why the rule applied. Informational; the wording can change."
          }
        },
        "required": [
          "applied"
        ],
        "additionalProperties": true
      },
      "FlightPricing": {
        "type": "object",
        "description": "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.",
        "properties": {
          "supplierCurrency": {
            "type": "string",
            "description": "ISO 4217 currency in which the supplier prices the fare.",
            "example": "USD"
          },
          "agencyCurrency": {
            "type": "string",
            "description": "ISO 4217 currency of your agency. Every amount of the block is in it.",
            "example": "DZD"
          },
          "basePrice": {
            "type": "number",
            "description": "The airline's base fare, before taxes, in the agency currency.",
            "deprecated": true,
            "x-replaced-by": "base_price"
          },
          "base_price": {
            "type": "number",
            "description": "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": {
            "type": "number",
            "description": "Taxes plus the platform's fees, folded together. Excludes your agency commission.",
            "deprecated": true,
            "x-replaced-by": "taxes_and_fees"
          },
          "taxes_and_fees": {
            "type": "number",
            "description": "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": {
            "type": "number",
            "description": "The commission of your agency on this sale.",
            "deprecated": true,
            "x-replaced-by": "agency_commission"
          },
          "agency_commission": {
            "type": "number",
            "description": "The commission of your agency on this sale. The snake_case twin of `agencyCommission`, with the same value: use this name."
          },
          "finalPrice": {
            "type": "number",
            "description": "What the traveler pays: `basePrice + taxesAndFees + agencyCommission`.",
            "deprecated": true,
            "x-replaced-by": "final_price"
          },
          "final_price": {
            "type": "number",
            "description": "What the traveler pays: `basePrice + taxesAndFees + agencyCommission`. The snake_case twin of `finalPrice`, with the same value: use this name."
          },
          "b2bPrice": {
            "type": "number",
            "description": "The amount debited from your agency balance: `finalPrice - agencyCommission`.",
            "deprecated": true,
            "x-replaced-by": "b2b_price"
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance: `finalPrice - agencyCommission`. The snake_case twin of `b2bPrice`, with the same value: use this name."
          },
          "totalAmount": {
            "type": "number",
            "description": "Order quote only: an alias of `b2bPrice`."
          },
          "taxAmount": {
            "type": "number",
            "description": "Order quote of a Duffel order only: the airline's taxes in your agency currency."
          },
          "originalTaxAmount": {
            "type": "number",
            "description": "Order quote of a Duffel order only: the same taxes in the supplier currency."
          },
          "additionalServices": {
            "type": "array",
            "items": {
              "description": "A paid extra the supplier lists.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Paid extras. A list in the offers of a search (empty for most offers)."
          },
          "acd": {
            "$ref": "#/components/schemas/FlightAcdPricing"
          }
        },
        "required": [
          "supplierCurrency",
          "agencyCurrency",
          "basePrice",
          "taxesAndFees",
          "agencyCommission",
          "finalPrice",
          "b2bPrice"
        ]
      },
      "FlightPlace": {
        "type": "object",
        "description": "Where and when a segment leaves or arrives.",
        "properties": {
          "airportCode": {
            "type": "string",
            "description": "IATA code of the airport.",
            "example": "ALG"
          },
          "airportName": {
            "type": "string",
            "description": "Name of the airport."
          },
          "cityName": {
            "type": "string",
            "description": "Name of the city."
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code.",
            "example": "DZ"
          },
          "terminal": {
            "anyOf": [
              {
                "type": "string",
                "description": "Terminal, when the supplier says."
              },
              {
                "type": "null"
              }
            ],
            "description": "Terminal, when the supplier says."
          },
          "datetime": {
            "type": "string",
            "description": "Local date and time at the airport, `YYYY-MM-DDTHH:MM:SS`, without a time zone.",
            "example": "2026-12-15T10:50:00"
          },
          "timeZone": {
            "type": "string",
            "description": "IANA time zone of the airport.",
            "example": "Africa/Algiers"
          }
        },
        "required": [
          "airportCode",
          "airportName",
          "cityName",
          "countryCode",
          "terminal",
          "datetime",
          "timeZone"
        ]
      },
      "FlightNumber": {
        "type": "object",
        "description": "The airline and the number of a flight.",
        "properties": {
          "carrierCode": {
            "type": "string",
            "description": "IATA code of the marketing airline.",
            "example": "AH"
          },
          "carrierName": {
            "type": "string",
            "description": "Name of the marketing airline."
          },
          "carrierLogoUrl": {
            "type": "string",
            "description": "Address of the logo of the marketing airline."
          },
          "flightNumber": {
            "type": "string",
            "description": "The flight number without the carrier code.",
            "example": "7426"
          },
          "aircraftCode": {
            "anyOf": [
              {
                "type": "string",
                "description": "IATA code of the aircraft type."
              },
              {
                "type": "null"
              }
            ],
            "description": "IATA code of the aircraft type."
          },
          "aircraftName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name of the aircraft type."
              },
              {
                "type": "null"
              }
            ],
            "description": "Name of the aircraft type."
          },
          "operatingCarrier": {
            "type": "string",
            "description": "IATA code of the airline that operates the flight. A different value than `carrierCode` means a codeshare."
          },
          "operatingCarrierName": {
            "type": "string",
            "description": "Name of the operating airline."
          }
        },
        "required": [
          "carrierCode",
          "carrierName",
          "flightNumber",
          "operatingCarrier",
          "operatingCarrierName"
        ]
      },
      "FlightSegment": {
        "type": "object",
        "description": "One flight of an itinerary: one take-off and one landing.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the segment. `fareDetailsBySegment[].segmentId` points to it."
          },
          "departure": {
            "$ref": "#/components/schemas/FlightPlace"
          },
          "arrival": {
            "$ref": "#/components/schemas/FlightPlace"
          },
          "flight": {
            "$ref": "#/components/schemas/FlightNumber"
          },
          "duration": {
            "type": "string",
            "description": "ISO 8601 duration of the flight.",
            "example": "PT2H24M"
          },
          "cabin": {
            "type": "string",
            "enum": [
              "ECONOMY",
              "PREMIUM_ECONOMY",
              "BUSINESS",
              "FIRST"
            ],
            "description": "The cabin of this segment. It can differ from the cabin you asked for."
          },
          "bookingClass": {
            "anyOf": [
              {
                "type": "string",
                "description": "Booking class letter (Amadeus: `Q`, `O`; Duffel: null)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Booking class letter (Amadeus: `Q`, `O`; Duffel: null)."
          },
          "fareBasis": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fare basis code."
              },
              {
                "type": "null"
              }
            ],
            "description": "Fare basis code."
          },
          "brandedFare": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fare family code."
              },
              {
                "type": "null"
              }
            ],
            "description": "Fare family code."
          },
          "brandedFareLabel": {
            "anyOf": [
              {
                "type": "string",
                "description": "Display label of the fare family."
              },
              {
                "type": "null"
              }
            ],
            "description": "Display label of the fare family."
          }
        },
        "required": [
          "id",
          "departure",
          "arrival",
          "flight",
          "duration",
          "cabin"
        ]
      },
      "FlightConditions": {
        "type": "object",
        "description": "What the fare allows before departure. Indicative only. A Duffel order can carry `null` for both when the airline does not say.",
        "properties": {
          "refundBeforeDeparture": {
            "anyOf": [
              {
                "type": "object",
                "description": "What the fare says about a refund before departure.",
                "properties": {
                  "allowed": {
                    "type": "boolean",
                    "description": "Whether a refund is allowed before departure. Indicative, not a promise: the fare rules of the airline decide."
                  }
                },
                "required": [
                  "allowed"
                ],
                "additionalProperties": true
              },
              {
                "type": "null"
              }
            ],
            "description": "What the fare says about a refund before departure."
          },
          "changeBeforeDeparture": {
            "anyOf": [
              {
                "type": "object",
                "description": "What the fare says about a change before departure.",
                "properties": {
                  "allowed": {
                    "type": "boolean",
                    "description": "Whether a change is allowed before departure. Indicative, not a promise: the fare rules of the airline decide."
                  }
                },
                "required": [
                  "allowed"
                ],
                "additionalProperties": true
              },
              {
                "type": "null"
              }
            ],
            "description": "What the fare says about a change before departure."
          }
        },
        "additionalProperties": true
      },
      "FlightItinerary": {
        "type": "object",
        "description": "One direction of the trip: the outbound, the return, or one leg of a multi-city search.",
        "properties": {
          "direction": {
            "type": "string",
            "description": "`outbound`, `return`, and `segment_3`, `segment_4` ... for the third and later legs of a multi-city search."
          },
          "duration": {
            "type": "string",
            "description": "ISO 8601 duration of this direction.",
            "example": "PT2H24M"
          },
          "stops": {
            "type": "integer",
            "description": "Number of stops. `0` is non-stop."
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightSegment"
            },
            "description": "The flights of this direction, in order."
          },
          "conditions": {
            "$ref": "#/components/schemas/FlightConditions"
          }
        },
        "required": [
          "direction",
          "duration",
          "stops",
          "segments"
        ]
      },
      "FlightFareDetail": {
        "type": "object",
        "description": "The fare of one traveler on one segment.",
        "properties": {
          "segmentId": {
            "type": "string",
            "description": "The `id` of the segment this fare detail is about."
          },
          "cabin": {
            "type": "string",
            "enum": [
              "ECONOMY",
              "PREMIUM_ECONOMY",
              "BUSINESS",
              "FIRST"
            ],
            "description": "Cabin of the fare on this segment."
          },
          "fareBasis": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fare basis code."
              },
              {
                "type": "null"
              }
            ],
            "description": "Fare basis code."
          },
          "brandedFare": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fare family code."
              },
              {
                "type": "null"
              }
            ],
            "description": "Fare family code."
          },
          "brandedFareLabel": {
            "anyOf": [
              {
                "type": "string",
                "description": "Display label of the fare family."
              },
              {
                "type": "null"
              }
            ],
            "description": "Display label of the fare family."
          },
          "bookingClass": {
            "anyOf": [
              {
                "type": "string",
                "description": "Booking class letter."
              },
              {
                "type": "null"
              }
            ],
            "description": "Booking class letter."
          },
          "includedCheckedBags": {
            "type": "object",
            "description": "The checked baggage included in the fare.",
            "properties": {
              "quantity": {
                "type": "integer",
                "description": "Number of checked bags included in the fare."
              }
            },
            "required": [
              "quantity"
            ],
            "additionalProperties": true
          },
          "includedCabinBags": {
            "type": "object",
            "description": "The cabin baggage included in the fare.",
            "properties": {
              "quantity": {
                "type": "integer",
                "description": "Number of cabin bags included in the fare."
              }
            },
            "required": [
              "quantity"
            ],
            "additionalProperties": true
          },
          "amenities": {
            "type": "array",
            "items": {
              "description": "An amenity of the fare: `description`, `isChargeable`, `amenityType`, `amenityProvider`.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "What else the fare includes or sells (seat, bag, meal, Wi-Fi ...)."
          }
        },
        "required": [
          "segmentId",
          "cabin"
        ]
      },
      "FlightTravelerPrice": {
        "type": "object",
        "description": "The price of one traveler.",
        "properties": {
          "supplierCurrency": {
            "type": "string",
            "description": "ISO 4217 currency in which the supplier prices the fare."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "ISO 4217 currency of your agency."
          },
          "base": {
            "type": "number",
            "description": "The traveler's base fare in the agency currency."
          },
          "total": {
            "type": "number",
            "description": "The traveler's final price: what this traveler pays. The totals of all travelers add up to `pricing.finalPrice`."
          },
          "b2bPrice": {
            "type": "number",
            "description": "The traveler's share of the amount debited from your balance."
          }
        },
        "required": [
          "base",
          "total"
        ],
        "additionalProperties": true
      },
      "FlightTravelerPricing": {
        "type": "object",
        "description": "The price of one priced traveler. The entries are grouped by type (adults, then seniors, children ...), not in the order of your request.",
        "properties": {
          "travelerId": {
            "type": "string",
            "description": "`\"1\"`, `\"2\"` ... for Amadeus, `pas_...` for Duffel.",
            "deprecated": true,
            "x-replaced-by": "passenger_id"
          },
          "passenger_id": {
            "type": "string",
            "description": "`\"1\"`, `\"2\"` ... for Amadeus, `pas_...` for Duffel. The snake_case twin of `travelerId`, with the same value: use this name."
          },
          "travelerType": {
            "type": "string",
            "description": "`ADULT`, `SENIOR`, `YOUNG`, `CHILD`, `SEATED_INFANT` or `HELD_INFANT` (a lap infant).",
            "deprecated": true,
            "x-replaced-by": "passenger_type"
          },
          "passenger_type": {
            "type": "string",
            "description": "`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": {
            "type": "string",
            "description": "The fare option, for example `STANDARD`."
          },
          "price": {
            "$ref": "#/components/schemas/FlightTravelerPrice"
          },
          "fareDetailsBySegment": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightFareDetail"
            },
            "description": "The fare of this traveler on every segment."
          }
        },
        "required": [
          "travelerId",
          "travelerType",
          "price"
        ]
      },
      "FlightOfferSupplier": {
        "type": "object",
        "description": "The supplier the offer comes from.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the supplier: `amadeus_aqc_dz_1` or `duffel_1` in the sandbox.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "Id of the supplier: `amadeus_aqc_dz_1` or `duffel_1` in the sandbox. The snake_case twin of `id`, with the same value: use this name."
          },
          "name": {
            "type": "string",
            "description": "Name of the supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_name"
          },
          "supplier_name": {
            "type": "string",
            "description": "Name of the supplier. The snake_case twin of `name`, with the same value: use this name."
          },
          "originalId": {
            "type": "string",
            "description": "The supplier's own id of the offer. For support only.",
            "deprecated": true,
            "x-replaced-by": "supplier_offer_id"
          },
          "supplier_offer_id": {
            "type": "string",
            "description": "The supplier's own id of the offer. For support only. The snake_case twin of `originalId`, with the same value: use this name."
          }
        },
        "required": [
          "id",
          "name",
          "originalId"
        ]
      },
      "FlightOffer": {
        "type": "object",
        "description": "One bookable flight offer of one supplier: the route, the price, the fare rules and the price of every traveler. Search returns it under `suppliers[n].results.offers[]`; `price_flight` returns the priced version under `results.offers[]`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the offer: `fx_offer_` and 8 hex characters. Send it as `offer_id` to `price_flight` and `book_flight`.",
            "deprecated": true,
            "x-replaced-by": "offer_id"
          },
          "offer_id": {
            "type": "string",
            "description": "The id of the offer: `fx_offer_` and 8 hex characters. Send it as `offer_id` to `price_flight` and `book_flight`. The snake_case twin of `id`, with the same value: use this name."
          },
          "type": {
            "type": "string",
            "enum": [
              "flight_offer"
            ],
            "description": "Always `flight_offer`."
          },
          "supplier": {
            "$ref": "#/components/schemas/FlightOfferSupplier"
          },
          "source": {
            "type": "string",
            "description": "Priced offer only: where the offer comes from, `duffel` or `GDS`."
          },
          "bookableSeats": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Seats left at this fare, when the supplier says (Amadeus: up to 9)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Seats left at this fare, when the supplier says (Amadeus: up to 9)."
          },
          "supportHold": {
            "type": "boolean",
            "description": "`true` when the offer can be held (booked without payment) with `booking_type: \"hold\"`. Read it again on the priced offer.",
            "deprecated": true,
            "x-replaced-by": "support_hold"
          },
          "support_hold": {
            "type": "boolean",
            "description": "`true` when the offer can be held (booked without payment) with `booking_type: \"hold\"`. Read it again on the priced offer. The snake_case twin of `supportHold`, with the same value: use this name."
          },
          "supportVoid": {
            "type": "boolean",
            "description": "`true` when the ticket can be voided during the same day.",
            "deprecated": true,
            "x-replaced-by": "support_void"
          },
          "support_void": {
            "type": "boolean",
            "description": "`true` when the ticket can be voided during the same day. The snake_case twin of `supportVoid`, with the same value: use this name."
          },
          "voidDeadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 8601 UTC time when the same-day void window ends, `null` when the supplier cannot void."
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 UTC time when the same-day void window ends, `null` when the supplier cannot void.",
            "deprecated": true,
            "x-replaced-by": "void_deadline"
          },
          "void_deadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 8601 UTC time when the same-day void window ends, `null` when the supplier cannot void."
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 UTC time when the same-day void window ends, `null` when the supplier cannot void. The snake_case twin of `voidDeadline`, with the same value: use this name."
          },
          "filtersApplied": {
            "description": "The filters you asked for, echoed. It says what you asked for, not what the supplier honoured.",
            "additionalProperties": true,
            "type": "object"
          },
          "warnings": {
            "type": "array",
            "items": {
              "description": "A note of the supplier.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Notes of the supplier about this offer. Empty in every sample."
          },
          "instantTicketingRequired": {
            "type": "boolean",
            "description": "`true`: the fare cannot be held; book it with `booking_type: \"issue\"`."
          },
          "lastTicketingDate": {
            "type": "string",
            "description": "Duffel only: the day the offer stops being sellable."
          },
          "lastTicketingDateTime": {
            "type": "string",
            "description": "Duffel only: the moment the offer stops being sellable. Typically 15 to 60 minutes after the search."
          },
          "pricing": {
            "$ref": "#/components/schemas/FlightPricing"
          },
          "itinerary": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightItinerary"
            },
            "description": "One entry for each direction."
          },
          "travelerPricing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightTravelerPricing"
            },
            "description": "One entry for each priced traveler."
          },
          "conditions": {
            "$ref": "#/components/schemas/FlightConditions"
          },
          "fareRules": {
            "type": "object",
            "description": "The fare rules of the offer: what exchange, refund and revalidation allow.",
            "properties": {
              "exchange": {
                "type": "object",
                "description": "What the fare says about an exchange before departure.",
                "properties": {
                  "allowed": {
                    "type": "boolean",
                    "description": "Whether an exchange is allowed before departure. Indicative, not a promise: the fare rules of the airline decide."
                  }
                },
                "required": [
                  "allowed"
                ],
                "additionalProperties": true
              },
              "refund": {
                "type": "object",
                "description": "What the fare says about a refund before departure.",
                "properties": {
                  "allowed": {
                    "type": "boolean",
                    "description": "Whether a refund is allowed before departure. Indicative, not a promise: the fare rules of the airline decide."
                  }
                },
                "required": [
                  "allowed"
                ],
                "additionalProperties": true
              },
              "revalidation": {
                "type": "object",
                "description": "What the fare says about a revalidation before departure.",
                "properties": {
                  "allowed": {
                    "type": "boolean",
                    "description": "Whether a revalidation is allowed before departure. Indicative, not a promise: the fare rules of the airline decide."
                  }
                },
                "required": [
                  "allowed"
                ],
                "additionalProperties": true
              },
              "supplierCurrency": {
                "type": "string",
                "description": "Currency of the penalty amounts, when there are some."
              },
              "agencyCurrency": {
                "type": "string",
                "description": "Currency of your agency."
              }
            },
            "additionalProperties": true
          },
          "validatingAirline": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "IATA code of the airline."
                },
                "name": {
                  "type": "string",
                  "description": "Name of the airline."
                }
              },
              "required": [
                "code"
              ]
            },
            "description": "The airline whose rules and ticket stock govern the fare."
          },
          "additionalServices": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "description": "A paid extra the supplier lists.",
                  "additionalProperties": true,
                  "type": "object"
                }
              },
              {
                "description": "`baggage`: the extra bags that can be added.",
                "additionalProperties": true,
                "type": "object"
              }
            ],
            "description": "Paid extras. A list in the offers of a search (Amadeus only); an object with a `baggage` list in a priced offer."
          },
          "requiresACD": {
            "type": "boolean",
            "description": "Amadeus Algeria only, present only when `true`: the fare is an ACD fare and the booking needs passports (see `upload_acd_documents`)."
          }
        },
        "required": [
          "id",
          "type",
          "supplier",
          "pricing",
          "itinerary",
          "travelerPricing"
        ],
        "examples": [
          {
            "id": "fx_offer_9ab6d940",
            "type": "flight_offer",
            "supplier": {
              "id": "duffel_1",
              "name": "Duffel",
              "originalId": "off_034q6JKdiQDZ8HIDMFus64"
            },
            "bookableSeats": null,
            "supportHold": false,
            "supportVoid": false,
            "voidDeadline": null,
            "filtersApplied": {},
            "warnings": [],
            "instantTicketingRequired": false,
            "lastTicketingDate": "2026-11-01",
            "lastTicketingDateTime": "2026-11-01T15:52:40.830072Z",
            "pricing": {
              "supplierCurrency": "USD",
              "agencyCurrency": "DZD",
              "basePrice": 16385,
              "taxesAndFees": 5226.2,
              "agencyCommission": 21.62,
              "finalPrice": 21632.82,
              "b2bPrice": 21611.2,
              "additionalServices": []
            },
            "itinerary": [
              {
                "direction": "outbound",
                "duration": "PT2H24M",
                "stops": 0,
                "segments": [
                  {
                    "id": "seg_0000BC0a5iUChSV6Qrz0Co",
                    "departure": {
                      "airportCode": "ALG",
                      "airportName": "Houari Boumediene Airport",
                      "cityName": "Algiers",
                      "countryCode": "DZ",
                      "terminal": "2",
                      "datetime": "2026-12-15T10:50:00",
                      "timeZone": "Africa/Algiers"
                    },
                    "arrival": {
                      "airportCode": "CDG",
                      "airportName": "Paris Charles de Gaulle Airport",
                      "cityName": "Paris",
                      "countryCode": "FR",
                      "terminal": "1",
                      "datetime": "2026-12-15T13:14:00",
                      "timeZone": "Europe/Paris"
                    },
                    "flight": {
                      "carrierCode": "ZZ",
                      "carrierName": "Duffel Airways",
                      "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                      "flightNumber": "7426",
                      "aircraftCode": null,
                      "aircraftName": null,
                      "operatingCarrier": "ZZ",
                      "operatingCarrierName": "Duffel Airways"
                    },
                    "duration": "PT2H24M",
                    "cabin": "ECONOMY",
                    "bookingClass": null,
                    "fareBasis": "Y20LGTN2",
                    "brandedFare": "BASIC",
                    "brandedFareLabel": "Basic"
                  }
                ],
                "conditions": {
                  "changeBeforeDeparture": {
                    "allowed": false
                  },
                  "refundBeforeDeparture": {
                    "allowed": false
                  }
                }
              }
            ],
            "travelerPricing": [
              {
                "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                "travelerType": "ADULT",
                "fareOption": "STANDARD",
                "price": {
                  "supplierCurrency": "USD",
                  "agencyCurrency": "DZD",
                  "base": 16385,
                  "total": 21632.82,
                  "b2bPrice": 21611.2
                },
                "fareDetailsBySegment": [
                  {
                    "segmentId": "seg_0000BC0a5iUChSV6Qrz0Co",
                    "cabin": "ECONOMY",
                    "fareBasis": "Y20LGTN2",
                    "brandedFare": "BASIC",
                    "brandedFareLabel": "Basic",
                    "bookingClass": null,
                    "includedCheckedBags": {
                      "quantity": 1
                    },
                    "includedCabinBags": {
                      "quantity": 1
                    },
                    "amenities": [
                      {
                        "description": "WIFI",
                        "isChargeable": true,
                        "amenityType": "WIFI",
                        "amenityProvider": {
                          "name": "BrandedFare"
                        }
                      },
                      {
                        "description": "POWER SUPPLY",
                        "isChargeable": false,
                        "amenityType": "ENTERTAINMENT",
                        "amenityProvider": {
                          "name": "BrandedFare"
                        }
                      }
                    ]
                  }
                ]
              }
            ],
            "conditions": {
              "refundBeforeDeparture": {
                "allowed": false
              },
              "changeBeforeDeparture": {
                "allowed": false
              }
            },
            "fareRules": {
              "exchange": {
                "allowed": false
              },
              "refund": {
                "allowed": false
              },
              "revalidation": {
                "allowed": false
              },
              "supplierCurrency": "USD",
              "agencyCurrency": "DZD"
            },
            "validatingAirline": [
              {
                "code": "ZZ",
                "name": "Duffel Airways"
              }
            ]
          }
        ]
      },
      "FlightSupplierMeta": {
        "type": "object",
        "description": "Who answered and how fast.",
        "properties": {
          "supplierId": {
            "type": "string",
            "description": "Id of the supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "Id of the supplier. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "technologyType": {
            "type": "string",
            "description": "The technology of the supplier: `amadeus_aqc` or `duffel`."
          },
          "supplierName": {
            "type": "string",
            "description": "Name of the supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_name"
          },
          "supplier_name": {
            "type": "string",
            "description": "Name of the supplier. The snake_case twin of `supplierName`, with the same value: use this name."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds this supplier took.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds this supplier took. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "supplierCurrency": {
            "type": "string",
            "description": "Currency in which the supplier prices."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of your agency."
          }
        },
        "required": [
          "supplierId",
          "technologyType",
          "supplierName",
          "supplierCurrency",
          "agencyCurrency"
        ]
      },
      "FlightSupplierResults": {
        "type": "object",
        "description": "The offers of one supplier.",
        "properties": {
          "totalOffers": {
            "type": "integer",
            "description": "Number of offers of this result set. Always equals the length of `offers`: there is no pagination."
          },
          "supplierCurrency": {
            "type": "string",
            "description": "Currency in which the supplier prices."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of your agency."
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightOffer"
            },
            "description": "The offers, in the order the supplier ranks them."
          }
        },
        "required": [
          "totalOffers",
          "offers"
        ]
      },
      "FlightFlexFlight": {
        "type": "object",
        "description": "The cheapest flight behind the price of a date.",
        "properties": {
          "carrierCode": {
            "type": "string",
            "description": "IATA code of the marketing airline."
          },
          "flightNumber": {
            "type": "string",
            "description": "Flight number **with** the carrier code (`IB1378`), unlike the offers."
          },
          "operatingCarrier": {
            "type": "string",
            "description": "IATA code of the operating airline."
          },
          "validatingAirline": {
            "type": "string",
            "description": "IATA code of the validating airline, as a text (a list of objects in offers)."
          },
          "duration": {
            "type": "string",
            "description": "ISO 8601 duration."
          },
          "stops": {
            "type": "integer",
            "description": "Number of stops."
          },
          "departureTime": {
            "type": "string",
            "description": "`HH:MM`."
          },
          "arrivalTime": {
            "type": "string",
            "description": "`HH:MM`."
          },
          "departureAirport": {
            "type": "string",
            "description": "IATA code."
          },
          "arrivalAirport": {
            "type": "string",
            "description": "IATA code."
          },
          "cabinClass": {
            "type": "string",
            "description": "Cabin of the fare."
          },
          "brandedFare": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fare family code."
              },
              {
                "type": "null"
              }
            ],
            "description": "Fare family code."
          },
          "brandedFareLabel": {
            "anyOf": [
              {
                "type": "string",
                "description": "Display label of the fare family."
              },
              {
                "type": "null"
              }
            ],
            "description": "Display label of the fare family."
          },
          "includedCheckedBags": {
            "type": "object",
            "description": "Checked baggage included.",
            "properties": {
              "quantity": {
                "type": "integer",
                "description": "Number of checked bags included."
              }
            },
            "required": [
              "quantity"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "FlightFlexEntry": {
        "type": "object",
        "description": "The cheapest fare of one date (or date pair). The price fields are null when `status` is `no_flights_available`.",
        "properties": {
          "departure": {
            "type": "string",
            "description": "Candidate departure date, `YYYY-MM-DD`."
          },
          "return": {
            "type": "string",
            "description": "Candidate return date. Only for a round trip or an open-jaw."
          },
          "status": {
            "type": "string",
            "enum": [
              "available",
              "no_flights_available"
            ],
            "description": "`available` when the supplier has a fare for this date or pair."
          },
          "finalPrice": {
            "anyOf": [
              {
                "type": "number",
                "description": "What the traveler pays, in `agencyCurrency`."
              },
              {
                "type": "null"
              }
            ],
            "description": "What the traveler pays, in `agencyCurrency`.",
            "deprecated": true,
            "x-replaced-by": "final_price"
          },
          "final_price": {
            "anyOf": [
              {
                "type": "number",
                "description": "What the traveler pays, in `agencyCurrency`."
              },
              {
                "type": "null"
              }
            ],
            "description": "What the traveler pays, in `agencyCurrency`. The snake_case twin of `finalPrice`, with the same value: use this name."
          },
          "basePrice": {
            "anyOf": [
              {
                "type": "number",
                "description": "The airline's base fare."
              },
              {
                "type": "null"
              }
            ],
            "description": "The airline's base fare.",
            "deprecated": true,
            "x-replaced-by": "base_price"
          },
          "base_price": {
            "anyOf": [
              {
                "type": "number",
                "description": "The airline's base fare."
              },
              {
                "type": "null"
              }
            ],
            "description": "The airline's base fare. The snake_case twin of `basePrice`, with the same value: use this name."
          },
          "taxesAndFees": {
            "anyOf": [
              {
                "type": "number",
                "description": "Taxes and fees."
              },
              {
                "type": "null"
              }
            ],
            "description": "Taxes and fees.",
            "deprecated": true,
            "x-replaced-by": "taxes_and_fees"
          },
          "taxes_and_fees": {
            "anyOf": [
              {
                "type": "number",
                "description": "Taxes and fees."
              },
              {
                "type": "null"
              }
            ],
            "description": "Taxes and fees. The snake_case twin of `taxesAndFees`, with the same value: use this name."
          },
          "agencyCommission": {
            "anyOf": [
              {
                "type": "number",
                "description": "Your commission."
              },
              {
                "type": "null"
              }
            ],
            "description": "Your commission.",
            "deprecated": true,
            "x-replaced-by": "agency_commission"
          },
          "agency_commission": {
            "anyOf": [
              {
                "type": "number",
                "description": "Your commission."
              },
              {
                "type": "null"
              }
            ],
            "description": "Your commission. The snake_case twin of `agencyCommission`, with the same value: use this name."
          },
          "b2bPrice": {
            "anyOf": [
              {
                "type": "number",
                "description": "The amount debited from your balance."
              },
              {
                "type": "null"
              }
            ],
            "description": "The amount debited from your balance.",
            "deprecated": true,
            "x-replaced-by": "b2b_price"
          },
          "b2b_price": {
            "anyOf": [
              {
                "type": "number",
                "description": "The amount debited from your balance."
              },
              {
                "type": "null"
              }
            ],
            "description": "The amount debited from your balance. The snake_case twin of `b2bPrice`, with the same value: use this name."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of the prices of the entry."
          },
          "flight": {
            "$ref": "#/components/schemas/FlightFlexFlight"
          }
        },
        "required": [
          "departure",
          "status"
        ]
      },
      "FlightFlexPrices": {
        "type": "object",
        "description": "The date matrix of a flexible search: one cheapest fare for each candidate date. An entry has no offer id: to book a date, search for it.",
        "properties": {
          "supplierCurrency": {
            "type": "string",
            "description": "Currency in which the supplier prices."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of the prices of `matrix`."
          },
          "matrix": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightFlexEntry"
            },
            "description": "One entry for each date (one-way: 7 dates) or date pair (round trip, open-jaw: up to 49)."
          }
        },
        "required": [
          "matrix"
        ]
      },
      "FlightSupplierResult": {
        "type": "object",
        "description": "The answer of one supplier: its own `searchId` and its offers. A supplier that answers in two batches (Amadeus Algeria, in the stream) appears as two result sets with two different `searchId` values.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether this supplier returned usable results."
          },
          "searchId": {
            "type": "string",
            "description": "**Save this.** `fs_<uuid>` (Amadeus Algeria) or `fs_dfx_<uuid>` (Duffel). Send it as `search_id` to `price_flight` and `book_flight`. Valid for 24 hours, but a supplier withdraws offers earlier.",
            "deprecated": true,
            "x-replaced-by": "search_id"
          },
          "search_id": {
            "type": "string",
            "description": "**Save this.** `fs_<uuid>` (Amadeus Algeria) or `fs_dfx_<uuid>` (Duffel). Send it as `search_id` to `price_flight` and `book_flight`. Valid for 24 hours, but a supplier withdraws offers earlier. The snake_case twin of `searchId`, with the same value: use this name."
          },
          "supportHold": {
            "type": "boolean",
            "description": "What the supplier can do in general: hold. Read it again on the priced offer.",
            "deprecated": true,
            "x-replaced-by": "support_hold"
          },
          "support_hold": {
            "type": "boolean",
            "description": "What the supplier can do in general: hold. Read it again on the priced offer. The snake_case twin of `supportHold`, with the same value: use this name."
          },
          "supportVoid": {
            "type": "boolean",
            "description": "What the supplier can do in general: same-day void.",
            "deprecated": true,
            "x-replaced-by": "support_void"
          },
          "support_void": {
            "type": "boolean",
            "description": "What the supplier can do in general: same-day void. The snake_case twin of `supportVoid`, with the same value: use this name."
          },
          "voidDeadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 8601 UTC end of the same-day void window, `null` when the supplier cannot void."
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 UTC end of the same-day void window, `null` when the supplier cannot void.",
            "deprecated": true,
            "x-replaced-by": "void_deadline"
          },
          "void_deadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 8601 UTC end of the same-day void window, `null` when the supplier cannot void."
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 UTC end of the same-day void window, `null` when the supplier cannot void. The snake_case twin of `voidDeadline`, with the same value: use this name."
          },
          "filtersApplied": {
            "description": "The filters you asked for, echoed.",
            "additionalProperties": true,
            "type": "object",
            "deprecated": true,
            "x-replaced-by": "filters_applied"
          },
          "filters_applied": {
            "description": "The filters you asked for, echoed. The snake_case twin of `filtersApplied`, with the same value: use this name.",
            "additionalProperties": true,
            "type": "object"
          },
          "warnings": {
            "type": "array",
            "items": {
              "description": "A note of the supplier.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Notes of the supplier."
          },
          "meta": {
            "$ref": "#/components/schemas/FlightSupplierMeta"
          },
          "results": {
            "$ref": "#/components/schemas/FlightSupplierResults"
          },
          "exchangeRate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's (Duffel: USD to DZD, 250.0). Every amount is already converted.",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's (Duffel: USD to DZD, 250.0). Every amount is already converted. The snake_case twin of `exchangeRate`, with the same value: use this name."
          },
          "flexPrices": {
            "$ref": "#/components/schemas/FlightFlexPrices"
          }
        },
        "required": [
          "success",
          "searchId",
          "meta"
        ]
      },
      "FlightFailure": {
        "type": "object",
        "description": "A supplier that did not answer. The search still succeeds when another supplier answered.",
        "properties": {
          "supplierId": {
            "type": "string",
            "description": "The supplier that failed.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "The supplier that failed. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "error": {
            "type": "string",
            "description": "A fixed sentence about the cause, never the supplier's own text."
          },
          "cause": {
            "type": "string",
            "enum": [
              "supplier_timeout",
              "supplier_api_error",
              "supplier_auth_error",
              "supplier_error",
              "supplier_temporarily_unavailable",
              "supplier_busy",
              "service_overloaded"
            ],
            "description": "Why the supplier failed: a stable code to branch on. `supplier_temporarily_unavailable`: the supplier is skipped for a short while after repeated failures. `supplier_busy`: FX-Port's capacity towards the supplier is full for a moment, the supplier was not called, retry in a few seconds. `service_overloaded`: FX-Port was too busy to prepare the results of the supplier, retry in a few seconds."
          }
        },
        "required": [
          "supplierId",
          "error"
        ]
      },
      "FlightSearchSummary": {
        "type": "object",
        "description": "Counters of the search.",
        "properties": {
          "totalSuppliers": {
            "type": "integer",
            "description": "The suppliers that answered (the same as `successfulSuppliers`). In the stream: the suppliers that were called."
          },
          "successfulSuppliers": {
            "type": "integer",
            "description": "Suppliers that returned results."
          },
          "failedSuppliers": {
            "type": "integer",
            "description": "Suppliers listed in `failures`."
          },
          "totalOffers": {
            "type": "integer",
            "description": "Offers of all the suppliers together."
          }
        },
        "required": [
          "totalSuppliers",
          "successfulSuppliers",
          "failedSuppliers",
          "totalOffers"
        ]
      },
      "FlightSearchResponse": {
        "type": "object",
        "description": "Search results grouped by supplier. Save the supplier-level `searchId` and the `id` of the offer you want to price.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` on every `200`, even if some suppliers failed: read `suppliers[n].success` and `failures`."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this search, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "searchType": {
            "type": "string",
            "enum": [
              "ONE_WAY",
              "ROUND_TRIP",
              "OPEN_JAW",
              "MULTI_CITY"
            ],
            "description": "The type of the search, derived from your route.",
            "deprecated": true,
            "x-replaced-by": "search_type"
          },
          "search_type": {
            "type": "string",
            "enum": [
              "ONE_WAY",
              "ROUND_TRIP",
              "OPEN_JAW",
              "MULTI_CITY"
            ],
            "description": "The type of the search, derived from your route. The snake_case twin of `searchType`, with the same value: use this name."
          },
          "flexible": {
            "type": "boolean",
            "description": "Whether the date matrix was requested."
          },
          "flexibleOnly": {
            "type": "boolean",
            "description": "Only present (and `true`) for a flexible-only search."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent by the whole search.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent by the whole search. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "suppliers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightSupplierResult"
            },
            "description": "One entry for each supplier that answered."
          },
          "summary": {
            "$ref": "#/components/schemas/FlightSearchSummary"
          },
          "failures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightFailure"
            },
            "description": "Only when a supplier failed."
          }
        },
        "required": [
          "success",
          "suppliers",
          "summary"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "54139f32",
            "searchType": "ONE_WAY",
            "flexible": false,
            "processingTime": 2.243,
            "suppliers": [
              {
                "success": true,
                "searchId": "fs_e1853430-16e2-4bcb-8f11-4f7bfbc33602",
                "supportHold": true,
                "supportVoid": true,
                "voidDeadline": "2026-11-01T23:00:00Z",
                "filtersApplied": {},
                "warnings": [],
                "meta": {
                  "supplierId": "amadeus_aqc_dz_1",
                  "technologyType": "amadeus_aqc",
                  "supplierName": "Amadeus Algeria",
                  "processingTime": 2.174,
                  "supplierCurrency": "DZD",
                  "agencyCurrency": "DZD"
                },
                "results": {
                  "totalOffers": 66,
                  "supplierCurrency": "DZD",
                  "agencyCurrency": "DZD",
                  "offers": [
                    {
                      "id": "fx_offer_b1c2a850",
                      "type": "flight_offer",
                      "supplier": {
                        "id": "amadeus_aqc_dz_1",
                        "name": "Amadeus Algeria",
                        "originalId": "6"
                      },
                      "bookableSeats": 9,
                      "supportHold": true,
                      "supportVoid": true,
                      "voidDeadline": "2026-11-01T23:00:00Z",
                      "filtersApplied": {},
                      "warnings": [],
                      "instantTicketingRequired": false,
                      "pricing": {
                        "supplierCurrency": "DZD",
                        "agencyCurrency": "DZD",
                        "basePrice": 18520,
                        "taxesAndFees": 11306.17,
                        "agencyCommission": 29.83,
                        "finalPrice": 29856,
                        "b2bPrice": 29826.17,
                        "acd": {
                          "applied": false
                        },
                        "additionalServices": []
                      },
                      "itinerary": [
                        {
                          "direction": "outbound",
                          "duration": "PT2H30M",
                          "stops": 0,
                          "segments": [
                            {
                              "id": "27",
                              "departure": {
                                "airportCode": "ALG",
                                "airportName": "Houari Boumediene Airport",
                                "cityName": "Algiers",
                                "countryCode": "DZ",
                                "terminal": "4",
                                "datetime": "2026-12-15T07:15:00",
                                "timeZone": "Africa/Algiers"
                              },
                              "arrival": {
                                "airportCode": "CDG",
                                "airportName": "Paris Charles de Gaulle Airport",
                                "cityName": "Paris",
                                "countryCode": "FR",
                                "terminal": "2D",
                                "datetime": "2026-12-15T09:45:00",
                                "timeZone": "Europe/Paris"
                              },
                              "flight": {
                                "carrierCode": "AH",
                                "carrierName": "AIR ALGERIE",
                                "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                                "flightNumber": "1002",
                                "aircraftCode": "7M8",
                                "aircraftName": "BOEING 737 MAX 8",
                                "operatingCarrier": "AH",
                                "operatingCarrierName": "AIR ALGERIE"
                              },
                              "duration": "PT2H30M",
                              "cabin": "ECONOMY",
                              "bookingClass": "Q",
                              "fareBasis": "QDZOO",
                              "brandedFare": "STANDARD",
                              "brandedFareLabel": ""
                            }
                          ],
                          "conditions": {
                            "refundBeforeDeparture": {
                              "allowed": false
                            },
                            "changeBeforeDeparture": {
                              "allowed": true
                            }
                          }
                        }
                      ],
                      "travelerPricing": [
                        {
                          "travelerId": "1",
                          "travelerType": "ADULT",
                          "fareOption": "STANDARD",
                          "price": {
                            "supplierCurrency": "DZD",
                            "agencyCurrency": "DZD",
                            "base": 18520,
                            "total": 29856,
                            "b2bPrice": 29826.17
                          },
                          "fareDetailsBySegment": [
                            {
                              "segmentId": "27",
                              "cabin": "ECONOMY",
                              "fareBasis": "QDZOO",
                              "brandedFare": "STANDARD",
                              "brandedFareLabel": "",
                              "bookingClass": "Q",
                              "includedCheckedBags": {
                                "quantity": 1
                              },
                              "includedCabinBags": {
                                "quantity": 1
                              },
                              "amenities": []
                            }
                          ]
                        }
                      ],
                      "conditions": {
                        "refundBeforeDeparture": {
                          "allowed": false
                        },
                        "changeBeforeDeparture": {
                          "allowed": true
                        }
                      },
                      "fareRules": {
                        "exchange": {
                          "allowed": true
                        },
                        "refund": {
                          "allowed": false
                        },
                        "revalidation": {
                          "allowed": false
                        },
                        "supplierCurrency": "DZD",
                        "agencyCurrency": "DZD"
                      },
                      "validatingAirline": [
                        {
                          "code": "AH",
                          "name": "AIR ALGERIE"
                        }
                      ],
                      "additionalServices": []
                    }
                  ]
                }
              },
              {
                "success": true,
                "searchId": "fs_dfx_a3aeaa36-74ad-424b-b88d-e6bdc9a4ba94",
                "supportHold": false,
                "supportVoid": false,
                "voidDeadline": null,
                "filtersApplied": {},
                "warnings": [],
                "meta": {
                  "supplierId": "duffel_1",
                  "technologyType": "duffel",
                  "supplierName": "Duffel",
                  "processingTime": 1.34,
                  "supplierCurrency": "USD",
                  "agencyCurrency": "DZD"
                },
                "results": {
                  "totalOffers": 92,
                  "supplierCurrency": "USD",
                  "agencyCurrency": "DZD",
                  "offers": [
                    {
                      "id": "fx_offer_9ab6d940",
                      "type": "flight_offer",
                      "supplier": {
                        "id": "duffel_1",
                        "name": "Duffel",
                        "originalId": "off_034q6JKdiQDZ8HIDMFus64"
                      },
                      "bookableSeats": null,
                      "supportHold": false,
                      "supportVoid": false,
                      "voidDeadline": null,
                      "filtersApplied": {},
                      "warnings": [],
                      "instantTicketingRequired": false,
                      "lastTicketingDate": "2026-11-01",
                      "lastTicketingDateTime": "2026-11-01T15:52:40.830072Z",
                      "pricing": {
                        "supplierCurrency": "USD",
                        "agencyCurrency": "DZD",
                        "basePrice": 16385,
                        "taxesAndFees": 5226.2,
                        "agencyCommission": 21.62,
                        "finalPrice": 21632.82,
                        "b2bPrice": 21611.2,
                        "additionalServices": []
                      },
                      "itinerary": [
                        {
                          "direction": "outbound",
                          "duration": "PT2H24M",
                          "stops": 0,
                          "segments": [
                            {
                              "id": "seg_0000BC0a5iUChSV6Qrz0Co",
                              "departure": {
                                "airportCode": "ALG",
                                "airportName": "Houari Boumediene Airport",
                                "cityName": "Algiers",
                                "countryCode": "DZ",
                                "terminal": "2",
                                "datetime": "2026-12-15T10:50:00",
                                "timeZone": "Africa/Algiers"
                              },
                              "arrival": {
                                "airportCode": "CDG",
                                "airportName": "Paris Charles de Gaulle Airport",
                                "cityName": "Paris",
                                "countryCode": "FR",
                                "terminal": "1",
                                "datetime": "2026-12-15T13:14:00",
                                "timeZone": "Europe/Paris"
                              },
                              "flight": {
                                "carrierCode": "ZZ",
                                "carrierName": "Duffel Airways",
                                "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                                "flightNumber": "7426",
                                "aircraftCode": null,
                                "aircraftName": null,
                                "operatingCarrier": "ZZ",
                                "operatingCarrierName": "Duffel Airways"
                              },
                              "duration": "PT2H24M",
                              "cabin": "ECONOMY",
                              "bookingClass": null,
                              "fareBasis": "Y20LGTN2",
                              "brandedFare": "BASIC",
                              "brandedFareLabel": "Basic"
                            }
                          ],
                          "conditions": {
                            "changeBeforeDeparture": {
                              "allowed": false
                            },
                            "refundBeforeDeparture": {
                              "allowed": false
                            }
                          }
                        }
                      ],
                      "travelerPricing": [
                        {
                          "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                          "travelerType": "ADULT",
                          "fareOption": "STANDARD",
                          "price": {
                            "supplierCurrency": "USD",
                            "agencyCurrency": "DZD",
                            "base": 16385,
                            "total": 21632.82,
                            "b2bPrice": 21611.2
                          },
                          "fareDetailsBySegment": [
                            {
                              "segmentId": "seg_0000BC0a5iUChSV6Qrz0Co",
                              "cabin": "ECONOMY",
                              "fareBasis": "Y20LGTN2",
                              "brandedFare": "BASIC",
                              "brandedFareLabel": "Basic",
                              "bookingClass": null,
                              "includedCheckedBags": {
                                "quantity": 1
                              },
                              "includedCabinBags": {
                                "quantity": 1
                              },
                              "amenities": [
                                {
                                  "description": "WIFI",
                                  "isChargeable": true,
                                  "amenityType": "WIFI",
                                  "amenityProvider": {
                                    "name": "BrandedFare"
                                  }
                                },
                                {
                                  "description": "POWER SUPPLY",
                                  "isChargeable": false,
                                  "amenityType": "ENTERTAINMENT",
                                  "amenityProvider": {
                                    "name": "BrandedFare"
                                  }
                                }
                              ]
                            }
                          ]
                        }
                      ],
                      "conditions": {
                        "refundBeforeDeparture": {
                          "allowed": false
                        },
                        "changeBeforeDeparture": {
                          "allowed": false
                        }
                      },
                      "fareRules": {
                        "exchange": {
                          "allowed": false
                        },
                        "refund": {
                          "allowed": false
                        },
                        "revalidation": {
                          "allowed": false
                        },
                        "supplierCurrency": "USD",
                        "agencyCurrency": "DZD"
                      },
                      "validatingAirline": [
                        {
                          "code": "ZZ",
                          "name": "Duffel Airways"
                        }
                      ]
                    }
                  ]
                },
                "exchangeRate": 250
              }
            ],
            "summary": {
              "totalSuppliers": 2,
              "successfulSuppliers": 2,
              "failedSuppliers": 0,
              "totalOffers": 158
            }
          }
        ]
      },
      "FlightPriceResponse": {
        "type": "object",
        "description": "The current, confirmed version of one offer: the price to book, `supportHold` after pricing and, for Amadeus, the extra bags that can be bought.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the offer was priced."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "offerId": {
            "type": "string",
            "description": "The `id` of the priced offer.",
            "deprecated": true,
            "x-replaced-by": "offer_id"
          },
          "offer_id": {
            "type": "string",
            "description": "The `id` of the priced offer. The snake_case twin of `offerId`, with the same value: use this name."
          },
          "offerType": {
            "type": "string",
            "enum": [
              "priced"
            ],
            "description": "Always `priced`."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "supplierCurrency": {
            "type": "string",
            "description": "Currency in which the supplier prices."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of your agency."
          },
          "results": {
            "type": "object",
            "description": "The priced offer.",
            "properties": {
              "totalOffers": {
                "type": "integer",
                "description": "Always 1."
              },
              "supplierCurrency": {
                "type": "string",
                "description": "Currency in which the supplier prices."
              },
              "agencyCurrency": {
                "type": "string",
                "description": "Currency of your agency."
              },
              "offers": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/FlightOffer"
                },
                "description": "The priced offer."
              }
            },
            "required": [
              "offers"
            ]
          },
          "exchangeRate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's.",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's. The snake_case twin of `exchangeRate`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "offerId",
          "results"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "76f8fc58",
            "offerId": "fx_offer_89d6592c",
            "offerType": "priced",
            "processingTime": 0.177,
            "supplierCurrency": "USD",
            "agencyCurrency": "DZD",
            "results": {
              "totalOffers": 1,
              "supplierCurrency": "USD",
              "agencyCurrency": "DZD",
              "offers": [
                {
                  "id": "fx_offer_89d6592c",
                  "type": "flight_offer",
                  "supplier": {
                    "id": "duffel_1",
                    "name": "Duffel",
                    "originalId": "off_034q6JKduVe2mSW1fH8lge"
                  },
                  "source": "duffel",
                  "bookableSeats": null,
                  "supportHold": false,
                  "supportVoid": false,
                  "voidDeadline": null,
                  "filtersApplied": {},
                  "warnings": [],
                  "instantTicketingRequired": false,
                  "pricing": {
                    "supplierCurrency": "USD",
                    "agencyCurrency": "DZD",
                    "basePrice": 16975,
                    "taxesAndFees": 5366.8,
                    "agencyCommission": 22.35,
                    "finalPrice": 22364.15,
                    "b2bPrice": 22341.8,
                    "additionalServices": []
                  },
                  "itinerary": [
                    {
                      "direction": "outbound",
                      "duration": "PT2H24M",
                      "stops": 0,
                      "segments": [
                        {
                          "id": "seg_0000BC0a5iVGdVLqUATqqr",
                          "departure": {
                            "airportCode": "ALG",
                            "airportName": "Houari Boumediene Airport",
                            "cityName": "Algiers",
                            "countryCode": "DZ",
                            "terminal": "2",
                            "datetime": "2026-12-15T10:50:00",
                            "timeZone": "Africa/Algiers"
                          },
                          "arrival": {
                            "airportCode": "CDG",
                            "airportName": "Paris Charles de Gaulle Airport",
                            "cityName": "Paris",
                            "countryCode": "FR",
                            "terminal": "1",
                            "datetime": "2026-12-15T13:14:00",
                            "timeZone": "Europe/Paris"
                          },
                          "flight": {
                            "carrierCode": "BA",
                            "carrierName": "British Airways",
                            "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/BA.svg",
                            "flightNumber": "0107",
                            "aircraftCode": null,
                            "aircraftName": null,
                            "operatingCarrier": "BA",
                            "operatingCarrierName": "British Airways"
                          },
                          "duration": "PT2H24M",
                          "cabin": "ECONOMY",
                          "bookingClass": null,
                          "fareBasis": "Y20LGTN2",
                          "brandedFare": "Basic",
                          "brandedFareLabel": "Basic"
                        }
                      ],
                      "conditions": {
                        "changeBeforeDeparture": {
                          "allowed": true
                        },
                        "refundBeforeDeparture": {
                          "allowed": true
                        }
                      }
                    }
                  ],
                  "travelerPricing": [
                    {
                      "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                      "travelerType": "ADULT",
                      "fareOption": "STANDARD",
                      "price": {
                        "supplierCurrency": "USD",
                        "agencyCurrency": "DZD",
                        "base": 16975,
                        "total": 22364.15,
                        "b2bPrice": 22341.8
                      },
                      "fareDetailsBySegment": [
                        {
                          "segmentId": "seg_0000BC0a5iVGdVLqUATqqr",
                          "cabin": "ECONOMY",
                          "fareBasis": "Y20LGTN2",
                          "brandedFare": "Basic",
                          "brandedFareLabel": "Basic",
                          "bookingClass": "Economy",
                          "includedCheckedBags": {
                            "quantity": 1
                          },
                          "includedCabinBags": {
                            "quantity": 1
                          },
                          "amenities": []
                        }
                      ]
                    }
                  ],
                  "conditions": {
                    "refundBeforeDeparture": {
                      "allowed": true
                    },
                    "changeBeforeDeparture": {
                      "allowed": true
                    }
                  },
                  "fareRules": {
                    "refund": {
                      "allowed": true
                    },
                    "exchange": {
                      "allowed": true
                    },
                    "supplierCurrency": "USD",
                    "agencyCurrency": "DZD"
                  },
                  "validatingAirline": [
                    {
                      "code": "BA",
                      "name": "British Airways"
                    }
                  ],
                  "additionalServices": {
                    "baggage": [
                      {
                        "id": "ase_0000BC0aGG1cCZZ8LCieTb",
                        "type": "checked",
                        "quantity": 1,
                        "maxQuantity": 1,
                        "maxWeightKg": 23,
                        "maxWeightUnit": "kg",
                        "priceAmount": 20,
                        "convertedPriceAmount": 5000,
                        "priceCurrency": "USD",
                        "applicablePassengers": [
                          "pas_0000BC0a5iH5UE0bmBqWkl"
                        ],
                        "applicableSegments": [
                          "seg_0000BC0a5iVGdVLqUATqqr"
                        ],
                        "bookableByItinerary": false,
                        "metadata": {
                          "name": "CHECKED",
                          "description": "Additional checked"
                        }
                      }
                    ]
                  }
                }
              ]
            },
            "exchangeRate": 250
          }
        ]
      },
      "FlightStreamStart": {
        "type": "object",
        "description": "The first event (`id` 0), sent at once.",
        "properties": {
          "environment": {
            "type": "string",
            "description": "`sandbox` or `live`. Only this event carries it, as its first key."
          },
          "type": {
            "type": "string",
            "enum": [
              "start"
            ],
            "description": "Always `start`."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this search, 8 characters."
          },
          "searchType": {
            "type": "string",
            "description": "The raw classification of the route: `ONE WAY`, `ROUNDTRIP`, `MULTIPLE CITY` or `OPEN`. Not the vocabulary of the JSON mode: read `search_type`."
          },
          "search_type": {
            "type": "string",
            "enum": [
              "ONE_WAY",
              "ROUND_TRIP",
              "OPEN_JAW",
              "MULTI_CITY"
            ],
            "description": "The type of the search in the vocabulary of the JSON mode."
          },
          "flexible": {
            "type": "boolean",
            "description": "Whether the date matrix was requested."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          },
          "expectedSuppliers": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A supplier id."
            },
            "description": "The suppliers called for this search: the compatible ones."
          }
        },
        "required": [
          "type",
          "requestId",
          "searchType",
          "flexible",
          "timestamp",
          "expectedSuppliers"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "type": "start",
            "requestId": "d36b6918",
            "searchType": "ONE WAY",
            "flexible": false,
            "timestamp": "2026-11-01T15:22:57.762071",
            "expectedSuppliers": [
              "amadeus_aqc_dz_1",
              "duffel_1"
            ],
            "search_type": "ONE_WAY"
          }
        ]
      },
      "FlightStreamMeta": {
        "type": "object",
        "description": "The supplier block of a result set of the stream.",
        "properties": {
          "filtersApplied": {
            "description": "The filters you asked for, echoed.",
            "additionalProperties": true,
            "type": "object",
            "deprecated": true,
            "x-replaced-by": "filters_applied"
          },
          "filters_applied": {
            "description": "The filters you asked for, echoed. The snake_case twin of `filtersApplied`, with the same value: use this name.",
            "additionalProperties": true,
            "type": "object"
          },
          "warnings": {
            "type": "array",
            "items": {
              "description": "A note of the supplier.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Notes of the supplier."
          },
          "supplierId": {
            "type": "string",
            "description": "Id of the supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "Id of the supplier. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "technologyType": {
            "type": "string",
            "description": "`amadeus_aqc` or `duffel`."
          },
          "supportHold": {
            "type": "boolean",
            "description": "Whether the supplier can hold.",
            "deprecated": true,
            "x-replaced-by": "support_hold"
          },
          "support_hold": {
            "type": "boolean",
            "description": "Whether the supplier can hold. The snake_case twin of `supportHold`, with the same value: use this name."
          },
          "supportVoid": {
            "type": "boolean",
            "description": "Whether the supplier can void the same day.",
            "deprecated": true,
            "x-replaced-by": "support_void"
          },
          "support_void": {
            "type": "boolean",
            "description": "Whether the supplier can void the same day. The snake_case twin of `supportVoid`, with the same value: use this name."
          },
          "voidDeadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "End of the same-day void window."
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the same-day void window.",
            "deprecated": true,
            "x-replaced-by": "void_deadline"
          },
          "void_deadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "End of the same-day void window."
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the same-day void window. The snake_case twin of `voidDeadline`, with the same value: use this name."
          }
        },
        "required": [
          "supplierId",
          "technologyType"
        ]
      },
      "FlightStreamResults": {
        "type": "object",
        "description": "The offers of a result set.",
        "properties": {
          "totalOffers": {
            "type": "integer",
            "description": "The number of offers of **this result set**. For a chunked set it is the total of the chunks."
          },
          "supplierCurrency": {
            "type": "string",
            "description": "Currency in which the supplier prices."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of your agency."
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightOffer"
            },
            "description": "The offers: all of them for `supplier_result`; empty for `supplier_start`, whose offers follow in `offers_chunk` events."
          },
          "chunked": {
            "type": "boolean",
            "description": "Only on `supplier_start`: `true`."
          },
          "chunkSize": {
            "type": "integer",
            "description": "Only on `supplier_start`: offers in a chunk (50)."
          },
          "totalChunks": {
            "type": "integer",
            "description": "Only on `supplier_start`: how many `offers_chunk` events follow."
          }
        },
        "required": [
          "totalOffers",
          "offers"
        ]
      },
      "FlightStreamSupplierData": {
        "type": "object",
        "description": "A result set: the answer of one supplier, or one later batch of it.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether this supplier returned usable results."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this search.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "request_id": {
            "type": "string",
            "description": "The id of this search. The snake_case twin of `requestId`, with the same value: use this name."
          },
          "searchId": {
            "type": "string",
            "description": "The `searchId` of **this result set**. Price and book an offer with the `searchId` of the result set it arrived in.",
            "deprecated": true,
            "x-replaced-by": "search_id"
          },
          "search_id": {
            "type": "string",
            "description": "The `searchId` of **this result set**. Price and book an offer with the `searchId` of the result set it arrived in. The snake_case twin of `searchId`, with the same value: use this name."
          },
          "searchType": {
            "type": "string",
            "description": "`ONE_WAY`, `ROUND_TRIP`, `OPEN_JAW` or `MULTI_CITY`.",
            "deprecated": true,
            "x-replaced-by": "search_type"
          },
          "search_type": {
            "type": "string",
            "description": "`ONE_WAY`, `ROUND_TRIP`, `OPEN_JAW` or `MULTI_CITY`. The snake_case twin of `searchType`, with the same value: use this name."
          },
          "flexible": {
            "type": "boolean",
            "description": "Only when the search asked for the date matrix: `true`."
          },
          "flexibleOnly": {
            "type": "boolean",
            "description": "Only for a flexible-only search: `true`."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds this supplier took.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds this supplier took. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "supplierCurrency": {
            "type": "string",
            "description": "Currency in which the supplier prices."
          },
          "agencyCurrency": {
            "type": "string",
            "description": "Currency of your agency."
          },
          "results": {
            "$ref": "#/components/schemas/FlightStreamResults"
          },
          "meta": {
            "$ref": "#/components/schemas/FlightStreamMeta"
          },
          "exchangeRate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's.",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's. The snake_case twin of `exchangeRate`, with the same value: use this name."
          },
          "flexPrices": {
            "$ref": "#/components/schemas/FlightFlexPrices"
          }
        },
        "required": [
          "success",
          "searchId",
          "results",
          "meta"
        ]
      },
      "FlightStreamSupplierResult": {
        "type": "object",
        "description": "A supplier answered. Keep one list of offers for each `searchId`.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "supplier_result",
              "supplier_start"
            ],
            "description": "`supplier_result`: the whole result set (50 offers or fewer). `supplier_start`: the result set without its offers (more than 50): they follow in `offers_chunk` events."
          },
          "supplierId": {
            "type": "string",
            "description": "The supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "The supplier. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "success": {
            "type": "boolean",
            "description": "Always `true` here."
          },
          "data": {
            "$ref": "#/components/schemas/FlightStreamSupplierData"
          },
          "complete": {
            "type": "boolean",
            "description": "Only on `supplier_result`: `false` means that **another batch of this supplier follows**, under another `searchId`."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds this supplier took.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds this supplier took. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "supplierId",
          "success",
          "data",
          "timestamp"
        ],
        "examples": [
          {
            "type": "supplier_result",
            "supplierId": "amadeus_aqc_dz_1",
            "success": true,
            "data": {
              "success": true,
              "requestId": "d36b6918",
              "searchId": "fs_35418d61-6092-44e4-81b7-a6fe985feb79",
              "searchType": "ONE_WAY",
              "processingTime": 2.516,
              "supplierCurrency": "DZD",
              "agencyCurrency": "DZD",
              "results": {
                "totalOffers": 2,
                "supplierCurrency": "DZD",
                "agencyCurrency": "DZD",
                "offers": [
                  {
                    "id": "fx_offer_b1c2a850",
                    "type": "flight_offer",
                    "supplier": {
                      "id": "amadeus_aqc_dz_1",
                      "name": "Amadeus Algeria",
                      "originalId": "6"
                    },
                    "bookableSeats": 9,
                    "supportHold": true,
                    "supportVoid": true,
                    "voidDeadline": "2026-11-01T23:00:00Z",
                    "filtersApplied": {},
                    "warnings": [],
                    "instantTicketingRequired": false,
                    "pricing": {
                      "supplierCurrency": "DZD",
                      "agencyCurrency": "DZD",
                      "basePrice": 18520,
                      "taxesAndFees": 11306.17,
                      "agencyCommission": 29.83,
                      "finalPrice": 29856,
                      "b2bPrice": 29826.17,
                      "acd": {
                        "applied": false
                      },
                      "additionalServices": []
                    },
                    "itinerary": [
                      {
                        "direction": "outbound",
                        "duration": "PT2H30M",
                        "stops": 0,
                        "segments": [
                          {
                            "id": "27",
                            "departure": {
                              "airportCode": "ALG",
                              "airportName": "Houari Boumediene Airport",
                              "cityName": "Algiers",
                              "countryCode": "DZ",
                              "terminal": "4",
                              "datetime": "2026-12-15T07:15:00",
                              "timeZone": "Africa/Algiers"
                            },
                            "arrival": {
                              "airportCode": "CDG",
                              "airportName": "Paris Charles de Gaulle Airport",
                              "cityName": "Paris",
                              "countryCode": "FR",
                              "terminal": "2D",
                              "datetime": "2026-12-15T09:45:00",
                              "timeZone": "Europe/Paris"
                            },
                            "flight": {
                              "carrierCode": "AH",
                              "carrierName": "AIR ALGERIE",
                              "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                              "flightNumber": "1002",
                              "aircraftCode": "7M8",
                              "aircraftName": "BOEING 737 MAX 8",
                              "operatingCarrier": "AH",
                              "operatingCarrierName": "AIR ALGERIE"
                            },
                            "duration": "PT2H30M",
                            "cabin": "ECONOMY",
                            "bookingClass": "Q",
                            "fareBasis": "QDZOO",
                            "brandedFare": "STANDARD",
                            "brandedFareLabel": ""
                          }
                        ],
                        "conditions": {
                          "refundBeforeDeparture": {
                            "allowed": false
                          },
                          "changeBeforeDeparture": {
                            "allowed": true
                          }
                        }
                      }
                    ],
                    "travelerPricing": [
                      {
                        "travelerId": "1",
                        "travelerType": "ADULT",
                        "fareOption": "STANDARD",
                        "price": {
                          "supplierCurrency": "DZD",
                          "agencyCurrency": "DZD",
                          "base": 18520,
                          "total": 29856,
                          "b2bPrice": 29826.17
                        },
                        "fareDetailsBySegment": [
                          {
                            "segmentId": "27",
                            "cabin": "ECONOMY",
                            "fareBasis": "QDZOO",
                            "brandedFare": "STANDARD",
                            "brandedFareLabel": "",
                            "bookingClass": "Q",
                            "includedCheckedBags": {
                              "quantity": 1
                            },
                            "includedCabinBags": {
                              "quantity": 1
                            },
                            "amenities": []
                          }
                        ]
                      }
                    ],
                    "conditions": {
                      "refundBeforeDeparture": {
                        "allowed": false
                      },
                      "changeBeforeDeparture": {
                        "allowed": true
                      }
                    },
                    "fareRules": {
                      "exchange": {
                        "allowed": true
                      },
                      "refund": {
                        "allowed": false
                      },
                      "revalidation": {
                        "allowed": false
                      },
                      "supplierCurrency": "DZD",
                      "agencyCurrency": "DZD"
                    },
                    "validatingAirline": [
                      {
                        "code": "AH",
                        "name": "AIR ALGERIE"
                      }
                    ],
                    "additionalServices": []
                  }
                ]
              },
              "meta": {
                "filtersApplied": {},
                "warnings": [],
                "supplierId": "amadeus_aqc_dz_1",
                "technologyType": "amadeus_aqc",
                "supportHold": true,
                "supportVoid": true,
                "voidDeadline": "2026-11-01T23:00:00Z"
              }
            },
            "complete": true,
            "processingTime": 2.516,
            "timestamp": "2026-11-01T15:23:00.284737"
          }
        ]
      },
      "FlightStreamOffersChunk": {
        "type": "object",
        "description": "Up to 50 offers of a chunked result set. One event can be up to 210 KB on one line.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "offers_chunk"
            ],
            "description": "Always `offers_chunk`."
          },
          "supplierId": {
            "type": "string",
            "description": "The supplier. Add the offers to the result set opened by the last `supplier_start` of this supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "The supplier. Add the offers to the result set opened by the last `supplier_start` of this supplier. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "chunkNumber": {
            "type": "integer",
            "description": "The number of this chunk, from 1."
          },
          "totalChunks": {
            "type": "integer",
            "description": "How many chunks the result set has."
          },
          "offersInChunk": {
            "type": "integer",
            "description": "Offers in this chunk (50 at most)."
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightOffer"
            },
            "description": "The offers of this chunk."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "supplierId",
          "chunkNumber",
          "totalChunks",
          "offersInChunk",
          "offers",
          "timestamp"
        ],
        "examples": [
          {
            "type": "offers_chunk",
            "supplierId": "duffel_1",
            "chunkNumber": 1,
            "totalChunks": 2,
            "offersInChunk": 50,
            "offers": [
              {
                "id": "fx_offer_9ab6d940",
                "type": "flight_offer",
                "supplier": {
                  "id": "duffel_1",
                  "name": "Duffel",
                  "originalId": "off_034q6JKdiQDZ8HIDMFus64"
                },
                "bookableSeats": null,
                "supportHold": false,
                "supportVoid": false,
                "voidDeadline": null,
                "filtersApplied": {},
                "warnings": [],
                "instantTicketingRequired": false,
                "lastTicketingDate": "2026-11-01",
                "lastTicketingDateTime": "2026-11-01T15:52:40.830072Z",
                "pricing": {
                  "supplierCurrency": "USD",
                  "agencyCurrency": "DZD",
                  "basePrice": 16385,
                  "taxesAndFees": 5226.2,
                  "agencyCommission": 21.62,
                  "finalPrice": 21632.82,
                  "b2bPrice": 21611.2,
                  "additionalServices": []
                },
                "itinerary": [
                  {
                    "direction": "outbound",
                    "duration": "PT2H24M",
                    "stops": 0,
                    "segments": [
                      {
                        "id": "seg_0000BC0a5iUChSV6Qrz0Co",
                        "departure": {
                          "airportCode": "ALG",
                          "airportName": "Houari Boumediene Airport",
                          "cityName": "Algiers",
                          "countryCode": "DZ",
                          "terminal": "2",
                          "datetime": "2026-12-15T10:50:00",
                          "timeZone": "Africa/Algiers"
                        },
                        "arrival": {
                          "airportCode": "CDG",
                          "airportName": "Paris Charles de Gaulle Airport",
                          "cityName": "Paris",
                          "countryCode": "FR",
                          "terminal": "1",
                          "datetime": "2026-12-15T13:14:00",
                          "timeZone": "Europe/Paris"
                        },
                        "flight": {
                          "carrierCode": "ZZ",
                          "carrierName": "Duffel Airways",
                          "carrierLogoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/ZZ.svg",
                          "flightNumber": "7426",
                          "aircraftCode": null,
                          "aircraftName": null,
                          "operatingCarrier": "ZZ",
                          "operatingCarrierName": "Duffel Airways"
                        },
                        "duration": "PT2H24M",
                        "cabin": "ECONOMY",
                        "bookingClass": null,
                        "fareBasis": "Y20LGTN2",
                        "brandedFare": "BASIC",
                        "brandedFareLabel": "Basic"
                      }
                    ],
                    "conditions": {
                      "changeBeforeDeparture": {
                        "allowed": false
                      },
                      "refundBeforeDeparture": {
                        "allowed": false
                      }
                    }
                  }
                ],
                "travelerPricing": [
                  {
                    "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl",
                    "travelerType": "ADULT",
                    "fareOption": "STANDARD",
                    "price": {
                      "supplierCurrency": "USD",
                      "agencyCurrency": "DZD",
                      "base": 16385,
                      "total": 21632.82,
                      "b2bPrice": 21611.2
                    },
                    "fareDetailsBySegment": [
                      {
                        "segmentId": "seg_0000BC0a5iUChSV6Qrz0Co",
                        "cabin": "ECONOMY",
                        "fareBasis": "Y20LGTN2",
                        "brandedFare": "BASIC",
                        "brandedFareLabel": "Basic",
                        "bookingClass": null,
                        "includedCheckedBags": {
                          "quantity": 1
                        },
                        "includedCabinBags": {
                          "quantity": 1
                        },
                        "amenities": [
                          {
                            "description": "WIFI",
                            "isChargeable": true,
                            "amenityType": "WIFI",
                            "amenityProvider": {
                              "name": "BrandedFare"
                            }
                          },
                          {
                            "description": "POWER SUPPLY",
                            "isChargeable": false,
                            "amenityType": "ENTERTAINMENT",
                            "amenityProvider": {
                              "name": "BrandedFare"
                            }
                          }
                        ]
                      }
                    ]
                  }
                ],
                "conditions": {
                  "refundBeforeDeparture": {
                    "allowed": false
                  },
                  "changeBeforeDeparture": {
                    "allowed": false
                  }
                },
                "fareRules": {
                  "exchange": {
                    "allowed": false
                  },
                  "refund": {
                    "allowed": false
                  },
                  "revalidation": {
                    "allowed": false
                  },
                  "supplierCurrency": "USD",
                  "agencyCurrency": "DZD"
                },
                "validatingAirline": [
                  {
                    "code": "ZZ",
                    "name": "Duffel Airways"
                  }
                ]
              }
            ],
            "timestamp": "2026-11-01T15:22:59.161771"
          }
        ]
      },
      "FlightStreamFlexPrices": {
        "type": "object",
        "description": "The date matrix of a chunked result set (`flexible: true`). Not seen in the sandbox: treat it as optional.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "flex_prices"
            ],
            "description": "Always `flex_prices`."
          },
          "supplierId": {
            "type": "string",
            "description": "The supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "The supplier. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "flexPrices": {
            "$ref": "#/components/schemas/FlightFlexPrices"
          },
          "count": {
            "type": "integer",
            "description": "Entries of the matrix."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "supplierId",
          "flexPrices",
          "timestamp"
        ]
      },
      "FlightStreamSupplierComplete": {
        "type": "object",
        "description": "The last chunk of a result set was sent.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "supplier_complete"
            ],
            "description": "Always `supplier_complete`."
          },
          "supplierId": {
            "type": "string",
            "description": "The supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "The supplier. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "totalOffers": {
            "type": "integer",
            "description": "Offers of **that result set**. The total of the search is in `summary`."
          },
          "hasFlexPrices": {
            "type": "boolean",
            "description": "Whether the result set carries a date matrix."
          },
          "flexPricesCount": {
            "type": "integer",
            "description": "Entries of the matrix."
          },
          "complete": {
            "type": "boolean",
            "description": "`false`: another batch of this supplier follows."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "supplierId",
          "totalOffers",
          "complete",
          "timestamp"
        ],
        "examples": [
          {
            "type": "supplier_complete",
            "supplierId": "amadeus_aqc_dz_1",
            "totalOffers": 68,
            "hasFlexPrices": false,
            "flexPricesCount": 0,
            "complete": false,
            "timestamp": "2026-11-01T15:22:59.819079"
          }
        ]
      },
      "FlightStreamError": {
        "type": "object",
        "description": "One supplier failed. The other suppliers go on and the stream still ends with `summary` and `done`.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Always `error`."
          },
          "supplierId": {
            "type": "string",
            "description": "The supplier that failed.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "The supplier that failed. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "success": {
            "type": "boolean",
            "description": "Always `false`."
          },
          "error": {
            "type": "string",
            "description": "A fixed sentence about the cause, never the supplier's own text."
          },
          "cause": {
            "type": "string",
            "enum": [
              "supplier_timeout",
              "supplier_api_error",
              "supplier_auth_error",
              "supplier_error",
              "supplier_temporarily_unavailable",
              "supplier_busy",
              "service_overloaded"
            ],
            "description": "Why the supplier failed: a stable code to branch on. `supplier_timeout` also marks the suppliers still running when the stream reached its 300 s limit. `supplier_busy`: FX-Port's capacity towards the supplier is full for a moment, the supplier was not called, retry the search in a few seconds. `service_overloaded`: FX-Port was too busy to prepare the results of the supplier, retry the search in a few seconds."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "supplierId",
          "success",
          "error",
          "timestamp"
        ],
        "examples": [
          {
            "type": "error",
            "supplierId": "duffel_1",
            "success": false,
            "error": "The supplier rejected the search request.",
            "cause": "supplier_error",
            "processingTime": 0,
            "timestamp": "2026-11-01T15:23:32.838533"
          }
        ]
      },
      "FlightStreamSummary": {
        "type": "object",
        "description": "After every supplier has answered or failed, just before `done`.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "summary"
            ],
            "description": "Always `summary`."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this search."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when at least one supplier returned offers."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds the whole search took."
          },
          "summary": {
            "type": "object",
            "description": "The counters of the search. (In the same event `successfulSuppliers` is a list next to this object.)",
            "properties": {
              "totalSuppliers": {
                "type": "integer",
                "description": "The suppliers that were called."
              },
              "successfulSuppliers": {
                "type": "integer",
                "description": "Suppliers that returned offers."
              },
              "failedSuppliers": {
                "type": "integer",
                "description": "Suppliers that failed."
              },
              "totalOffers": {
                "type": "integer",
                "description": "Offers of all result sets together."
              }
            },
            "required": [
              "totalSuppliers",
              "successfulSuppliers",
              "failedSuppliers",
              "totalOffers"
            ]
          },
          "successfulSuppliers": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "A supplier that returned offers.",
              "properties": {
                "supplierId": {
                  "type": "string",
                  "description": "The supplier.",
                  "deprecated": true,
                  "x-replaced-by": "supplier_id"
                },
                "supplier_id": {
                  "type": "string",
                  "description": "The supplier. The snake_case twin of `supplierId`, with the same value: use this name."
                },
                "offersCount": {
                  "type": "integer",
                  "description": "Its offers."
                },
                "processingTime": {
                  "type": "number",
                  "description": "Seconds it took."
                }
              },
              "required": [
                "supplierId",
                "offersCount"
              ]
            },
            "description": "The suppliers that returned offers."
          },
          "failures": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "A supplier that failed.",
                  "properties": {
                    "supplierId": {
                      "type": "string",
                      "description": "The supplier.",
                      "deprecated": true,
                      "x-replaced-by": "supplier_id"
                    },
                    "supplier_id": {
                      "type": "string",
                      "description": "The supplier. The snake_case twin of `supplierId`, with the same value: use this name."
                    },
                    "error": {
                      "type": "string",
                      "description": "A fixed sentence about the cause."
                    },
                    "cause": {
                      "type": "string",
                      "description": "The stable cause."
                    }
                  },
                  "required": [
                    "supplierId",
                    "error"
                  ]
                },
                "description": "The suppliers that failed: `null` when none did."
              },
              {
                "type": "null"
              }
            ],
            "description": "The suppliers that failed: `null` when none did."
          },
          "truncated": {
            "type": "boolean",
            "description": "Only present (and `true`) when the stream reached its 300 s limit: the suppliers that had not answered are in `failures` with the cause `supplier_timeout`."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "requestId",
          "success",
          "summary",
          "successfulSuppliers",
          "timestamp"
        ],
        "examples": [
          {
            "type": "summary",
            "requestId": "d36b6918",
            "success": true,
            "processingTime": 2.581,
            "summary": {
              "totalSuppliers": 2,
              "successfulSuppliers": 2,
              "failedSuppliers": 0,
              "totalOffers": 162
            },
            "successfulSuppliers": [
              {
                "supplierId": "duffel_1",
                "offersCount": 92,
                "processingTime": 1.381
              },
              {
                "supplierId": "amadeus_aqc_dz_1",
                "offersCount": 70,
                "processingTime": 2.516
              }
            ],
            "failures": null,
            "timestamp": "2026-11-01T15:23:00.286463"
          }
        ]
      },
      "FlightStreamDone": {
        "type": "object",
        "description": "The last event, always: the stream is over only here.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "done"
            ],
            "description": "Always `done`."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this search."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 UTC time of the event, without a `Z`: `2026-11-01T15:22:59.175148`."
          }
        },
        "required": [
          "type",
          "requestId",
          "timestamp"
        ],
        "examples": [
          {
            "type": "done",
            "requestId": "d36b6918",
            "timestamp": "2026-11-01T15:23:00.286605"
          }
        ]
      },
      "FlightOrderBlock": {
        "type": "object",
        "description": "The order as the supplier knows it.",
        "properties": {
          "reference": {
            "type": "string",
            "description": "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`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "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": {
            "type": "string",
            "description": "`confirmed`, `pending` (a hold that is not paid yet), `awaiting_payment`, `cancelled`, `failed` or `partial_refund`."
          },
          "pnr": {
            "type": "string",
            "description": "The airline record locator. Not unique across suppliers and airlines."
          },
          "type": {
            "type": "string",
            "enum": [
              "instant",
              "hold"
            ],
            "description": "`instant`: ticketed at once. `hold`: reserved, not paid."
          },
          "createdAt": {
            "type": "string",
            "description": "When the order was created."
          },
          "cancelledAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "When the order was cancelled."
              },
              {
                "type": "null"
              }
            ],
            "description": "When the order was cancelled."
          },
          "content": {
            "type": "string",
            "description": "Who manages the order: `managed`."
          },
          "syncedAt": {
            "type": "string",
            "description": "When FX-Port last synchronised the order with the supplier."
          },
          "availableActions": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "An action the supplier allows: `confirm`, `cancel`, `change`."
            },
            "description": "What can still be done with the order."
          },
          "voidableUntil": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 8601 UTC end of the same-day void window."
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 UTC end of the same-day void window."
          },
          "cancellation": {
            "anyOf": [
              {
                "description": "How the order was cancelled: `type`, `deleted`, `deletedAt`.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "How the order was cancelled: `type`, `deleted`, `deletedAt`."
          }
        },
        "required": [
          "reference",
          "status",
          "pnr",
          "type"
        ],
        "additionalProperties": true
      },
      "FlightOrderSupplier": {
        "type": "object",
        "description": "The supplier of the order.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "type": "string",
            "description": "Id of the supplier. The snake_case twin of `id`, with the same value: use this name."
          },
          "technology": {
            "type": "string",
            "description": "The technology of the supplier: `amadeus_aqc` or `duffel`."
          },
          "name": {
            "type": "string",
            "description": "Name of the supplier.",
            "deprecated": true,
            "x-replaced-by": "supplier_name"
          },
          "supplier_name": {
            "type": "string",
            "description": "Name of the supplier. The snake_case twin of `name`, with the same value: use this name."
          },
          "offerId": {
            "anyOf": [
              {
                "type": "string",
                "description": "The supplier's own id of the offer."
              },
              {
                "type": "null"
              }
            ],
            "description": "The supplier's own id of the offer.",
            "deprecated": true,
            "x-replaced-by": "supplier_offer_id"
          },
          "supplier_offer_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "The supplier's own id of the offer."
              },
              {
                "type": "null"
              }
            ],
            "description": "The supplier's own id of the offer. The snake_case twin of `offerId`, with the same value: use this name."
          }
        },
        "required": [
          "id",
          "technology",
          "name"
        ]
      },
      "FlightOrderAgency": {
        "type": "object",
        "description": "Your agency. The id of the agency is not returned: you know it.",
        "properties": {
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency of your agency. Every amount of the response is in it."
          }
        },
        "required": [
          "currency"
        ],
        "additionalProperties": true
      },
      "FlightPaymentState": {
        "type": "object",
        "description": "The payment state of the order.",
        "properties": {
          "status": {
            "type": "string",
            "description": "`awaiting_payment` for a hold, `paid` once ticketed."
          },
          "paidAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "When the order was paid."
              },
              {
                "type": "null"
              }
            ],
            "description": "When the order was paid."
          },
          "paymentRequiredBy": {
            "anyOf": [
              {
                "type": "string",
                "description": "The payment deadline of a hold, when the supplier gives one."
              },
              {
                "type": "null"
              }
            ],
            "description": "The payment deadline of a hold, when the supplier gives one."
          },
          "priceGuaranteeExpiresAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "Until when the price is guaranteed."
              },
              {
                "type": "null"
              }
            ],
            "description": "Until when the price is guaranteed."
          },
          "awaitingPayment": {
            "type": "boolean",
            "description": "`true` for a hold that still has to be paid."
          }
        },
        "required": [
          "status",
          "awaitingPayment"
        ],
        "additionalProperties": true
      },
      "FlightOrderDocument": {
        "type": "object",
        "description": "A travel document of the order (an e-ticket).",
        "properties": {
          "type": {
            "type": "string",
            "description": "`electronic_ticket`."
          },
          "uniqueIdentifier": {
            "type": "string",
            "description": "The ticket number (Duffel sandbox: the text `1`)."
          },
          "passengerIds": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A passenger id."
            },
            "description": "The passengers the document is for."
          },
          "documentType": {
            "type": "string",
            "description": "`ETICKET`."
          },
          "status": {
            "type": "string",
            "description": "`ISSUED`."
          },
          "segmentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A segment id."
            },
            "description": "The segments the document covers."
          }
        },
        "required": [
          "type"
        ],
        "additionalProperties": true
      },
      "FlightBookingReference": {
        "type": "object",
        "description": "A record locator of an airline.",
        "properties": {
          "pnr": {
            "type": "string",
            "description": "The airline record locator."
          },
          "carrier": {
            "type": "object",
            "description": "The airline that issued the record locator.",
            "properties": {
              "iataCode": {
                "type": "string",
                "description": "IATA code of the airline."
              },
              "name": {
                "type": "string",
                "description": "Name of the airline."
              },
              "logoUrl": {
                "type": "string",
                "description": "Address of the logo."
              },
              "logoLockupUrl": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Address of the wide logo."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Address of the wide logo."
              }
            },
            "required": [
              "iataCode"
            ]
          }
        },
        "required": [
          "pnr"
        ]
      },
      "FlightPassengerRecord": {
        "type": "object",
        "description": "A passenger of the order, as the supplier stored it.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the passenger on the order: `\"1\"` (Amadeus) or `pas_...` (Duffel).",
            "deprecated": true,
            "x-replaced-by": "passenger_id"
          },
          "passenger_id": {
            "type": "string",
            "description": "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": {
            "type": "string",
            "description": "`ADULT`, `CHILD`, `INFANT_WITHOUT_SEAT` ... as the supplier writes it.",
            "deprecated": true,
            "x-replaced-by": "passenger_type"
          },
          "passenger_type": {
            "type": "string",
            "description": "`ADULT`, `CHILD`, `INFANT_WITHOUT_SEAT` ... as the supplier writes it. The snake_case twin of `type`, with the same value: use this name."
          },
          "title": {
            "type": "string",
            "description": "`mr`, `mrs` ... derived from the gender."
          },
          "givenName": {
            "type": "string",
            "description": "Given name, as stored (Amadeus writes it in upper case).",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "first_name": {
            "type": "string",
            "description": "Given name, as stored (Amadeus writes it in upper case). The snake_case twin of `givenName`, with the same value: use this name."
          },
          "familyName": {
            "type": "string",
            "description": "Family name, as stored.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          },
          "last_name": {
            "type": "string",
            "description": "Family name, as stored. The snake_case twin of `familyName`, with the same value: use this name."
          },
          "gender": {
            "type": "string",
            "description": "`male` or `female`."
          },
          "bornOn": {
            "type": "string",
            "description": "Date of birth, `YYYY-MM-DD`.",
            "deprecated": true,
            "x-replaced-by": "date_of_birth"
          },
          "date_of_birth": {
            "type": "string",
            "description": "Date of birth, `YYYY-MM-DD`. The snake_case twin of `bornOn`, with the same value: use this name."
          },
          "email": {
            "type": "string",
            "description": "The e-mail of the contact."
          },
          "phoneNumber": {
            "type": "string",
            "description": "The phone number of the contact.",
            "deprecated": true,
            "x-replaced-by": "phone_number"
          },
          "phone_number": {
            "type": "string",
            "description": "The phone number of the contact. The snake_case twin of `phoneNumber`, with the same value: use this name."
          },
          "infantPassengerId": {
            "anyOf": [
              {
                "type": "string",
                "description": "The id of the lap infant this adult carries."
              },
              {
                "type": "null"
              }
            ],
            "description": "The id of the lap infant this adult carries."
          },
          "loyaltyProgrammeAccounts": {
            "type": "array",
            "items": {
              "description": "A frequent-flyer account.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Frequent-flyer accounts."
          },
          "ticketNumber": {
            "anyOf": [
              {
                "type": "string",
                "description": "The ticket number of this passenger. `null` until the airline returns it."
              },
              {
                "type": "null"
              }
            ],
            "description": "The ticket number of this passenger. `null` until the airline returns it."
          }
        },
        "required": [
          "id",
          "type",
          "givenName",
          "familyName"
        ],
        "additionalProperties": true
      },
      "FlightOrderOwner": {
        "type": "object",
        "description": "The airline that owns the order.",
        "properties": {
          "id": {
            "anyOf": [
              {
                "type": "string",
                "description": "Duffel's id of the airline."
              },
              {
                "type": "null"
              }
            ],
            "description": "Duffel's id of the airline."
          },
          "iataCode": {
            "type": "string",
            "description": "IATA code of the airline that owns the order."
          },
          "name": {
            "type": "string",
            "description": "Name of the airline."
          },
          "logoUrl": {
            "type": "string",
            "description": "Address of the logo."
          },
          "logoLockupUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the wide logo."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the wide logo."
          },
          "conditionsOfCarriageUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the conditions of carriage."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the conditions of carriage."
          }
        },
        "required": [
          "iataCode",
          "name"
        ]
      },
      "FlightOrderMeta": {
        "type": "object",
        "description": "Capabilities of the order and what the supplier reports about it.",
        "properties": {
          "supportHold": {
            "type": "boolean",
            "description": "Whether the supplier can hold.",
            "deprecated": true,
            "x-replaced-by": "support_hold"
          },
          "support_hold": {
            "type": "boolean",
            "description": "Whether the supplier can hold. The snake_case twin of `supportHold`, with the same value: use this name."
          },
          "supportVoid": {
            "type": "boolean",
            "description": "Whether the supplier can void the ticket the same day.",
            "deprecated": true,
            "x-replaced-by": "support_void"
          },
          "support_void": {
            "type": "boolean",
            "description": "Whether the supplier can void the ticket the same day. The snake_case twin of `supportVoid`, with the same value: use this name."
          },
          "voidDeadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "End of the same-day void window."
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the same-day void window.",
            "deprecated": true,
            "x-replaced-by": "void_deadline"
          },
          "void_deadline": {
            "anyOf": [
              {
                "type": "string",
                "description": "End of the same-day void window."
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the same-day void window. The snake_case twin of `voidDeadline`, with the same value: use this name."
          },
          "voidWindowEndsAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "End of the void window, when the supplier gives it."
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the void window, when the supplier gives it."
          },
          "airlineInitiatedChanges": {
            "type": "array",
            "items": {
              "description": "A change of the airline.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Schedule changes the airline made."
          },
          "changes": {
            "type": "array",
            "items": {
              "description": "A change of the order.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Changes of the order."
          },
          "users": {
            "type": "array",
            "items": {
              "description": "A user of the order.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Users of the order."
          },
          "orderMetadata": {
            "anyOf": [
              {
                "description": "Supplier-specific metadata of the order.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Supplier-specific metadata of the order."
          }
        },
        "additionalProperties": true
      },
      "FlightOrderSegment": {
        "type": "object",
        "description": "One flight of the order.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the segment."
          },
          "departure": {
            "$ref": "#/components/schemas/FlightPlace"
          },
          "arrival": {
            "$ref": "#/components/schemas/FlightPlace"
          },
          "flight": {
            "type": "object",
            "description": "The airline and the number of the flight.",
            "properties": {
              "carrierCode": {
                "type": "string",
                "description": "IATA code of the marketing airline."
              },
              "flightNumber": {
                "type": "string",
                "description": "The flight number."
              },
              "operatingCarrier": {
                "type": "string",
                "description": "IATA code of the operating airline."
              }
            },
            "required": [
              "carrierCode",
              "flightNumber"
            ],
            "additionalProperties": true
          },
          "duration": {
            "type": "string",
            "description": "ISO 8601 duration."
          },
          "cabin": {
            "type": "string",
            "description": "The cabin."
          },
          "fareBasis": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fare basis code."
              },
              {
                "type": "null"
              }
            ],
            "description": "Fare basis code."
          },
          "passengerDetails": {
            "type": "array",
            "items": {
              "description": "The cabin, bags and seat of one passenger on this segment.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "What each passenger has on this segment."
          }
        },
        "required": [
          "id",
          "departure",
          "arrival",
          "flight"
        ],
        "additionalProperties": true
      },
      "FlightOrderItinerary": {
        "type": "object",
        "description": "One direction of the order.",
        "properties": {
          "direction": {
            "type": "string",
            "description": "`outbound`, `return` ..."
          },
          "duration": {
            "type": "string",
            "description": "ISO 8601 duration."
          },
          "stops": {
            "type": "integer",
            "description": "Number of stops."
          },
          "fareBrandName": {
            "type": "string",
            "description": "Name of the fare brand."
          },
          "origin": {
            "type": "object",
            "description": "Where this direction starts.",
            "properties": {
              "airportCode": {
                "type": "string",
                "description": "IATA code of the airport."
              },
              "airportName": {
                "type": "string",
                "description": "Name of the airport."
              },
              "cityName": {
                "type": "string",
                "description": "Name of the city."
              },
              "countryCode": {
                "type": "string",
                "description": "ISO 3166-1 alpha-2 country code."
              },
              "timeZone": {
                "type": "string",
                "description": "IANA time zone."
              }
            },
            "required": [
              "airportCode"
            ],
            "additionalProperties": true
          },
          "destination": {
            "type": "object",
            "description": "Where this direction ends.",
            "properties": {
              "airportCode": {
                "type": "string",
                "description": "IATA code of the airport."
              },
              "airportName": {
                "type": "string",
                "description": "Name of the airport."
              },
              "cityName": {
                "type": "string",
                "description": "Name of the city."
              },
              "countryCode": {
                "type": "string",
                "description": "ISO 3166-1 alpha-2 country code."
              },
              "timeZone": {
                "type": "string",
                "description": "IANA time zone."
              }
            },
            "required": [
              "airportCode"
            ],
            "additionalProperties": true
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightOrderSegment"
            },
            "description": "The flights of this direction."
          },
          "conditions": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FlightConditions"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "direction",
          "segments"
        ],
        "additionalProperties": true
      },
      "FlightOrderPostProcessing": {
        "type": "object",
        "description": "What FX-Port did after the supplier call.",
        "properties": {
          "bookingDbId": {
            "type": "string",
            "description": "The FX-Port id of the booking (a UUID). **The best key to store**: it is accepted as `booking_id` by `get_booking`."
          },
          "bucketLink": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the PDF of the ticket, `null` until it is generated. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the PDF of the ticket, `null` until it is generated. A public link."
          },
          "emailSent": {
            "type": "boolean",
            "description": "Whether the confirmation e-mail went out."
          },
          "bookingStatusUpdated": {
            "type": "boolean",
            "description": "`cancel_order`: whether the FX-Port booking was updated."
          },
          "refundMethod": {
            "type": "string",
            "description": "`confirm_cancel`: where the refund went: `balance`."
          }
        },
        "additionalProperties": true
      },
      "FlightInternalBooking": {
        "type": "object",
        "description": "The FX-Port identifiers of the booking.",
        "properties": {
          "bookingId": {
            "type": "string",
            "description": "The PNR."
          },
          "internalId": {
            "type": "string",
            "description": "The FX-Port id of the booking (a UUID), the same as `postProcessing.bookingDbId` of the booking call."
          }
        },
        "required": [
          "bookingId",
          "internalId"
        ]
      },
      "FlightOrder": {
        "type": "object",
        "description": "The order of a booking or of a payment: the supplier's order, the price, the passengers and the flights. `book_flight` answers it right after the booking, `pay_order` after the payment. When the supplier refuses the payment the answer is HTTP `200` with `success: false`, `paymentSucceeded: false` and `error`: the other keys are absent then.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the order was created (booking) or paid (payment)."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "paymentSucceeded": {
            "type": "boolean",
            "description": "`pay_order` only: whether the payment succeeded. Read it together with `success`."
          },
          "searchId": {
            "type": "string",
            "description": "`book_flight` only: the `search_id` you sent.",
            "deprecated": true,
            "x-replaced-by": "search_id"
          },
          "search_id": {
            "type": "string",
            "description": "`book_flight` only: the `search_id` you sent. The snake_case twin of `searchId`, with the same value: use this name."
          },
          "offerId": {
            "type": "string",
            "description": "`book_flight` only: the `offer_id` you sent.",
            "deprecated": true,
            "x-replaced-by": "offer_id"
          },
          "offer_id": {
            "type": "string",
            "description": "`book_flight` only: the `offer_id` you sent. The snake_case twin of `offerId`, with the same value: use this name."
          },
          "orderId": {
            "type": "string",
            "description": "`pay_order` only: the supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "`pay_order` only: the supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "paymentDetails": {
            "description": "`pay_order` only: `paymentId`, `status`, `type` (`issuance`), `paymentAmount`, `currency`, `createdAt`, `message`.",
            "additionalProperties": true,
            "type": "object"
          },
          "booking": {
            "$ref": "#/components/schemas/FlightOrderBlock"
          },
          "supplier": {
            "$ref": "#/components/schemas/FlightOrderSupplier"
          },
          "agency": {
            "$ref": "#/components/schemas/FlightOrderAgency"
          },
          "pricing": {
            "$ref": "#/components/schemas/FlightPricing"
          },
          "requiresACD": {
            "type": "boolean",
            "description": "Present only when `true`: the booking needs one passport copy for every traveler: see `upload_acd_documents`."
          },
          "payment": {
            "$ref": "#/components/schemas/FlightPaymentState"
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightOrderDocument"
            },
            "description": "The e-tickets. Empty for a hold."
          },
          "bookingReferences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightBookingReference"
            },
            "description": "The record locators."
          },
          "passengers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightPassengerRecord"
            },
            "description": "The passengers."
          },
          "travelerPricing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightTravelerPricing"
            },
            "description": "The price of each traveler."
          },
          "itinerary": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlightOrderItinerary"
            },
            "description": "The flights of the order."
          },
          "conditions": {
            "$ref": "#/components/schemas/FlightConditions"
          },
          "fareRules": {
            "description": "What exchange, refund and revalidation allow.",
            "additionalProperties": true,
            "type": "object"
          },
          "owner": {
            "$ref": "#/components/schemas/FlightOrderOwner"
          },
          "services": {
            "type": "array",
            "items": {
              "description": "A service bought with the order.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Extra services of the order (bags, seats). Empty."
          },
          "meta": {
            "$ref": "#/components/schemas/FlightOrderMeta"
          },
          "exchangeRate": {
            "type": "number",
            "description": "Only when the supplier's currency is not your agency's.",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "type": "number",
            "description": "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": {
            "$ref": "#/components/schemas/FlightInternalBooking"
          },
          "postProcessing": {
            "$ref": "#/components/schemas/FlightOrderPostProcessing"
          },
          "balanceDebited": {
            "type": "boolean",
            "description": "`book_flight` only. Always `false` in the sandbox: nothing is charged."
          },
          "transactionCreated": {
            "type": "boolean",
            "description": "`book_flight` only. Always `false` in the sandbox."
          },
          "payoutCreated": {
            "type": "boolean",
            "description": "`book_flight` only. Always `false` in the sandbox."
          },
          "error": {
            "$ref": "#/components/schemas/FlightSupplierRefusal",
            "description": "Only when `success` is `false`: the refusal of the supplier for `pay_order`."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "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`."
          }
        },
        "required": [
          "success"
        ],
        "examples": [
          {
            "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
          }
        ]
      },
      "FlightOrderQuote": {
        "type": "object",
        "description": "The current price and payment state of an order. An Amadeus order is answered from the stored booking; a Duffel order is priced by the supplier. When the supplier refuses the quote the answer is HTTP `200` with `success: false` and `error`: `orderQuote` is absent then.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the quote could be built."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "orderId": {
            "type": "string",
            "description": "The supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "orderQuote": {
            "type": "object",
            "description": "The quote.",
            "properties": {
              "orderId": {
                "type": "string",
                "description": "The supplier's order id.",
                "deprecated": true,
                "x-replaced-by": "order_id"
              },
              "order_id": {
                "type": "string",
                "description": "The supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
              },
              "bookingReference": {
                "type": "string",
                "description": "The PNR.",
                "deprecated": true,
                "x-replaced-by": "pnr"
              },
              "pnr": {
                "type": "string",
                "description": "The PNR. The snake_case twin of `bookingReference`, with the same value: use this name."
              },
              "status": {
                "type": "string",
                "description": "The status of the booking: `awaiting_payment`, `confirmed`, `cancelled`."
              },
              "pricing": {
                "$ref": "#/components/schemas/FlightPricing"
              },
              "paymentStatus": {
                "type": "object",
                "description": "Whether and until when the order can be paid.",
                "properties": {
                  "awaitingPayment": {
                    "type": "boolean",
                    "description": "`true` for a hold."
                  },
                  "paymentRequiredBy": {
                    "anyOf": [
                      {
                        "type": "string",
                        "description": "The payment deadline, when there is one."
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "The payment deadline, when there is one."
                  },
                  "priceGuaranteeExpiresAt": {
                    "anyOf": [
                      {
                        "type": "string",
                        "description": "Until when the price is guaranteed."
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Until when the price is guaranteed."
                  },
                  "priceGuaranteed": {
                    "type": "boolean",
                    "description": "Whether the price of the hold is guaranteed."
                  },
                  "canPay": {
                    "type": "boolean",
                    "description": "Whether `pay_order` can pay the order now."
                  }
                },
                "required": [
                  "awaitingPayment",
                  "canPay"
                ],
                "additionalProperties": true
              },
              "liveMode": {
                "type": "boolean",
                "description": "`false` in the sandbox."
              }
            },
            "required": [
              "orderId",
              "status",
              "pricing"
            ],
            "additionalProperties": true
          },
          "supplier": {
            "$ref": "#/components/schemas/FlightOrderSupplier"
          },
          "agency": {
            "$ref": "#/components/schemas/FlightOrderAgency"
          },
          "meta": {
            "description": "`priceGuaranteed`, `paymentDeadline`, `canPay`.",
            "additionalProperties": true,
            "type": "object"
          },
          "exchangeRate": {
            "description": "Duffel orders: `{ rate, marginPercent, fromCurrency, toCurrency }`.",
            "additionalProperties": true,
            "type": "object",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "description": "Duffel orders: `{ rate, marginPercent, fromCurrency, toCurrency }`. The snake_case twin of `exchangeRate`, with the same value: use this name.",
            "additionalProperties": true,
            "type": "object"
          },
          "error": {
            "$ref": "#/components/schemas/FlightSupplierRefusal",
            "description": "Only when `success` is `false`: the refusal of the supplier for `get_order_quote`."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "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`."
          }
        },
        "required": [
          "success"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "469d8803",
            "orderId": "eJzTd9ePNA9wdA0FAAs8AmE",
            "processingTime": 0.134,
            "orderQuote": {
              "orderId": "eJzTd9ePNA9wdA0FAAs8AmE",
              "bookingReference": "Y7PAEU",
              "status": "awaiting_payment",
              "pricing": {
                "supplierCurrency": "DZD",
                "agencyCurrency": "DZD",
                "basePrice": 3160,
                "taxesAndFees": 1765.44,
                "agencyCommission": 4.93,
                "finalPrice": 4930.37,
                "b2bPrice": 4925.44,
                "additionalServices": [],
                "totalAmount": 4925.44
              },
              "paymentStatus": {
                "awaitingPayment": true,
                "paymentRequiredBy": null,
                "priceGuaranteeExpiresAt": null,
                "priceGuaranteed": true,
                "canPay": true
              },
              "liveMode": false
            },
            "supplier": {
              "id": "amadeus_aqc_dz_1",
              "technology": "amadeus_aqc",
              "name": "Amadeus Algeria"
            },
            "agency": {
              "currency": "DZD"
            },
            "meta": {
              "priceGuaranteed": true,
              "paymentDeadline": null,
              "canPay": true
            }
          }
        ]
      },
      "FlightCancelOrderResponse": {
        "type": "object",
        "description": "The reservation of a hold was deleted and the booking is `cancelled`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the reservation was deleted."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "orderId": {
            "type": "string",
            "description": "The supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "booking": {
            "$ref": "#/components/schemas/FlightOrderBlock"
          },
          "supplier": {
            "$ref": "#/components/schemas/FlightOrderSupplier"
          },
          "agency": {
            "$ref": "#/components/schemas/FlightOrderAgency"
          },
          "cancellation": {
            "type": "object",
            "description": "The result of the cancellation.",
            "properties": {
              "status": {
                "type": "string",
                "description": "`deleted`."
              },
              "deleted": {
                "type": "boolean",
                "description": "`true` when the reservation was deleted at the supplier."
              },
              "deletedAt": {
                "type": "string",
                "description": "When it was deleted."
              },
              "refundAmount": {
                "type": "number",
                "description": "The refund: `0` for a hold (nothing was paid)."
              },
              "refundCurrency": {
                "type": "string",
                "description": "Currency of the refund."
              },
              "refundTo": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Where a refund goes."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Where a refund goes."
              },
              "message": {
                "type": "string",
                "description": "A sentence about the result."
              }
            },
            "required": [
              "status",
              "deleted"
            ]
          },
          "meta": {
            "$ref": "#/components/schemas/FlightOrderMeta"
          },
          "postProcessing": {
            "$ref": "#/components/schemas/FlightOrderPostProcessing"
          }
        },
        "required": [
          "success",
          "booking",
          "cancellation"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "11487aa1",
            "orderId": "eJzTd9ePNA90dHUGAAsuAlA",
            "processingTime": 2.147,
            "booking": {
              "reference": "Y7QAEC",
              "status": "cancelled",
              "pnr": "Y7QAEC",
              "type": "hold",
              "createdAt": "2026-11-01T15:26:27.977426+00:00",
              "cancelledAt": "2026-11-01T15:26:30.748257",
              "content": "managed",
              "syncedAt": "2026-11-01T15:26:30.748257",
              "availableActions": [],
              "voidableUntil": null,
              "cancellation": {
                "type": "void",
                "deleted": true,
                "deletedAt": "2026-11-01T15:26:30.748257"
              }
            },
            "supplier": {
              "id": "amadeus_aqc_dz_1",
              "technology": "amadeus_aqc",
              "name": "Amadeus Algeria",
              "offerId": null
            },
            "agency": {
              "currency": "DZD"
            },
            "cancellation": {
              "status": "deleted",
              "deleted": true,
              "deletedAt": "2026-11-01T15:26:30.748257",
              "refundAmount": 0,
              "refundCurrency": "DZD",
              "refundTo": null,
              "message": "Reservation deleted successfully."
            },
            "meta": {
              "supportHold": true,
              "supportVoid": true,
              "voidDeadline": "2026-11-01T23:00:00Z",
              "voidWindowEndsAt": null,
              "airlineInitiatedChanges": [],
              "changes": [],
              "users": [],
              "orderMetadata": {
                "supplierStatusCode": 204,
                "supplierDeleteErrorIgnored": false,
                "supplierDeleteErrorCode": 204,
                "supplierDeleteErrorMessage": null
              }
            },
            "postProcessing": {
              "bookingStatusUpdated": true
            }
          }
        ]
      },
      "FlightRefund": {
        "type": "object",
        "description": "The refund of a cancellation.",
        "properties": {
          "originalRefundCurrency": {
            "type": "string",
            "description": "Currency of the airline's refund."
          },
          "originalRefundAmount": {
            "type": "number",
            "description": "The airline's refund, in its currency."
          },
          "refundCurrency": {
            "type": "string",
            "description": "Currency of your balance."
          },
          "refundAmount": {
            "type": "number",
            "description": "What is credited to your balance."
          },
          "refundTo": {
            "type": "string",
            "description": "Where the refund goes: `FX-Port Balance`."
          },
          "refundToRaw": {
            "type": "string",
            "description": "`balance`."
          },
          "convertedSupplierRefundAmount": {
            "type": "number",
            "description": "The airline's refund converted to your currency."
          },
          "isFullBalanceRefund": {
            "type": "boolean",
            "description": "Whether the whole payment is refunded."
          },
          "kioskCommissionRefund": {
            "type": "number",
            "description": "Commission taken back."
          }
        },
        "required": [
          "refundAmount",
          "refundCurrency"
        ],
        "additionalProperties": true
      },
      "FlightCancellation": {
        "type": "object",
        "description": "A cancellation of an issued Duffel order.",
        "properties": {
          "cancellationId": {
            "type": "string",
            "description": "The `ore_...` id of the cancellation. Send it to `confirm_cancel`.",
            "deprecated": true,
            "x-replaced-by": "cancellation_id"
          },
          "cancellation_id": {
            "type": "string",
            "description": "The `ore_...` id of the cancellation. Send it to `confirm_cancel`. The snake_case twin of `cancellationId`, with the same value: use this name."
          },
          "orderId": {
            "type": "string",
            "description": "The supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "status": {
            "type": "string",
            "description": "`pending` for a quote, `confirmed` once confirmed."
          },
          "refund": {
            "$ref": "#/components/schemas/FlightRefund"
          },
          "expiresAt": {
            "type": "string",
            "description": "A quote is valid until this moment (one hour)."
          },
          "confirmedAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "When it was confirmed."
              },
              {
                "type": "null"
              }
            ],
            "description": "When it was confirmed."
          },
          "createdAt": {
            "type": "string",
            "description": "When the quote was created."
          },
          "airlineCredits": {
            "anyOf": [
              {
                "description": "Credits the airline issues instead of a refund.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Credits the airline issues instead of a refund."
          },
          "liveMode": {
            "type": "boolean",
            "description": "`false` in the sandbox."
          },
          "message": {
            "type": "string",
            "description": "A sentence about the result (`confirm_cancel`)."
          }
        },
        "required": [
          "cancellationId",
          "status"
        ],
        "additionalProperties": true
      },
      "FlightFxRate": {
        "type": "object",
        "description": "The exchange rate used for the refund.",
        "properties": {
          "rate": {
            "type": "number",
            "description": "Units of the target currency for one unit of the source currency."
          },
          "marginPercent": {
            "type": "number",
            "description": "The margin included in the rate."
          },
          "fromCurrency": {
            "type": "string",
            "description": "Supplier currency."
          },
          "toCurrency": {
            "type": "string",
            "description": "Your currency."
          }
        },
        "required": [
          "rate",
          "fromCurrency",
          "toCurrency"
        ]
      },
      "FlightCancelQuoteResponse": {
        "type": "object",
        "description": "The refund of cancelling an issued Duffel order, as a pending cancellation. Nothing is cancelled until `confirm_cancel`. When the supplier refuses the quote (many fares answer `order_not_cancellable`) the answer is HTTP `200` with `success: false` and `error`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the quote was created."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "orderId": {
            "type": "string",
            "description": "The supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "cancellation": {
            "$ref": "#/components/schemas/FlightCancellation"
          },
          "supplier": {
            "$ref": "#/components/schemas/FlightOrderSupplier"
          },
          "agency": {
            "$ref": "#/components/schemas/FlightOrderAgency"
          },
          "exchangeRate": {
            "$ref": "#/components/schemas/FlightFxRate",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "$ref": "#/components/schemas/FlightFxRate",
            "description": ". The snake_case twin of `exchangeRate`, with the same value: use this name."
          },
          "meta": {
            "description": "`quoteValidUntil`, `isConfirmed`, `canConfirm`.",
            "additionalProperties": true,
            "type": "object"
          },
          "error": {
            "$ref": "#/components/schemas/FlightSupplierRefusal",
            "description": "Only when `success` is `false`: the refusal of the supplier for `cancel_quote`."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "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`."
          }
        },
        "required": [
          "success"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "2cb63f69",
            "orderId": "ord_0000BC0VDONwA54LrfoCkm",
            "processingTime": 0.128,
            "cancellation": {
              "cancellationId": "ore_0000BC0VG9WBaFAZ83u3yy",
              "orderId": "ord_0000BC0VDONwA54LrfoCkm",
              "status": "pending",
              "refund": {
                "originalRefundCurrency": "USD",
                "originalRefundAmount": 76.86,
                "refundCurrency": "DZD",
                "refundAmount": 19215,
                "refundTo": "FX-Port Balance",
                "refundToRaw": "balance",
                "convertedSupplierRefundAmount": 19215,
                "isFullBalanceRefund": false,
                "kioskCommissionRefund": 0
              },
              "expiresAt": "2026-11-01T15:28:32Z",
              "confirmedAt": null,
              "createdAt": "2026-11-01T14:28:32.488592Z",
              "airlineCredits": null,
              "liveMode": false
            },
            "supplier": {
              "id": "duffel_1",
              "technology": "duffel",
              "name": "Duffel"
            },
            "agency": {
              "currency": "DZD"
            },
            "exchangeRate": {
              "rate": 250,
              "marginPercent": 0,
              "fromCurrency": "USD",
              "toCurrency": "DZD"
            },
            "meta": {
              "quoteValidUntil": "2026-11-01T15:28:32Z",
              "isConfirmed": false,
              "canConfirm": true
            }
          }
        ]
      },
      "FlightConfirmCancelResponse": {
        "type": "object",
        "description": "The order is cancelled at the airline and the refund is credited to your balance. This cannot be undone. When the supplier refuses (the order was already cancelled, or the cancellation is unknown) the answer is HTTP `200` with `success: false` and `error`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the cancellation was confirmed."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "orderId": {
            "type": "string",
            "description": "The supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "cancellationId": {
            "type": "string",
            "description": "Only when `success` is `false`: the cancellation you tried to confirm."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "confirmation": {
            "$ref": "#/components/schemas/FlightCancellation"
          },
          "supplier": {
            "$ref": "#/components/schemas/FlightOrderSupplier"
          },
          "agency": {
            "$ref": "#/components/schemas/FlightOrderAgency"
          },
          "exchangeRate": {
            "$ref": "#/components/schemas/FlightFxRate",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "$ref": "#/components/schemas/FlightFxRate",
            "description": ". The snake_case twin of `exchangeRate`, with the same value: use this name."
          },
          "meta": {
            "description": "`cancellationConfirmedAt` and `refundProcessing`.",
            "additionalProperties": true,
            "type": "object"
          },
          "postProcessing": {
            "$ref": "#/components/schemas/FlightOrderPostProcessing"
          },
          "error": {
            "$ref": "#/components/schemas/FlightSupplierRefusal",
            "description": "Only when `success` is `false`: the refusal of the supplier for `confirm_cancel`."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "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`."
          }
        },
        "required": [
          "success"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "64666d3c",
            "orderId": "ord_0000BC0VDONwA54LrfoCkm",
            "processingTime": 0.245,
            "confirmation": {
              "cancellationId": "ore_0000BC0VG9WBaFAZ83u3yy",
              "orderId": "ord_0000BC0VDONwA54LrfoCkm",
              "status": "confirmed",
              "refund": {
                "originalRefundCurrency": "USD",
                "originalRefundAmount": 76.86,
                "refundCurrency": "DZD",
                "refundAmount": 19215,
                "refundTo": "FX-Port Balance",
                "refundToRaw": "balance",
                "convertedSupplierRefundAmount": 19215,
                "isFullBalanceRefund": false,
                "kioskCommissionRefund": 0
              },
              "confirmedAt": "2026-11-01T14:30:04.851325Z",
              "createdAt": "2026-11-01T14:28:32.488592Z",
              "airlineCredits": null,
              "liveMode": false,
              "message": "Order cancellation confirmed. Refund will be processed."
            },
            "supplier": {
              "id": "duffel_1",
              "technology": "duffel",
              "name": "Duffel"
            },
            "agency": {
              "currency": "DZD"
            },
            "exchangeRate": {
              "rate": 250,
              "marginPercent": 0,
              "fromCurrency": "USD",
              "toCurrency": "DZD"
            },
            "meta": {
              "cancellationConfirmedAt": "2026-11-01T14:30:04.851325Z",
              "refundProcessing": {
                "method": "FX-Port Balance",
                "estimatedDays": "Immediate",
                "description": "Refund credited to your FX-Port balance"
              }
            },
            "postProcessing": {
              "refundMethod": "balance",
              "emailSent": false
            }
          }
        ]
      },
      "FlightSupplierRefusal": {
        "type": "object",
        "description": "A refusal of the supplier on a post-booking call. It comes with HTTP `200` and `success: false`: read `success` before you trust the answer.",
        "properties": {
          "message": {
            "type": "string",
            "description": "The supplier's message."
          },
          "code": {
            "type": "string",
            "description": "The supplier's code: a number as text for Amadeus (`4926`), a name for Duffel (`order_not_cancellable`)."
          },
          "type": {
            "type": "string",
            "description": "The supplier's error type."
          },
          "details": {
            "type": "array",
            "items": {
              "description": "A detail of the supplier.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "The supplier's details, in its own format."
          },
          "userMessage": {
            "type": "string",
            "description": "A sentence the supplier wrote for the traveler, when there is one."
          }
        },
        "required": [
          "message",
          "code"
        ],
        "additionalProperties": true
      },
      "FlightBooking": {
        "type": "object",
        "description": "A flight booking of your agency, as FX-Port stores it. The booking is a database row: more keys than the ones listed here exist (the void history, the details of a changed schedule ...); they are internal and not part of the contract. Where the ACD passports sit in FX-Port's storage is never returned: only their public links.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The FX-Port UUID of the booking. **Use it as the key** when you store or re-read a booking."
          },
          "booking_id": {
            "type": "string",
            "description": "The PNR. Not unique; prefer `id`.",
            "deprecated": true,
            "x-replaced-by": "pnr"
          },
          "pnr": {
            "type": "string",
            "description": "The PNR. Not unique; prefer `id`. The snake_case twin of `booking_id`, with the same value: use this name."
          },
          "amadeus_id": {
            "type": "string",
            "description": "The supplier's order id (the name is historical: it also holds the `ord_...` of a Duffel order). This is the `order_id` of `pay_order`, `cancel_order` and `cancel_quote`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id (the name is historical: it also holds the `ord_...` of a Duffel order). This is the `order_id` of `pay_order`, `cancel_order` and `cancel_quote`. The snake_case twin of `amadeus_id`, with the same value: use this name."
          },
          "status": {
            "type": "string",
            "description": "`confirmed`, `awaiting_payment` (a hold), `cancelled`, `partial_refund`, `failed`; `voided`, `refunded` and `expired` can appear for changed orders."
          },
          "created_at": {
            "type": "string",
            "description": "When the booking was created."
          },
          "paid_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "When it was paid."
              },
              {
                "type": "null"
              }
            ],
            "description": "When it was paid."
          },
          "updated_at": {
            "type": "string",
            "description": "When it last changed."
          },
          "total_price": {
            "type": "number",
            "description": "The final price in the currency of the agency."
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the agency."
          },
          "departure_airport": {
            "type": "string",
            "description": "IATA code of the first departure."
          },
          "arrival_airport": {
            "type": "string",
            "description": "IATA code of the last arrival."
          },
          "departure_date": {
            "type": "string",
            "description": "Local departure time, without a zone."
          },
          "arrival_date": {
            "type": "string",
            "description": "Local arrival time, without a zone."
          },
          "return_departure_date": {
            "anyOf": [
              {
                "type": "string",
                "description": "Local departure time of the return."
              },
              {
                "type": "null"
              }
            ],
            "description": "Local departure time of the return."
          },
          "return_arrival_date": {
            "anyOf": [
              {
                "type": "string",
                "description": "Local arrival time of the return."
              },
              {
                "type": "null"
              }
            ],
            "description": "Local arrival time of the return."
          },
          "included_checked_bags": {
            "type": "string",
            "description": "The checked bags included, as a text."
          },
          "ticketing_agreement": {
            "anyOf": [
              {
                "type": "string",
                "description": "The ticketing-time-limit agreement of an Amadeus booking, for example `DELAY_TO_CANCEL`."
              },
              {
                "type": "null"
              }
            ],
            "description": "The ticketing-time-limit agreement of an Amadeus booking, for example `DELAY_TO_CANCEL`."
          },
          "supplier_name": {
            "type": "string",
            "description": "Name of the supplier."
          },
          "supplier_id": {
            "type": "string",
            "description": "Id of the supplier."
          },
          "ticket_number": {
            "anyOf": [
              {
                "type": "string",
                "description": "The e-ticket number(s), as one text; several numbers are joined by commas. `null` until the airline returns one."
              },
              {
                "type": "null"
              }
            ],
            "description": "The e-ticket number(s), as one text; several numbers are joined by commas. `null` until the airline returns one."
          },
          "bucket_link": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the PDF of the ticket (a hold: the hold voucher). `null` until generated. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the PDF of the ticket (a hold: the hold voucher). `null` until generated. A public link.",
            "deprecated": true,
            "x-replaced-by": "pdf_url"
          },
          "pdf_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the PDF of the ticket (a hold: the hold voucher). `null` until generated. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the PDF of the ticket (a hold: the hold voucher). `null` until generated. A public link. The snake_case twin of `bucket_link`, with the same value: use this name."
          },
          "bucket_link_no_price": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the same PDF without prices. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the same PDF without prices. A public link.",
            "deprecated": true,
            "x-replaced-by": "pdf_url_no_price"
          },
          "pdf_url_no_price": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the same PDF without prices. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the same PDF without prices. A public link. The snake_case twin of `bucket_link_no_price`, with the same value: use this name."
          },
          "email_sent": {
            "type": "boolean",
            "description": "Whether the confirmation e-mail went out."
          },
          "require_acd": {
            "type": "boolean",
            "description": "`true` for an ACD booking (the stored flag)."
          },
          "acd_required": {
            "type": "boolean",
            "description": "`true` for an ACD booking whose passports FX-Port did not waive: the same value as `acd.acd_required` of `get_acd_documents`."
          },
          "requires_acd": {
            "type": "boolean",
            "description": "The same value as `acd_required`."
          },
          "acd_submitted": {
            "type": "boolean",
            "description": "`true` once every traveler has a passport. Never `null`."
          },
          "acd_documents": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/FlightBookingAcdDocument"
                },
                "description": "The passports uploaded through the API, one entry for each traveler who has one. `null` for a booking that never had one. `get_acd_documents` gives the same information for each traveler, with the files FX-Port staff added from the dashboard."
              },
              {
                "type": "null"
              }
            ],
            "description": "The passports uploaded through the API, one entry for each traveler who has one. `null` for a booking that never had one. `get_acd_documents` gives the same information for each traveler, with the files FX-Port staff added from the dashboard."
          },
          "acd_links": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "The public address of a passport file."
                },
                "description": "The public addresses of every passport file of the booking, including the ones FX-Port staff added. `null` when there is none."
              },
              {
                "type": "null"
              }
            ],
            "description": "The public addresses of every passport file of the booking, including the ones FX-Port staff added. `null` when there is none."
          },
          "acd_amount": {
            "anyOf": [
              {
                "type": "number",
                "description": "The ACD amount of the booking in euros, saved when the set of passports became complete. Informational. `null` before."
              },
              {
                "type": "null"
              }
            ],
            "description": "The ACD amount of the booking in euros, saved when the set of passports became complete. Informational. `null` before."
          },
          "acd_paid": {
            "type": "boolean",
            "description": "`true` once FX-Port's team has marked the ACD amount as paid. You cannot set it."
          },
          "acd_documents_revision": {
            "type": "integer",
            "description": "Increases by one with each upload that stored a file. `0` for a booking without any."
          },
          "has_changed": {
            "type": "boolean",
            "description": "`true` when the airline changed the schedule after ticketing."
          },
          "new_flight_details": {
            "anyOf": [
              {
                "description": "The new schedule, when `has_changed`.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "The new schedule, when `has_changed`."
          },
          "voided_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "When the ticket was voided."
              },
              {
                "type": "null"
              }
            ],
            "description": "When the ticket was voided."
          },
          "refund_amount": {
            "anyOf": [
              {
                "type": "number",
                "description": "The refund of a cancelled Duffel order."
              },
              {
                "type": "null"
              }
            ],
            "description": "The refund of a cancelled Duffel order."
          },
          "refund_currency": {
            "anyOf": [
              {
                "type": "string",
                "description": "Currency of the refund."
              },
              {
                "type": "null"
              }
            ],
            "description": "Currency of the refund."
          },
          "refund_method": {
            "anyOf": [
              {
                "type": "string",
                "description": "How the refund was paid: `balance`."
              },
              {
                "type": "null"
              }
            ],
            "description": "How the refund was paid: `balance`."
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "Your own agent id, when you sent one."
              },
              {
                "type": "null"
              }
            ],
            "description": "Your own agent id, when you sent one."
          },
          "environment": {
            "type": "string",
            "description": "`sandbox` or `live`."
          },
          "pricingBreakdown": {
            "anyOf": [
              {
                "type": "object",
                "description": "The price of the booking in snake_case. The rate and the ACD details are not here.",
                "properties": {
                  "supplier_currency": {
                    "type": "string",
                    "description": "Currency of the supplier."
                  },
                  "agency_currency": {
                    "type": "string",
                    "description": "Currency of the agency."
                  },
                  "base_price": {
                    "type": "number",
                    "description": "Base fare."
                  },
                  "taxes_and_fees": {
                    "type": "number",
                    "description": "Taxes and fees."
                  },
                  "agency_commission": {
                    "type": "number",
                    "description": "Your commission."
                  },
                  "final_price": {
                    "type": "number",
                    "description": "What the traveler pays."
                  },
                  "b2b_price": {
                    "type": "number",
                    "description": "What was debited from your balance."
                  }
                },
                "required": [
                  "final_price",
                  "b2b_price"
                ],
                "additionalProperties": true
              },
              {
                "type": "null"
              }
            ],
            "description": "The price of the booking in snake_case. The rate and the ACD details are not here."
          },
          "travelerPricing": {
            "type": "array",
            "items": {
              "description": "The price of one traveler in snake_case: `traveler_id`, `traveler_type`, `fare_option`, `base`, `total`, `b2b_price`.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "The price of each traveler."
          },
          "balanceOperations": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One entry of the balance ledger of this booking.",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Id of the entry."
                },
                "flight_booking_id": {
                  "type": "string",
                  "description": "The `id` of the booking."
                },
                "operation": {
                  "type": "string",
                  "description": "`debit`, `refund` ..."
                },
                "amount": {
                  "type": "number",
                  "description": "The amount."
                },
                "currency": {
                  "type": "string",
                  "description": "Currency of the amount."
                },
                "description": {
                  "type": "string",
                  "description": "What the entry is for."
                },
                "created_at": {
                  "type": "string",
                  "description": "When it was written."
                },
                "b2b_price": {
                  "type": "number",
                  "description": "The booking's `b2b_price` at that time."
                },
                "final_price": {
                  "type": "number",
                  "description": "The booking's `final_price` at that time."
                }
              },
              "required": [
                "id",
                "operation",
                "amount"
              ]
            },
            "description": "The ledger entries of this booking (debits, refunds). Empty in the sandbox."
          },
          "apiResponse": {
            "description": "Only with `include_api_response: true`: the stored answer of the booking call, with the full passenger list.",
            "additionalProperties": true,
            "type": "object"
          }
        },
        "required": [
          "id",
          "booking_id",
          "status"
        ],
        "additionalProperties": true,
        "examples": [
          {
            "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
            "booking_id": "Y7PAEU",
            "amadeus_id": "eJzTd9ePNA9wdA0FAAs8AmE",
            "status": "confirmed",
            "created_at": "2026-11-01T15:26:14.871035+00:00",
            "paid_at": "2026-11-01T15:26:21.961664+00:00",
            "updated_at": "2026-11-01T15:26:21.998944+00:00",
            "total_price": 4930.37,
            "agency_commission": 4.93,
            "currency": "DZD",
            "departure_airport": "ALG",
            "arrival_airport": "ORN",
            "departure_date": "2026-12-22T19:45:00",
            "arrival_date": "2026-12-22T20:45:00",
            "return_departure_date": null,
            "return_arrival_date": null,
            "included_checked_bags": "0",
            "ticketing_agreement": "DELAY_TO_CANCEL",
            "supplier_name": "Amadeus Algeria",
            "supplier_id": "amadeus_aqc_dz_1",
            "ticket_number": "124-2446977397",
            "bucket_link": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
            "bucket_link_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
            "email_sent": false,
            "require_acd": false,
            "acd_submitted": false,
            "acd_links": null,
            "acd_amount": null,
            "acd_paid": false,
            "acd_documents_revision": 0,
            "acd_documents": null,
            "has_changed": false,
            "new_flight_details": null,
            "voided_at": null,
            "refund_amount": null,
            "refund_currency": null,
            "refund_method": null,
            "agent_id": null,
            "environment": "sandbox",
            "pricingBreakdown": {
              "supplier_currency": "DZD",
              "agency_currency": "DZD",
              "base_price": 3160,
              "taxes_and_fees": 1765.44,
              "agency_commission": 4.93,
              "final_price": 4930.37,
              "b2b_price": 4925.44,
              "created_at": "2026-11-01T15:26:22.176516+00:00",
              "environment": "sandbox"
            },
            "travelerPricing": [
              {
                "traveler_id": "1",
                "traveler_type": "ADULT",
                "fare_option": "STANDARD",
                "supplier_currency": "DZD",
                "agency_currency": "DZD",
                "base": 3160,
                "total": 4930.37,
                "b2b_price": 4925.44,
                "created_at": "2026-11-01T15:26:15.133066+00:00",
                "environment": "sandbox"
              }
            ],
            "balanceOperations": [],
            "order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
            "pnr": "Y7PAEU",
            "pdf_url": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
            "pdf_url_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
            "acd_required": false,
            "requires_acd": false
          },
          {
            "id": "2acf8907-f5c5-4d5d-93b5-3e4a038ccdf1",
            "booking_id": "Y7HWMD",
            "amadeus_id": "eJzTd9ePNPcI93UBAAtdAmY",
            "status": "confirmed",
            "created_at": "2026-11-01T14:19:02.015222+00:00",
            "paid_at": "2026-11-01T14:19:01.979761+00:00",
            "updated_at": "2026-11-01T14:24:33.08805+00:00",
            "total_price": 78780.71,
            "agency_commission": 78.71,
            "currency": "DZD",
            "departure_airport": "CDG",
            "arrival_airport": "ALG",
            "departure_date": "2026-12-16T06:10:00",
            "arrival_date": "2026-12-16T16:55:00",
            "return_departure_date": null,
            "return_arrival_date": null,
            "included_checked_bags": "0",
            "ticketing_agreement": "CONFIRM",
            "supplier_name": "Amadeus Algeria",
            "supplier_id": "amadeus_aqc_dz_1",
            "ticket_number": null,
            "bucket_link": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE.pdf",
            "bucket_link_no_price": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE-no-price.pdf",
            "email_sent": false,
            "require_acd": true,
            "acd_submitted": true,
            "acd_links": [
              "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142432_087a6dc8.png"
            ],
            "acd_amount": 235,
            "acd_paid": false,
            "acd_documents_revision": 2,
            "acd_documents": [
              {
                "traveler_id": "1",
                "traveler_name": "JANE DOE",
                "traveler_type": "ADULT",
                "link": "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142432_087a6dc8.png",
                "filename": "rescan.png",
                "content_type": "image/png",
                "size": 69,
                "format": "png",
                "uploaded_at": "2026-11-01T14:24:33.057044+00:00",
                "replaced_count": 1,
                "previous_links": [
                  {
                    "link": "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142430_9b354245.pdf",
                    "uploaded_at": "2026-11-01T14:24:31.256905+00:00",
                    "replaced_at": "2026-11-01T14:24:33.057044+00:00"
                  }
                ],
                "source": "api"
              }
            ],
            "has_changed": false,
            "new_flight_details": null,
            "voided_at": null,
            "refund_amount": null,
            "refund_currency": null,
            "refund_method": null,
            "agent_id": null,
            "environment": "sandbox",
            "pricingBreakdown": {
              "supplier_currency": "DZD",
              "agency_currency": "DZD",
              "base_price": 16390,
              "taxes_and_fees": 62312,
              "agency_commission": 78.71,
              "final_price": 78780.71,
              "b2b_price": 78702,
              "created_at": "2026-11-01T14:19:02.205874+00:00",
              "environment": "sandbox"
            },
            "travelerPricing": [
              {
                "traveler_id": "1",
                "traveler_type": "ADULT",
                "fare_option": "STANDARD",
                "supplier_currency": "DZD",
                "agency_currency": "DZD",
                "base": 16390,
                "total": 78780.72,
                "b2b_price": 78702.01,
                "created_at": "2026-11-01T14:19:02.115601+00:00",
                "environment": "sandbox"
              }
            ],
            "balanceOperations": [],
            "order_id": "eJzTd9ePNPcI93UBAAtdAmY",
            "pnr": "Y7HWMD",
            "pdf_url": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE.pdf",
            "pdf_url_no_price": "https://s3.example.com/flight-bookings/Y7HWMD-JANE-DOE-no-price.pdf",
            "acd_required": true,
            "requires_acd": true
          }
        ]
      },
      "FlightBookingAcdDocument": {
        "type": "object",
        "description": "The passport stored for one traveler of an ACD booking. FX-Port's own storage keys are never returned.",
        "properties": {
          "traveler_id": {
            "type": "string",
            "description": "The traveler the passport belongs to: the `traveler_id` of `get_acd_documents`."
          },
          "traveler_name": {
            "type": "string",
            "description": "The name on the booking."
          },
          "traveler_type": {
            "type": "string",
            "description": "The passenger type, for example `ADULT`."
          },
          "link": {
            "type": "string",
            "description": "The **public** address of the file: anybody who has it can open the passport. Treat it as sensitive."
          },
          "filename": {
            "type": "string",
            "description": "The file name that was sent."
          },
          "content_type": {
            "type": "string",
            "description": "The type FX-Port detected from the content, for example `application/pdf` or `image/png`."
          },
          "size": {
            "type": "integer",
            "description": "The size of the stored file in bytes (`size_bytes` in `get_acd_documents`)."
          },
          "format": {
            "type": "string",
            "description": "The detected format: `pdf`, `jpeg`, `png`, `webp`, `heif`, `avif`, `tiff`, `bmp`, `gif`, `jp2` or `jxl`."
          },
          "uploaded_at": {
            "type": "string",
            "description": "When the file was stored: ISO 8601, UTC."
          },
          "replaced_count": {
            "type": "integer",
            "description": "How many times the passport was replaced."
          },
          "previous_links": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "A passport file that a newer upload replaced.",
              "properties": {
                "link": {
                  "type": "string",
                  "description": "The public address of a file that was replaced."
                },
                "uploaded_at": {
                  "type": "string",
                  "description": "When that file was stored."
                },
                "replaced_at": {
                  "type": "string",
                  "description": "When it was replaced."
                }
              },
              "required": [
                "link"
              ]
            },
            "description": "The files this passport replaced, oldest first. Empty for a passport that was never replaced."
          },
          "source": {
            "type": "string",
            "description": "How the file arrived: `api` for an upload through the API."
          }
        },
        "required": [
          "traveler_id",
          "link"
        ],
        "additionalProperties": true
      },
      "FlightBookingResponse": {
        "type": "object",
        "description": "A booking of your agency.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the booking was found."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds spent.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds spent. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "booking": {
            "$ref": "#/components/schemas/FlightBooking"
          }
        },
        "required": [
          "success",
          "booking"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "0c2b315c",
            "processingTime": 0.272,
            "booking": {
              "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
              "booking_id": "Y7PAEU",
              "amadeus_id": "eJzTd9ePNA9wdA0FAAs8AmE",
              "status": "confirmed",
              "created_at": "2026-11-01T15:26:14.871035+00:00",
              "paid_at": "2026-11-01T15:26:21.961664+00:00",
              "updated_at": "2026-11-01T15:26:21.998944+00:00",
              "total_price": 4930.37,
              "agency_commission": 4.93,
              "currency": "DZD",
              "departure_airport": "ALG",
              "arrival_airport": "ORN",
              "departure_date": "2026-12-22T19:45:00",
              "arrival_date": "2026-12-22T20:45:00",
              "return_departure_date": null,
              "return_arrival_date": null,
              "included_checked_bags": "0",
              "ticketing_agreement": "DELAY_TO_CANCEL",
              "supplier_name": "Amadeus Algeria",
              "supplier_id": "amadeus_aqc_dz_1",
              "ticket_number": "124-2446977397",
              "bucket_link": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
              "bucket_link_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
              "email_sent": false,
              "require_acd": false,
              "acd_submitted": false,
              "acd_links": null,
              "acd_amount": null,
              "acd_paid": false,
              "acd_documents_revision": 0,
              "acd_documents": null,
              "has_changed": false,
              "new_flight_details": null,
              "voided_at": null,
              "refund_amount": null,
              "refund_currency": null,
              "refund_method": null,
              "agent_id": null,
              "environment": "sandbox",
              "pricingBreakdown": {
                "supplier_currency": "DZD",
                "agency_currency": "DZD",
                "base_price": 3160,
                "taxes_and_fees": 1765.44,
                "agency_commission": 4.93,
                "final_price": 4930.37,
                "b2b_price": 4925.44,
                "created_at": "2026-11-01T15:26:22.176516+00:00",
                "environment": "sandbox"
              },
              "travelerPricing": [
                {
                  "traveler_id": "1",
                  "traveler_type": "ADULT",
                  "fare_option": "STANDARD",
                  "supplier_currency": "DZD",
                  "agency_currency": "DZD",
                  "base": 3160,
                  "total": 4930.37,
                  "b2b_price": 4925.44,
                  "created_at": "2026-11-01T15:26:15.133066+00:00",
                  "environment": "sandbox"
                }
              ],
              "balanceOperations": [],
              "order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
              "pnr": "Y7PAEU",
              "pdf_url": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
              "pdf_url_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
              "acd_required": false,
              "requires_acd": false
            }
          }
        ]
      },
      "FlightTicketResponse": {
        "type": "object",
        "description": "The PDF of the ticket (or the hold voucher) of a booking.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the booking was found."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "ticket_ready": {
            "type": "boolean",
            "description": "`true` as soon as the booking has a PDF link, whatever its status. It does not mean that an e-ticket exists: ask `get_ticket_number`."
          },
          "booking_id": {
            "type": "string",
            "description": "Echoes the value you sent: the PNR, the order id or the UUID."
          },
          "message": {
            "type": "string",
            "description": "Only when `ticket_ready` is `false`."
          },
          "download_url": {
            "type": "string",
            "description": "The PDF with the fare receipt. A public link.",
            "deprecated": true,
            "x-replaced-by": "pdf_url"
          },
          "pdf_url": {
            "type": "string",
            "description": "The PDF with the fare receipt. A public link. The snake_case twin of `download_url`, with the same value: use this name."
          },
          "download_url_with_price": {
            "type": "string",
            "description": "The same address as `download_url`."
          },
          "download_url_no_price": {
            "anyOf": [
              {
                "type": "string",
                "description": "A second PDF of the same ticket without prices. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "A second PDF of the same ticket without prices. A public link.",
            "deprecated": true,
            "x-replaced-by": "pdf_url_no_price"
          },
          "pdf_url_no_price": {
            "anyOf": [
              {
                "type": "string",
                "description": "A second PDF of the same ticket without prices. A public link."
              },
              {
                "type": "null"
              }
            ],
            "description": "A second PDF of the same ticket without prices. A public link. The snake_case twin of `download_url_no_price`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "ticket_ready",
          "booking_id"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "71fdaeb7",
            "ticket_ready": true,
            "booking_id": "Y7PAEU",
            "download_url": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
            "download_url_with_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
            "download_url_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf"
          }
        ]
      },
      "FlightTicketNumberResponse": {
        "type": "object",
        "description": "The e-ticket number of a booking, asked of the airline when it is not stored yet.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the booking was found."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "ticket_available": {
            "type": "boolean",
            "description": "`true` when the airline has returned a ticket number."
          },
          "ticket_number": {
            "anyOf": [
              {
                "type": "string",
                "description": "The e-ticket number, for example `124-2446977397` (Amadeus); in the Duffel sandbox the text `1`. Several tickets are joined in one text. `null` until available."
              },
              {
                "type": "null"
              }
            ],
            "description": "The e-ticket number, for example `124-2446977397` (Amadeus); in the Duffel sandbox the text `1`. Several tickets are joined in one text. `null` until available."
          },
          "booking_id": {
            "type": "string",
            "description": "The PNR.",
            "deprecated": true,
            "x-replaced-by": "pnr"
          },
          "pnr": {
            "type": "string",
            "description": "The PNR. The snake_case twin of `booking_id`, with the same value: use this name."
          },
          "supplier_order_id": {
            "type": "string",
            "description": "The supplier's order id.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order id. The snake_case twin of `supplier_order_id`, with the same value: use this name."
          },
          "message": {
            "type": "string",
            "description": "Only when `ticket_available` is `false`."
          },
          "booking_response": {
            "description": "The stored answer of the booking, in the shape of the `book_flight` response, without the supplier's costs and commissions.",
            "additionalProperties": true,
            "type": "object"
          }
        },
        "required": [
          "success",
          "ticket_available",
          "booking_id"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "a8e2164f",
            "ticket_available": true,
            "ticket_number": "124-2446977397",
            "booking_id": "Y7PAEU",
            "supplier_order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
            "booking_response": {
              "booking": {
                "pnr": "Y7PAEU",
                "type": "instant",
                "status": "confirmed",
                "content": "managed",
                "syncedAt": "2026-11-01T15:26:21.896668",
                "createdAt": "2026-11-01T15:26:21.896668",
                "reference": "eJzTd9ePNA9wdA0FAAs8AmE",
                "cancelledAt": null,
                "cancellation": null,
                "voidableUntil": "2026-11-01T23:00:00Z",
                "availableActions": []
              },
              "supplier": {
                "id": "amadeus_aqc_dz_1",
                "name": "Amadeus AQC",
                "offerId": "1",
                "technology": "amadeus_aqc"
              },
              "pricing": {
                "b2bPrice": 4736,
                "basePrice": 3160,
                "finalPrice": 4930.37,
                "taxesAndFees": 1770.37,
                "agencyCurrency": "DZD",
                "agencyCommission": 4.93,
                "supplierCurrency": "DZD",
                "additionalServices": []
              },
              "payment": {
                "paidAt": "2026-11-01T15:26:21.896710",
                "status": "paid",
                "awaitingPayment": false,
                "paymentRequiredBy": null,
                "priceGuaranteeExpiresAt": null
              },
              "documents": [
                {
                  "type": "electronic_ticket",
                  "status": "ISSUED",
                  "segmentIds": [
                    "1"
                  ],
                  "documentType": "ETICKET",
                  "passengerIds": [
                    "1"
                  ],
                  "uniqueIdentifier": "124-2446977397"
                }
              ],
              "bookingReferences": [
                {
                  "pnr": "Y7PAEU",
                  "carrier": {
                    "name": "Air Algerie",
                    "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                    "iataCode": "AH",
                    "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg"
                  }
                }
              ],
              "passengers": [
                {
                  "id": "1",
                  "type": "ADULT",
                  "email": "traveler@example.com",
                  "title": "mr",
                  "bornOn": "1988-06-15",
                  "gender": "male",
                  "givenName": "JAMES",
                  "familyName": "SMITH",
                  "phoneNumber": "15555550123",
                  "ticketNumber": "124-2446977397",
                  "infantPassengerId": null,
                  "loyaltyProgrammeAccounts": []
                }
              ],
              "owner": {
                "id": null,
                "name": "Air Algerie",
                "logoUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-logo/AH.svg",
                "iataCode": "AH",
                "logoLockupUrl": "https://assets.duffel.com/img/airlines/for-light-background/full-color-lockup/AH.svg",
                "conditionsOfCarriageUrl": null
              }
            }
          }
        ]
      },
      "AcdDocument": {
        "type": "object",
        "description": "The current passport file of a traveler.",
        "properties": {
          "filename": {
            "anyOf": [
              {
                "type": "string",
                "description": "The file name you sent."
              },
              {
                "type": "null"
              }
            ],
            "description": "The file name you sent."
          },
          "content_type": {
            "type": "string",
            "description": "The type FX-Port detected from the content (for example `application/pdf`, `image/jpeg`), not the one you sent."
          },
          "size_bytes": {
            "type": "integer",
            "description": "The size of the stored file."
          },
          "download_url": {
            "type": "string",
            "description": "A **public** address of the file: anyone who has it can download the passport. Treat it as sensitive."
          },
          "uploaded_at": {
            "type": "string",
            "description": "ISO 8601 UTC time of the upload, with `+00:00`."
          },
          "replaced_count": {
            "type": "integer",
            "description": "How many times the passport was replaced."
          }
        },
        "required": [
          "content_type",
          "size_bytes",
          "download_url",
          "uploaded_at",
          "replaced_count"
        ]
      },
      "AcdTraveler": {
        "type": "object",
        "description": "A traveler of the booking and the state of their passport. Travelers are listed even when ACD is not required.",
        "properties": {
          "traveler_id": {
            "type": "string",
            "description": "The id to send in `traveler_ids`: the id of the passenger on the booking (`\"1\"`, `\"2\"` for Amadeus Algeria, `pas_...` for Duffel)."
          },
          "traveler_name": {
            "type": "string",
            "description": "The name on the booking."
          },
          "traveler_type": {
            "type": "string",
            "description": "The passenger type, for example `ADULT`."
          },
          "ticket_number": {
            "anyOf": [
              {
                "type": "string",
                "description": "The e-ticket number. `null` until the airline returns one: always for a hold, and for an instant booking whose number has not arrived."
              },
              {
                "type": "null"
              }
            ],
            "description": "The e-ticket number. `null` until the airline returns one: always for a hold, and for an instant booking whose number has not arrived."
          },
          "status": {
            "type": "string",
            "enum": [
              "uploaded",
              "missing"
            ],
            "description": "Whether this traveler has a passport."
          },
          "document": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AcdDocument"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "traveler_id",
          "traveler_name",
          "traveler_type",
          "ticket_number",
          "status",
          "document"
        ]
      },
      "AcdState": {
        "type": "object",
        "description": "The ACD state of a booking: what is needed, what was uploaded, and whether you can upload.",
        "properties": {
          "booking_id": {
            "type": "string",
            "description": "The PNR of the booking.",
            "deprecated": true,
            "x-replaced-by": "pnr"
          },
          "pnr": {
            "type": "string",
            "description": "The PNR of the booking. The snake_case twin of `booking_id`, with the same value: use this name."
          },
          "acd_required": {
            "type": "boolean",
            "description": "`true` when the booking needs ACD passports and FX-Port did not waive them."
          },
          "acd_waived": {
            "type": "boolean",
            "description": "`true` when FX-Port marked ACD as not required for this booking. No upload is accepted then. You cannot set it."
          },
          "acd_submitted": {
            "type": "boolean",
            "description": "`true` once every traveler has a passport. Never `null`. Also `true` for a hold that has a complete set."
          },
          "acd_paid": {
            "type": "boolean",
            "description": "`true` once FX-Port's team has marked the ACD amount of the booking as paid. You cannot set it."
          },
          "complete": {
            "type": "boolean",
            "description": "Every required passport is uploaded. Always `false` when ACD is not required."
          },
          "can_upload": {
            "type": "boolean",
            "description": "Whether `upload_acd_documents` accepts files for this booking right now."
          },
          "blocked_reason": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "ACD_NOT_REQUIRED",
                  "ACD_WAIVED",
                  "BOOKING_NOT_ELIGIBLE",
                  "TRAVELERS_NOT_AVAILABLE"
                ],
                "description": "Why `can_upload` is `false`. `null` otherwise."
              },
              {
                "type": "null"
              }
            ],
            "description": "Why `can_upload` is `false`. `null` otherwise."
          },
          "required_documents": {
            "type": "integer",
            "description": "The number of passports needed: one for each traveler of the booking. `0` when ACD is not required."
          },
          "uploaded_documents": {
            "type": "integer",
            "description": "How many of the required passports are uploaded."
          },
          "missing_documents": {
            "type": "integer",
            "description": "How many are still missing."
          },
          "missing_traveler_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A traveler id."
            },
            "description": "The travelers who still need a passport. Use these values in `traveler_ids`."
          },
          "travelers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcdTraveler"
            },
            "description": "Every traveler of the booking."
          },
          "unassigned_documents": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "A file FX-Port holds for the booking but could not match to a traveler.",
              "properties": {
                "download_url": {
                  "type": "string",
                  "description": "A public address of the file."
                }
              },
              "required": [
                "download_url"
              ]
            },
            "description": "Files that do not count as a traveler's passport."
          },
          "revision": {
            "type": "integer",
            "description": "Increases by one with each upload that stored a file. `0` for a booking without any."
          },
          "limits": {
            "type": "object",
            "description": "The upload limits. Read them instead of hardcoding them.",
            "properties": {
              "accepted_formats": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "avif",
                    "bmp",
                    "gif",
                    "heif",
                    "jp2",
                    "jpeg",
                    "jxl",
                    "pdf",
                    "png",
                    "tiff",
                    "webp"
                  ]
                },
                "description": "The 11 accepted formats."
              },
              "accepted_extensions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "A file extension."
                },
                "description": "The 20 extensions of those formats."
              },
              "max_file_size_bytes": {
                "type": "integer",
                "description": "The largest accepted file: 10485760 (10 MiB)."
              },
              "max_request_size_bytes": {
                "type": "integer",
                "description": "The largest total size of the files of one request: 26214400 (25 MiB)."
              },
              "max_documents_per_request": {
                "type": "integer",
                "description": "The number of travelers of the booking: the most files one request can carry."
              }
            },
            "required": [
              "accepted_formats",
              "accepted_extensions",
              "max_file_size_bytes",
              "max_request_size_bytes",
              "max_documents_per_request"
            ]
          }
        },
        "required": [
          "booking_id",
          "acd_required",
          "acd_waived",
          "acd_submitted",
          "acd_paid",
          "complete",
          "can_upload",
          "blocked_reason",
          "required_documents",
          "uploaded_documents",
          "missing_documents",
          "missing_traveler_ids",
          "travelers",
          "unassigned_documents",
          "revision",
          "limits"
        ],
        "examples": [
          {
            "booking_id": "Y7IA34",
            "acd_required": true,
            "acd_waived": false,
            "acd_submitted": false,
            "acd_paid": false,
            "complete": false,
            "can_upload": true,
            "blocked_reason": null,
            "required_documents": 3,
            "uploaded_documents": 1,
            "missing_documents": 2,
            "missing_traveler_ids": [
              "1",
              "3"
            ],
            "travelers": [
              {
                "traveler_id": "1",
                "traveler_name": "JANE DOE",
                "traveler_type": "ADULT",
                "ticket_number": null,
                "status": "missing",
                "document": null
              },
              {
                "traveler_id": "2",
                "traveler_name": "JOHN DOE",
                "traveler_type": "ADULT",
                "ticket_number": null,
                "status": "uploaded",
                "document": {
                  "filename": "john-passport.pdf",
                  "content_type": "application/pdf",
                  "size_bytes": 482113,
                  "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                  "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                  "replaced_count": 0
                }
              },
              {
                "traveler_id": "3",
                "traveler_name": "JAMES DOE",
                "traveler_type": "ADULT",
                "ticket_number": null,
                "status": "missing",
                "document": null
              }
            ],
            "unassigned_documents": [],
            "revision": 1,
            "limits": {
              "accepted_formats": [
                "avif",
                "bmp",
                "gif",
                "heif",
                "jp2",
                "jpeg",
                "jxl",
                "pdf",
                "png",
                "tiff",
                "webp"
              ],
              "accepted_extensions": [
                "avif",
                "bmp",
                "dib",
                "gif",
                "heic",
                "heif",
                "j2k",
                "jfif",
                "jp2",
                "jpeg",
                "jpg",
                "jpx",
                "jxl",
                "pdf",
                "pjp",
                "pjpeg",
                "png",
                "tif",
                "tiff",
                "webp"
              ],
              "max_file_size_bytes": 10485760,
              "max_request_size_bytes": 26214400,
              "max_documents_per_request": 3
            }
          }
        ]
      },
      "GetAcdDocumentsRequest": {
        "type": "object",
        "description": "Which booking to read. When `booking_id` is sent together with `order_id` or `pnr` and the values differ, `booking_id` wins and an `ignored_field` warning names the dropped one.",
        "properties": {
          "booking_id": {
            "type": "string",
            "description": "The PNR, the supplier order id or the FX-Port booking UUID. Letters, digits and `- _ . : = ~`, up to 128 characters. `order_id` and `pnr` are accepted as other names of the same field.",
            "example": "Y7IA34"
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "bookingId": {
            "type": "string",
            "description": "The older spelling of `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "orderId": {
            "type": "string",
            "description": "The older spelling of `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The older spelling of `agency_id`.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          }
        },
        "required": [
          "booking_id"
        ]
      },
      "GetAcdDocumentsResponse": {
        "type": "object",
        "description": "The ACD state of a booking.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "Always `true` here: a booking without ACD is a `200` too."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The PNR, whatever identifier you sent.",
            "deprecated": true,
            "x-replaced-by": "pnr"
          },
          "pnr": {
            "type": "string",
            "description": "The PNR, whatever identifier you sent. The snake_case twin of `booking_id`, with the same value: use this name."
          },
          "acd": {
            "$ref": "#/components/schemas/AcdState"
          }
        },
        "required": [
          "success",
          "booking_id",
          "acd"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "requestId": "8201f6f6",
            "booking_id": "Y7IA34",
            "acd": {
              "booking_id": "Y7IA34",
              "acd_required": true,
              "acd_waived": false,
              "acd_submitted": false,
              "acd_paid": false,
              "complete": false,
              "can_upload": true,
              "blocked_reason": null,
              "required_documents": 3,
              "uploaded_documents": 1,
              "missing_documents": 2,
              "missing_traveler_ids": [
                "1",
                "3"
              ],
              "travelers": [
                {
                  "traveler_id": "1",
                  "traveler_name": "JANE DOE",
                  "traveler_type": "ADULT",
                  "ticket_number": null,
                  "status": "missing",
                  "document": null
                },
                {
                  "traveler_id": "2",
                  "traveler_name": "JOHN DOE",
                  "traveler_type": "ADULT",
                  "ticket_number": null,
                  "status": "uploaded",
                  "document": {
                    "filename": "john-passport.pdf",
                    "content_type": "application/pdf",
                    "size_bytes": 482113,
                    "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                    "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                    "replaced_count": 0
                  }
                },
                {
                  "traveler_id": "3",
                  "traveler_name": "JAMES DOE",
                  "traveler_type": "ADULT",
                  "ticket_number": null,
                  "status": "missing",
                  "document": null
                }
              ],
              "unassigned_documents": [],
              "revision": 1,
              "limits": {
                "accepted_formats": [
                  "avif",
                  "bmp",
                  "gif",
                  "heif",
                  "jp2",
                  "jpeg",
                  "jxl",
                  "pdf",
                  "png",
                  "tiff",
                  "webp"
                ],
                "accepted_extensions": [
                  "avif",
                  "bmp",
                  "dib",
                  "gif",
                  "heic",
                  "heif",
                  "j2k",
                  "jfif",
                  "jp2",
                  "jpeg",
                  "jpg",
                  "jpx",
                  "jxl",
                  "pdf",
                  "pjp",
                  "pjpeg",
                  "png",
                  "tif",
                  "tiff",
                  "webp"
                ],
                "max_file_size_bytes": 10485760,
                "max_request_size_bytes": 26214400,
                "max_documents_per_request": 3
              }
            }
          }
        ]
      },
      "AcdUploadRequest": {
        "type": "object",
        "description": "The form fields of an upload (`multipart/form-data`).",
        "properties": {
          "booking_id": {
            "type": "string",
            "description": "The PNR, the supplier order id or the FX-Port booking UUID. `order_id` and `pnr` are accepted as other names. When the form holds several spellings with different values, the first of `booking_id`, `bookingId`, `order_id`, `orderId`, `pnr` wins and an `ignored_field` warning names each dropped one.",
            "example": "Y7IA34"
          },
          "traveler_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A traveler id."
            },
            "description": "The travelers the files belong to, **in the same order as `documents`**. Send the field once for each file, or one comma-separated value (`1,2`), or a JSON array as text (`[\"1\",\"2\"]`). A traveler may appear once per request."
          },
          "documents": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A passport file.",
              "format": "binary"
            },
            "description": "One passport file for each traveler: PDF or an image, 10 MiB at most each, 25 MiB for all together. The field name must be `documents`."
          },
          "replace": {
            "type": "string",
            "enum": [
              "true",
              "false"
            ],
            "description": "`true` to overwrite travelers that already have a passport (`1`, `yes`, `on` and `0`, `no`, `off` are read too). Default `false`."
          },
          "agency_id": {
            "type": "string",
            "description": "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."
          },
          "bookingId": {
            "type": "string",
            "description": "The older spelling of `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "orderId": {
            "type": "string",
            "description": "The older spelling of `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "agencyId": {
            "type": "string",
            "description": "The older spelling of `agency_id`.",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          },
          "travelerIds": {
            "type": "string",
            "description": "The older spelling of `traveler_ids`.",
            "deprecated": true,
            "x-replaced-by": "traveler_ids"
          },
          "travelerId": {
            "type": "string",
            "description": "The older spelling of `traveler_ids`.",
            "deprecated": true,
            "x-replaced-by": "traveler_ids"
          },
          "traveler_id": {
            "type": "string",
            "description": "The older spelling of `traveler_ids`.",
            "deprecated": true,
            "x-replaced-by": "traveler_ids"
          },
          "files": {
            "type": "string",
            "description": "The older spelling of `documents`.",
            "format": "binary",
            "deprecated": true,
            "x-replaced-by": "documents"
          },
          "file": {
            "type": "string",
            "description": "The older spelling of `documents`.",
            "format": "binary",
            "deprecated": true,
            "x-replaced-by": "documents"
          },
          "document": {
            "type": "string",
            "description": "The older spelling of `documents`.",
            "format": "binary",
            "deprecated": true,
            "x-replaced-by": "documents"
          }
        },
        "required": [
          "booking_id",
          "traveler_ids",
          "documents"
        ]
      },
      "AcdUploadResult": {
        "type": "object",
        "description": "What happened to one file of the request.",
        "properties": {
          "index": {
            "type": "integer",
            "description": "The position of the file in your request, from 0."
          },
          "traveler_id": {
            "type": "string",
            "description": "The traveler of this file."
          },
          "traveler_name": {
            "anyOf": [
              {
                "type": "string",
                "description": "The name on the booking, `null` when the id is not on the booking."
              },
              {
                "type": "null"
              }
            ],
            "description": "The name on the booking, `null` when the id is not on the booking."
          },
          "filename": {
            "type": "string",
            "description": "The file name you sent (`document-<n>` when the part had none)."
          },
          "status": {
            "type": "string",
            "enum": [
              "uploaded",
              "replaced",
              "failed",
              "skipped"
            ],
            "description": "`uploaded`: a new passport. `replaced`: a passport that existed was replaced. `failed`: this file has a problem (see `error`). `skipped`: this file is valid, but another file of the request failed, so it was not processed."
          },
          "content_type": {
            "type": "string",
            "description": "Only for `uploaded` and `replaced`: the detected type."
          },
          "size_bytes": {
            "type": "integer",
            "description": "Only for `uploaded` and `replaced`: the size of the stored file."
          },
          "download_url": {
            "type": "string",
            "description": "Only for `uploaded` and `replaced`: the public address of the stored file."
          },
          "error": {
            "type": "object",
            "description": "Only for `failed`: what is wrong with this file.",
            "properties": {
              "code": {
                "type": "string",
                "description": "The code of the problem."
              },
              "message": {
                "type": "string",
                "description": "A sentence about it."
              },
              "expected_traveler_ids": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "A traveler id."
                },
                "description": "`UNKNOWN_TRAVELER_ID`: the ids of the booking."
              },
              "accepted_extensions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An extension."
                },
                "description": "`UNSUPPORTED_FILE_TYPE`: the accepted extensions."
              },
              "max_file_size_bytes": {
                "type": "integer",
                "description": "`FILE_TOO_LARGE`: the limit."
              },
              "reason": {
                "type": "string",
                "description": "`STORAGE_UPLOAD_FAILED`: always the fixed text `storage_error`. The message of the storage system is never returned."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "index",
          "traveler_id",
          "filename",
          "status"
        ]
      },
      "AcdNotification": {
        "type": "object",
        "description": "Whether the ACD team was notified. The e-mail goes to FX-Port, not to you or the traveler.",
        "properties": {
          "sent": {
            "type": "boolean",
            "description": "Whether FX-Port's ACD team was e-mailed."
          },
          "type": {
            "type": "string",
            "enum": [
              "submitted",
              "updated"
            ],
            "description": "`submitted` when the set became complete, `updated` after a replacement on a complete set. Absent when nothing was due."
          },
          "skipped": {
            "type": "boolean",
            "description": "`true` when no e-mail was due or sent: see `reason`."
          },
          "reason": {
            "type": "string",
            "description": "Why no e-mail was sent: `awaiting_remaining_documents`, `hold_booking` or `sandbox_environment`; with `error` it is the fixed text `email_error` (the message of the mail system is never returned)."
          },
          "error": {
            "type": "string",
            "description": "`EMAIL_SEND_FAILED` when the e-mail failed. The files are saved."
          }
        },
        "required": [
          "sent"
        ],
        "additionalProperties": true
      },
      "UploadAcdDocumentsResponse": {
        "type": "object",
        "description": "The result of an upload: HTTP `200` when every file was stored, `207` when only some were.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "description": "A problem that did not stop the upload (`EMAIL_SEND_FAILED`) or a notice about your request.",
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Problems that did not stop the upload."
          },
          "success": {
            "type": "boolean",
            "description": "`true` for `200` and for `207`."
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "partial_success"
            ],
            "description": "`success`: every file stored. `partial_success`: some stored, some failed (HTTP `207`)."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The PNR.",
            "deprecated": true,
            "x-replaced-by": "pnr"
          },
          "pnr": {
            "type": "string",
            "description": "The PNR. The snake_case twin of `booking_id`, with the same value: use this name."
          },
          "message": {
            "type": "string",
            "description": "A sentence: `2 documents saved.`"
          },
          "summary": {
            "type": "object",
            "description": "The counts of this request.",
            "properties": {
              "received": {
                "type": "integer",
                "description": "Files in the request."
              },
              "uploaded": {
                "type": "integer",
                "description": "New passports."
              },
              "replaced": {
                "type": "integer",
                "description": "Replaced passports."
              },
              "failed": {
                "type": "integer",
                "description": "Files that failed."
              }
            },
            "required": [
              "received",
              "uploaded",
              "replaced",
              "failed"
            ]
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcdUploadResult"
            },
            "description": "One entry for each file, in the order you sent them."
          },
          "acd": {
            "$ref": "#/components/schemas/AcdState"
          },
          "notification": {
            "$ref": "#/components/schemas/AcdNotification"
          }
        },
        "required": [
          "success",
          "status",
          "booking_id",
          "message",
          "summary",
          "results",
          "acd",
          "notification",
          "warnings"
        ],
        "examples": [
          {
            "environment": "sandbox",
            "success": true,
            "status": "success",
            "requestId": "58bfcf4a",
            "booking_id": "Y7IA34",
            "message": "2 documents saved.",
            "summary": {
              "received": 2,
              "uploaded": 2,
              "replaced": 0,
              "failed": 0
            },
            "results": [
              {
                "index": 0,
                "traveler_id": "1",
                "traveler_name": "JANE DOE",
                "filename": "passport-1.png",
                "status": "uploaded",
                "content_type": "image/png",
                "size_bytes": 69,
                "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/1/20261101142620_28e115c8.png"
              },
              {
                "index": 1,
                "traveler_id": "3",
                "traveler_name": "JAMES DOE",
                "filename": "passport-3.pdf",
                "status": "uploaded",
                "content_type": "application/pdf",
                "size_bytes": 191,
                "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/3/20261101142620_4194315e.pdf"
              }
            ],
            "acd": {
              "booking_id": "Y7IA34",
              "acd_required": true,
              "acd_waived": false,
              "acd_submitted": true,
              "acd_paid": false,
              "complete": true,
              "can_upload": true,
              "blocked_reason": null,
              "required_documents": 3,
              "uploaded_documents": 3,
              "missing_documents": 0,
              "missing_traveler_ids": [],
              "travelers": [
                {
                  "traveler_id": "1",
                  "traveler_name": "JANE DOE",
                  "traveler_type": "ADULT",
                  "ticket_number": null,
                  "status": "missing",
                  "document": null
                },
                {
                  "traveler_id": "2",
                  "traveler_name": "JOHN DOE",
                  "traveler_type": "ADULT",
                  "ticket_number": null,
                  "status": "uploaded",
                  "document": {
                    "filename": "john-passport.pdf",
                    "content_type": "application/pdf",
                    "size_bytes": 482113,
                    "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
                    "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
                    "replaced_count": 0
                  }
                },
                {
                  "traveler_id": "3",
                  "traveler_name": "JAMES DOE",
                  "traveler_type": "ADULT",
                  "ticket_number": null,
                  "status": "missing",
                  "document": null
                }
              ],
              "unassigned_documents": [],
              "revision": 2,
              "limits": {
                "accepted_formats": [
                  "avif",
                  "bmp",
                  "gif",
                  "heif",
                  "jp2",
                  "jpeg",
                  "jxl",
                  "pdf",
                  "png",
                  "tiff",
                  "webp"
                ],
                "accepted_extensions": [
                  "avif",
                  "bmp",
                  "dib",
                  "gif",
                  "heic",
                  "heif",
                  "j2k",
                  "jfif",
                  "jp2",
                  "jpeg",
                  "jpg",
                  "jpx",
                  "jxl",
                  "pdf",
                  "pjp",
                  "pjpeg",
                  "png",
                  "tif",
                  "tiff",
                  "webp"
                ],
                "max_file_size_bytes": 10485760,
                "max_request_size_bytes": 26214400,
                "max_documents_per_request": 3
              }
            },
            "notification": {
              "sent": false,
              "type": "submitted",
              "skipped": true,
              "reason": "sandbox_environment"
            },
            "warnings": []
          }
        ]
      },
      "AcdError": {
        "type": "object",
        "description": "The flat error body of the ACD endpoints. The unified `errors[]` is added next to it.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "Always `false`."
          },
          "status": {
            "type": "string",
            "description": "Always `failed`."
          },
          "error": {
            "type": "string",
            "description": "The code in lower case."
          },
          "code": {
            "type": "string",
            "description": "The code in upper case: the stable code of these endpoints."
          },
          "message": {
            "type": "string",
            "description": "A sentence about the problem."
          },
          "requestId": {
            "type": "string",
            "description": "The id of this request, 8 characters, for support.",
            "deprecated": true,
            "x-replaced-by": "request_id"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcdUploadResult"
            },
            "description": "The files and their problems, for the failures that are about files."
          },
          "acd": {
            "$ref": "#/components/schemas/AcdState"
          },
          "received_traveler_ids": {
            "type": "integer",
            "description": "`DOCUMENT_COUNT_MISMATCH`: how many ids you sent."
          },
          "received_documents": {
            "type": "integer",
            "description": "`DOCUMENT_COUNT_MISMATCH`, `TOO_MANY_DOCUMENTS`: how many files you sent."
          },
          "expected_travelers": {
            "type": "integer",
            "description": "`TOO_MANY_DOCUMENTS`: the travelers of the booking."
          },
          "duplicate_traveler_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A traveler id."
            },
            "description": "`DUPLICATE_TRAVELER_ID`: the ids that appear twice."
          },
          "max_request_size_bytes": {
            "type": "integer",
            "description": "`REQUEST_TOO_LARGE`: the limit."
          },
          "booking_status": {
            "type": "string",
            "description": "`BOOKING_NOT_ELIGIBLE`: the status of the booking."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorItem"
            },
            "description": "The unified error list: `errors[0].code` is the code in lower case."
          },
          "meta": {
            "description": "`request_id` and `status`.",
            "additionalProperties": true,
            "type": "object"
          }
        },
        "required": [
          "success",
          "code",
          "message"
        ],
        "additionalProperties": true,
        "examples": [
          {
            "environment": "sandbox",
            "success": false,
            "status": "failed",
            "error": "acd_not_required",
            "code": "ACD_NOT_REQUIRED",
            "message": "This booking does not require ACD documents.",
            "acd": {
              "booking_id": "Y7HKJM",
              "acd_required": false,
              "acd_waived": false,
              "acd_submitted": false,
              "acd_paid": false,
              "complete": false,
              "can_upload": false,
              "blocked_reason": "ACD_NOT_REQUIRED",
              "required_documents": 0,
              "uploaded_documents": 0,
              "missing_documents": 0,
              "missing_traveler_ids": [],
              "travelers": [
                {
                  "traveler_id": "1",
                  "traveler_name": "JOHN SMITH",
                  "traveler_type": "ADULT",
                  "ticket_number": "124-2446977395",
                  "status": "missing",
                  "document": null
                }
              ],
              "unassigned_documents": [],
              "revision": 0,
              "limits": {
                "accepted_formats": [
                  "avif",
                  "bmp",
                  "gif",
                  "heif",
                  "jp2",
                  "jpeg",
                  "jxl",
                  "pdf",
                  "png",
                  "tiff",
                  "webp"
                ],
                "accepted_extensions": [
                  "avif",
                  "bmp",
                  "dib",
                  "gif",
                  "heic",
                  "heif",
                  "j2k",
                  "jfif",
                  "jp2",
                  "jpeg",
                  "jpg",
                  "jpx",
                  "jxl",
                  "pdf",
                  "pjp",
                  "pjpeg",
                  "png",
                  "tif",
                  "tiff",
                  "webp"
                ],
                "max_file_size_bytes": 10485760,
                "max_request_size_bytes": 26214400,
                "max_documents_per_request": 1
              }
            },
            "requestId": "5b73272a"
          }
        ]
      },
      "HotelSearchRequest": {
        "type": "object",
        "description": "A geo search: the hotels around a point (`latitude`, `longitude` and `radius`) priced for a stay, or one hotel (`hotel_id`) instead of the point. The dates and the rooms are always required. Do not send `currency`: it is the currency of your agency and a `currency` field is refused.",
        "properties": {
          "latitude": {
            "type": "number",
            "description": "Geo search: the latitude of the center of the search, -90 to 90. Send it with `longitude` and `radius`; the coordinates of a place come from the locations search. A number written as text (`\"48.8566\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": -90,
            "maximum": 90,
            "example": 48.8566
          },
          "longitude": {
            "type": "number",
            "description": "Geo search: the longitude of the center, -180 to 180. A number written as text (`\"2.3522\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": -180,
            "maximum": 180,
            "example": 2.3522
          },
          "radius": {
            "type": "integer",
            "description": "Geo search: the radius around the center, in meters, 100 to 70,000. Required for a geo search; there is no default. A value outside the range is moved to the nearest bound and the answer carries a `coerced_value` warning. A number written as text (`\"3000\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": 100,
            "maximum": 70000,
            "example": 3000
          },
          "hotel_id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Search one hotel instead of the hotels around a point: its FX-Port id (1 to 128 characters: letters, digits and `_ . : -`; spaces around it are trimmed). A JSON integer is read as its text, but it is not looked up as the supplier's numeric `hid` here: send the FX-Port id. When it is sent, `latitude`, `longitude` and `radius` are ignored; a `null` `hotel_id` next to `latitude` and `longitude` is a geo search.",
            "example": "test_hotel_do_not_book"
          },
          "checkin": {
            "type": "string",
            "description": "Check-in date, `YYYY-MM-DD`: from yesterday (for time zones) to 400 days ahead. A date written with a midnight time (`2026-12-01T00:00:00Z`) is read as that day, with a `coerced_value` warning; any other time of day is `invalid_date`.",
            "example": "2026-12-01"
          },
          "checkout": {
            "type": "string",
            "description": "Check-out date, `YYYY-MM-DD`, after `checkin`. A stay is 1 to 30 nights. A midnight time is read as in `checkin`.",
            "example": "2026-12-04"
          },
          "guests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelGuestRoom"
            },
            "description": "The rooms: one `{adults, children}` object per room, 1 to 9 rooms."
          },
          "residency": {
            "type": "string",
            "description": "Residency of the guests, a two-letter ISO 3166-1 country code (`gb`, `dz`, `fr`). A sub-territory code of the website's nationality list (`GB-SCT`, `USPR`, `ES-XCI`, `PT-MA`, `BQ-SA` ...) is read as its country, with a `coerced_value` warning: send the 2-letter country. Some rates exist for some residencies only. Not defaulted: always send it.",
            "example": "gb"
          },
          "language": {
            "type": "string",
            "description": "Language of the supplier's answer, such as `en`, `fr` or `zh_CN`. A longer tag (`zh-Hans-CN`, `es-419`, `sr-Latn-RS`) is read as the supplier's code, with a `coerced_value` warning. The hotel content in the answer is English whatever you send.",
            "default": "en",
            "example": "en"
          },
          "id": {
            "type": "string",
            "description": "The hotel id, in its older spelling. Use `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hotelId": {
            "type": "string",
            "description": "The hotel id, in its older spelling. Use `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "checkinDate": {
            "type": "string",
            "description": "The check-in date, in an older spelling. Use `checkin`.",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "checkin_date": {
            "type": "string",
            "description": "The check-in date, in an older spelling. Use `checkin`.",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "check_in": {
            "type": "string",
            "description": "The check-in date, in an older spelling. Use `checkin`.",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "checkoutDate": {
            "type": "string",
            "description": "The check-out date, in an older spelling. Use `checkout`.",
            "deprecated": true,
            "x-replaced-by": "checkout"
          },
          "checkout_date": {
            "type": "string",
            "description": "The check-out date, in an older spelling. Use `checkout`.",
            "deprecated": true,
            "x-replaced-by": "checkout"
          },
          "check_out": {
            "type": "string",
            "description": "The check-out date, in an older spelling. Use `checkout`.",
            "deprecated": true,
            "x-replaced-by": "checkout"
          }
        },
        "required": [
          "checkin",
          "checkout",
          "guests"
        ]
      },
      "HotelProcessingStatus": {
        "type": "object",
        "description": "How many hotels of the search have content.",
        "properties": {
          "total_hotels": {
            "type": "integer",
            "description": "Hotels the supplier priced for the search."
          },
          "cached_hotels": {
            "type": "integer",
            "description": "Of them, the hotels whose content FX-Port holds: they are in `hotels_details`."
          },
          "queued_hotels": {
            "type": "integer",
            "description": "Of them, the hotels without cached content that were queued for loading: their ids are in `missing_hotel_ids`."
          }
        },
        "required": [
          "total_hotels",
          "cached_hotels",
          "queued_hotels"
        ]
      },
      "HotelQueueInfo": {
        "type": "object",
        "description": "The hotels without cached content that FX-Port loads in the background.",
        "properties": {
          "queued_hotel_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A hotel id."
            },
            "description": "The ids of the hotels whose content was queued for loading (at most 50 per search)."
          },
          "estimated_completion": {
            "anyOf": [
              {
                "type": "string",
                "description": "A rough delay until the content is loaded, such as `about 9 minutes`. `null` when nothing was queued."
              },
              {
                "type": "null"
              }
            ],
            "description": "A rough delay until the content is loaded, such as `about 9 minutes`. `null` when nothing was queued."
          },
          "estimated_completion_seconds": {
            "type": "integer",
            "description": "The same delay in seconds. Present only when hotels were queued."
          }
        },
        "required": [
          "queued_hotel_ids",
          "estimated_completion"
        ]
      },
      "HotelSearchTiming": {
        "type": "object",
        "description": "Where the time of a search went, in seconds.",
        "properties": {
          "total_duration": {
            "type": "number",
            "description": "Seconds the whole search took, in FX-Port."
          },
          "search_duration": {
            "type": "number",
            "description": "Seconds the supplier took to price the hotels."
          },
          "cache_check_duration": {
            "type": "number",
            "description": "Seconds FX-Port took to read the hotel content."
          }
        },
        "required": [
          "total_duration",
          "search_duration",
          "cache_check_duration"
        ]
      },
      "HotelPricingEntry": {
        "type": "object",
        "description": "The price of the cheapest rate of one hotel for the whole stay. It carries no room, meal or cancellation terms: call `get_rates` for them.",
        "properties": {
          "exchange_rate": {
            "type": "number",
            "description": "The rate of exchange from `EUR` to `agency_currency` (`285.0` in the sandbox)."
          },
          "agency_currency": {
            "type": "string",
            "description": "The currency of your agency.",
            "example": "DZD"
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission, in `agency_currency`."
          },
          "final_price": {
            "type": "number",
            "description": "What the traveler pays for the cheapest rate: the whole stay, all rooms, in `agency_currency`."
          },
          "b2b_price": {
            "type": "number",
            "description": "What your agency is debited: `final_price - agency_commission`."
          }
        },
        "required": [
          "exchange_rate",
          "agency_currency",
          "agency_commission",
          "final_price",
          "b2b_price"
        ]
      },
      "HotelSearchResponse": {
        "type": "object",
        "description": "The answer of `search_advanced`. It always answers JSON in one piece: `stream=true` is ignored for API keys.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "search_id": {
            "type": "string",
            "description": "The id of the search, also in the `X-Search-Id` header. Quote it to support.",
            "example": "71498fa9-d3a7-4b6c-81bd-76693be726d1"
          },
          "status": {
            "type": "string",
            "enum": [
              "complete",
              "partial",
              "cache_miss",
              "no_hotels_found"
            ],
            "description": "`complete`: every priced hotel has content. `partial`: some do not (see `missing_hotel_ids`). `cache_miss`: none has. `no_hotels_found`: no hotel is available for the request."
          },
          "hotels_details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelMini"
            },
            "description": "The mini card of each hotel that has cached content. No rates here: the prices are in `hotels_pricing`."
          },
          "processing_status": {
            "$ref": "#/components/schemas/HotelProcessingStatus"
          },
          "queue_info": {
            "$ref": "#/components/schemas/HotelQueueInfo"
          },
          "timing": {
            "$ref": "#/components/schemas/HotelSearchTiming"
          },
          "hotels_pricing": {
            "type": "object",
            "description": "The price of the cheapest rate of every priced hotel, by hotel id. Absent when `status` is `no_hotels_found`.",
            "properties": {},
            "additionalProperties": {
              "$ref": "#/components/schemas/HotelPricingEntry"
            }
          },
          "missing_hotel_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A hotel id."
            },
            "description": "The ids of the hotels that are priced but have no cached content yet. Get one with `get_hotel_info`. Absent when `status` is `no_hotels_found`."
          },
          "missing_hotels_pricing": {
            "type": "object",
            "description": "The price entry of each hotel of `missing_hotel_ids`, with the same fields as `hotels_pricing`.",
            "properties": {},
            "additionalProperties": {
              "$ref": "#/components/schemas/HotelPricingEntry"
            }
          },
          "truncated": {
            "type": "boolean",
            "description": "`true` when the supplier found more hotels than one search answers and the rest was cut off. Present only then: narrow the search."
          },
          "max_hotels": {
            "type": "integer",
            "description": "The most hotels one search answers. Present with `truncated`."
          },
          "total_hotels_found": {
            "type": "integer",
            "description": "How many hotels the supplier found before the cut. Present with `truncated`."
          },
          "note": {
            "type": "string",
            "description": "A hint about an empty answer, for example that `test_hotel` has no availability for the residency you sent. Present only when there is one."
          }
        },
        "required": [
          "search_id",
          "status",
          "hotels_details",
          "processing_status",
          "queue_info",
          "timing"
        ]
      },
      "HotelLocation": {
        "type": "object",
        "description": "A place a hotel search can start from: a city, an airport or a hotel. The keys are camelCase and have no snake_case twin: add `?naming=snake` to get them in snake_case.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the place at the location provider (a city id, an airport code or a hotel id of the provider). It is not a FX-Port hotel id.",
            "example": "20119"
          },
          "name": {
            "type": "string",
            "description": "Name of the place.",
            "example": "Tegucigalpa"
          },
          "displayName": {
            "type": "string",
            "description": "Name with its context, for a list.",
            "example": "Tegucigalpa, Honduras"
          },
          "city": {
            "type": "string",
            "description": "City of the place."
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code.",
            "example": "HN"
          },
          "latitude": {
            "type": "number",
            "description": "Latitude: send it to `search_advanced`.",
            "example": 14.094204124743296
          },
          "longitude": {
            "type": "number",
            "description": "Longitude: send it to `search_advanced`.",
            "example": -87.19410705810549
          },
          "type": {
            "type": "string",
            "description": "Always `location`. The kind of place is `raw.loctype`: `city`, `ap` (airport), `hotel` ...",
            "example": "location"
          },
          "raw": {
            "description": "The record of the location provider, about 40 keys (`loctype`, `lat`, `lng`, `cc`, `cityname`, `timezone`, `box_*`, `destination_images` ...). Best effort: unlisted keys can change.",
            "additionalProperties": true,
            "type": "object"
          }
        },
        "required": [
          "id",
          "name",
          "displayName",
          "latitude",
          "longitude",
          "type",
          "raw"
        ]
      },
      "HotelLocationsResponse": {
        "type": "object",
        "description": "The answer of the location search.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the lookup worked, also with no result."
          },
          "query": {
            "type": "string",
            "description": "The text you searched for."
          },
          "resultsCount": {
            "type": "integer",
            "description": "How many places are in `locations`."
          },
          "locations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelLocation"
            },
            "description": "The places that match, best first. Places without coordinates are dropped before `limit` is applied."
          }
        },
        "required": [
          "success",
          "query",
          "resultsCount",
          "locations"
        ]
      },
      "HotelNameMatch": {
        "type": "object",
        "description": "A hotel found by name. With `include_details=true` the hit is the whole cached hotel document (as `POST /hotels/details` returns it) plus `match_score` and `match_type`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The FX-Port hotel id: send it as `hotel_id`.",
            "example": "sofitel_algiers_hamma_garden"
          },
          "name": {
            "type": "string",
            "description": "Name of the hotel.",
            "example": "Sofitel Algiers Hamma Garden"
          },
          "match_score": {
            "type": "number",
            "description": "How well the name matches your text, 0 to 100, two decimals. Below about 80 the hit is often another hotel.",
            "minimum": 0,
            "maximum": 100,
            "example": 83.39
          },
          "match_type": {
            "type": "string",
            "enum": [
              "exact",
              "partial",
              "fuzzy"
            ],
            "description": "How the name matched."
          },
          "address": {
            "type": "string",
            "description": "Street address."
          },
          "city": {
            "type": "string",
            "description": "City."
          },
          "country_code": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code."
          },
          "postal_code": {
            "type": "string",
            "description": "Postal code; an empty string when unknown."
          }
        },
        "required": [
          "id",
          "name",
          "match_score",
          "match_type"
        ],
        "additionalProperties": true
      },
      "HotelNameSearchResponse": {
        "type": "object",
        "description": "The answer of the name search.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "query": {
            "type": "string",
            "description": "The text you searched for."
          },
          "results_count": {
            "type": "integer",
            "description": "How many hotels are in `hotels`."
          },
          "search_time": {
            "type": "string",
            "description": "Time the search took, as text with its unit, such as `0.061s`."
          },
          "hotels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelNameMatch"
            },
            "description": "The hotels that match, best match first."
          }
        },
        "required": [
          "query",
          "results_count",
          "search_time",
          "hotels"
        ]
      },
      "HotelNameSearchRequest": {
        "type": "object",
        "description": "A hotel name search sent as a body.",
        "properties": {
          "query": {
            "type": "string",
            "description": "The hotel name or a part of it, 2 to 100 characters. Typos and accents are tolerated.",
            "example": "Sofitel Algiers"
          },
          "limit": {
            "type": "integer",
            "description": "Most hits to return, 1 to 100. A larger number is capped at 100, with a `coerced_value` warning. A whole number written as text (`\"10\"`) is read as the number, with the same warning.",
            "minimum": 1,
            "maximum": 100,
            "default": 10
          },
          "min_score": {
            "type": "number",
            "description": "Lowest match score to return, 0 to 100. A number written as text (`\"60\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": 0,
            "maximum": 100,
            "default": 60
          },
          "include_location": {
            "type": "boolean",
            "description": "Add the address, city, country code and postal code to each hit. Ignored with `include_details`. The text `true` or `false` (also `1`, `0`, `yes`, `no`, `on`, `off`) is read as the boolean, with a `coerced_value` warning; `null` means the default.",
            "default": true
          },
          "include_details": {
            "type": "boolean",
            "description": "Return the whole cached hotel document for each hit instead of the summary. About 26 KB per hit. Text is read as in `include_location`.",
            "default": false
          }
        },
        "required": [
          "query"
        ]
      },
      "HotelIdsRequest": {
        "type": "object",
        "description": "The ids of the hotels to read. The older form, a bare JSON list of ids as the whole body, still works.",
        "properties": {
          "hotel_ids": {
            "type": "array",
            "items": {
              "type": [
                "string",
                "integer"
              ],
              "description": "A hotel id: text, or the numeric supplier id (`hid`) as a JSON integer."
            },
            "description": "The ids of the hotels, at most 200. An id made of digits is also looked up as the supplier's numeric `hid`: `[8473727]` finds the hotel. Spaces around an id are trimmed. Duplicates are dropped; the answer does not follow your order.",
            "maxItems": 200
          },
          "hotelIds": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A hotel id."
            },
            "description": "The hotel ids, in their older spelling. Use `hotel_ids`.",
            "deprecated": true,
            "x-replaced-by": "hotel_ids"
          },
          "ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A hotel id."
            },
            "description": "The hotel ids, in their older spelling. Use `hotel_ids`.",
            "deprecated": true,
            "x-replaced-by": "hotel_ids"
          }
        },
        "required": [
          "hotel_ids"
        ]
      },
      "HotelInfoRequest": {
        "type": "object",
        "description": "The hotel to read. Send `hotel_id` or `hotel_ids`.",
        "properties": {
          "hotel_id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "One hotel: its FX-Port id, or its numeric supplier id (`hid`, a JSON integer or digits). Only one id gets a live fetch when the hotel is not cached.",
            "example": "test_hotel_do_not_book"
          },
          "hotel_ids": {
            "type": "array",
            "items": {
              "type": [
                "string",
                "integer"
              ],
              "description": "A hotel id: text, or the numeric supplier id (`hid`) as a JSON integer."
            },
            "description": "Several hotels, at most 200. Only hotels already cached are returned (no live fetch). Wins over `hotel_id`.",
            "maxItems": 200
          },
          "detail_level": {
            "type": "string",
            "enum": [
              "full",
              "minimal",
              "mini"
            ],
            "description": "`full` (default) returns the whole document, `minimal` (alias `mini`) the compact card for result lists.",
            "default": "full"
          },
          "images_limit": {
            "type": "integer",
            "description": "With `detail_level` `minimal`: keep only the first N photos of each hotel, 0 to 30. A whole number written as text is read as the number.",
            "minimum": 0,
            "maximum": 30
          },
          "language": {
            "type": "string",
            "description": "Language of the live fetch of an uncached hotel. A cached hotel is English whatever you send.",
            "default": "en",
            "example": "en"
          },
          "hotelId": {
            "type": "string",
            "description": "One hotel id, in its older spelling. Use `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "id": {
            "type": "string",
            "description": "One hotel id, in an older spelling. Use `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hid": {
            "type": "string",
            "description": "The supplier's numeric hotel id, in an older spelling. Use `hotel_id`, which takes it as well.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hotelIds": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A hotel id."
            },
            "description": "Several hotel ids, in their older spelling. Use `hotel_ids`.",
            "deprecated": true,
            "x-replaced-by": "hotel_ids"
          },
          "ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A hotel id."
            },
            "description": "Several hotel ids, in an older spelling. Use `hotel_ids`.",
            "deprecated": true,
            "x-replaced-by": "hotel_ids"
          },
          "detailLevel": {
            "type": "string",
            "description": "The detail level, in its older spelling. Use `detail_level`.",
            "deprecated": true,
            "x-replaced-by": "detail_level"
          },
          "imagesLimit": {
            "type": "integer",
            "description": "The photo limit, in its older spelling. Use `images_limit`.",
            "deprecated": true,
            "x-replaced-by": "images_limit"
          }
        }
      },
      "HotelInfoResponse": {
        "type": "object",
        "description": "The answer of `get_hotel_info`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "data": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/HotelInfo"
                },
                {
                  "$ref": "#/components/schemas/HotelMini"
                }
              ]
            },
            "description": "The hotels that were found, in no particular order: full camelCase documents (`HotelInfo`), or compact cards (`HotelMini`, snake_case) with `detail_level` `minimal`. Hotels that are not found are left out: the list is empty when none is."
          }
        },
        "required": [
          "data"
        ]
      },
      "HotelRegion": {
        "type": "object",
        "description": "The city or area a hotel is in.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Supplier id of the region."
          },
          "country_code": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code, upper case.",
            "example": "HN"
          },
          "iata": {
            "type": "string",
            "description": "IATA code of the main airport or city of the region."
          },
          "name": {
            "type": "string",
            "description": "Name of the region, usually the city.",
            "example": "Tegucigalpa"
          },
          "type": {
            "type": "string",
            "description": "Kind of region as the supplier names it: `City`, `Region`, `Airport` ...",
            "example": "City"
          },
          "type_v2": {
            "type": "string",
            "description": "The same kind in lower case (`city`). Missing for a few hotels.",
            "example": "city"
          }
        },
        "required": [
          "id",
          "country_code",
          "iata",
          "name",
          "type"
        ],
        "examples": [
          {
            "id": 3421,
            "country_code": "HN",
            "iata": "TGU",
            "name": "Tegucigalpa",
            "type": "City",
            "type_v2": "city"
          }
        ]
      },
      "HotelAmenityGroup": {
        "type": "object",
        "description": "A group of amenities of a hotel.",
        "properties": {
          "group_name": {
            "type": "string",
            "description": "Name of the group: `General`, `Rooms`, `Services and amenities`, `Meals`, `Sports and Recreation` ...",
            "example": "General"
          },
          "amenities": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "An amenity of the hotel."
            },
            "description": "Every amenity of the group, free or paid."
          },
          "non_free_amenities": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "An amenity that costs extra."
                },
                "description": "The amenities of `amenities` that are paid at the property. `null` or empty when all are free."
              },
              {
                "type": "null"
              }
            ],
            "description": "The amenities of `amenities` that are paid at the property. `null` or empty when all are free."
          }
        },
        "required": [
          "group_name",
          "amenities"
        ]
      },
      "HotelMini": {
        "type": "object",
        "description": "The compact card of a hotel, for a list of search results: identity, place, stars, photos and amenities. The same 13 fields come back from `search_advanced` (`hotels_details[]`), from `POST /hotels/search?detail_level=minimal` and from `get_hotel_info` with `detail_level=minimal`. It carries no price: prices come from `hotels_pricing` and `get_rates`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The FX-Port id of the hotel, a lower-case slug. Send it as `hotel_id` to `get_rates` and `get_hotel_info`.",
            "example": "test_hotel_do_not_book"
          },
          "hid": {
            "type": "integer",
            "description": "The supplier's numeric hotel id. `get_hotel_info` also accepts it as a lookup key.",
            "example": 8473727
          },
          "name": {
            "type": "string",
            "description": "Name of the hotel.",
            "example": "Test Hotel (Do Not Book) test"
          },
          "kind": {
            "type": "string",
            "description": "Type of property as the supplier names it: `Hotel`, `Apartment`, `Resort`, `Hostel`, `Guesthouse`, `Boutique and Design` ...",
            "example": "Apartment"
          },
          "address": {
            "type": "string",
            "description": "Street address, in English.",
            "example": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa"
          },
          "latitude": {
            "type": "number",
            "description": "Latitude of the hotel.",
            "example": 14.079872
          },
          "longitude": {
            "type": "number",
            "description": "Longitude of the hotel.",
            "example": -87.21677
          },
          "postal_code": {
            "anyOf": [
              {
                "type": "string",
                "description": "Postal code. `null` for a minority of hotels."
              },
              {
                "type": "null"
              }
            ],
            "description": "Postal code. `null` for a minority of hotels."
          },
          "region": {
            "$ref": "#/components/schemas/HotelRegion"
          },
          "star_rating": {
            "type": "integer",
            "description": "Number of stars, 0 to 5. `0` means the property is not rated.",
            "minimum": 0,
            "maximum": 5,
            "example": 0
          },
          "images": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "URL template of a photo."
                },
                "description": "URL templates of the hotel photos, best photo first. Replace `{size}` to get a picture: `120x120`, `240x240`, `640x400` and `1024x768` work, and so does `x500` (500 px high); `orig` does not. See \"Images\" in Mini and full hotel data."
              },
              {
                "type": "null"
              }
            ],
            "description": "URL templates of the hotel photos, best photo first. Replace `{size}` to get a picture: `120x120`, `240x240`, `640x400` and `1024x768` work, and so does `x500` (500 px high); `orig` does not. See \"Images\" in Mini and full hotel data."
          },
          "serp_filters": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A filter tag."
            },
            "description": "Tags to build result filters with: `has_internet`, `has_parking`, `has_pool`, `has_spa`, `has_fitness`, `has_pets`, `has_kids`, `has_meal`, `has_jacuzzi`, `has_business`, `has_smoking`, `has_disabled_support`, `has_airport_transfer`, `air_conditioning`, `kitchen`."
          },
          "amenity_groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelAmenityGroup"
            },
            "description": "The amenities of the hotel by group. With the images it makes up about 90% of the bytes of a mini card."
          }
        },
        "required": [
          "id",
          "hid",
          "name",
          "kind",
          "address",
          "latitude",
          "longitude",
          "postal_code",
          "region",
          "star_rating",
          "images",
          "serp_filters",
          "amenity_groups"
        ],
        "examples": [
          {
            "id": "test_hotel_do_not_book",
            "hid": 8473727,
            "name": "Test Hotel (Do Not Book) test",
            "kind": "Apartment",
            "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
            "latitude": 14.079872,
            "longitude": -87.21677,
            "postal_code": null,
            "region": {
              "id": 3421,
              "country_code": "HN",
              "iata": "TGU",
              "name": "Tegucigalpa",
              "type": "City",
              "type_v2": "city"
            },
            "star_rating": 0,
            "images": [
              "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
              "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG"
            ],
            "serp_filters": [
              "has_internet",
              "has_parking",
              "has_spa",
              "has_pets",
              "has_jacuzzi",
              "kitchen"
            ],
            "amenity_groups": [
              {
                "amenities": [
                  "24-hour reception",
                  "Early check-in",
                  "Late check-out"
                ],
                "non_free_amenities": [],
                "group_name": "General"
              },
              {
                "amenities": [
                  "Concierge services",
                  "Dry-cleaning",
                  "Shoe shine"
                ],
                "non_free_amenities": [
                  "Dry-cleaning",
                  "Shoe shine"
                ],
                "group_name": "Services and amenities"
              }
            ]
          }
        ]
      },
      "HotelStarCertificate": {
        "type": "object",
        "description": "The official star certificate of a hotel, when it has one.",
        "properties": {
          "certificate_id": {
            "type": "string",
            "description": "Id of the star certificate; an empty string when the supplier has none."
          },
          "valid_to": {
            "type": "string",
            "description": "End of validity of the certificate; an empty string when unknown."
          }
        },
        "required": [
          "certificate_id",
          "valid_to"
        ]
      },
      "HotelDescriptionBlock": {
        "type": "object",
        "description": "One titled block of the description of a hotel.",
        "properties": {
          "title": {
            "type": "string",
            "description": "Title of the block: `Location`, `At the hotel`, `At the apartment`, `Room amenities` ...",
            "example": "Location"
          },
          "paragraphs": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A paragraph of text."
            },
            "description": "The paragraphs of the block, in English."
          }
        },
        "required": [
          "title",
          "paragraphs"
        ]
      },
      "HotelRoomGroupNameStruct": {
        "type": "object",
        "description": "The room name taken apart.",
        "properties": {
          "main_name": {
            "type": "string",
            "description": "The room name without the bedding.",
            "example": "Standard Double room"
          },
          "bedding_type": {
            "anyOf": [
              {
                "type": "string",
                "description": "The bedding of the room, when the name says it.",
                "example": "full double bed"
              },
              {
                "type": "null"
              }
            ],
            "description": "The bedding of the room, when the name says it."
          },
          "bathroom": {
            "anyOf": [
              {
                "type": "string",
                "description": "The bathroom, when the name says it.",
                "example": "shared bathroom"
              },
              {
                "type": "null"
              }
            ],
            "description": "The bathroom, when the name says it."
          }
        },
        "required": [
          "main_name"
        ]
      },
      "HotelRoomGroupCodes": {
        "type": "object",
        "description": "Numeric codes of the properties of a room group. They are the supplier's own; the camelCase `get_hotel_info` document decodes them into words (`roomClass`, `quality`, `beddingType` ...).",
        "properties": {
          "class": {
            "type": "integer",
            "description": "Room class code."
          },
          "quality": {
            "type": "integer",
            "description": "Room quality code."
          },
          "sex": {
            "type": "integer",
            "description": "Gender restriction code; `0` for none."
          },
          "bathroom": {
            "type": "integer",
            "description": "Bathroom code."
          },
          "bedding": {
            "type": "integer",
            "description": "Bedding code."
          },
          "family": {
            "type": "integer",
            "description": "`1` for a family room, `0` otherwise."
          },
          "capacity": {
            "type": "integer",
            "description": "Capacity code; `0` when unknown."
          },
          "club": {
            "type": "integer",
            "description": "`1` for a club room, `0` otherwise."
          },
          "bedrooms": {
            "type": "integer",
            "description": "Number of bedrooms; `0` when unknown."
          },
          "balcony": {
            "type": "integer",
            "description": "`1` when the room has a balcony, `0` otherwise."
          },
          "floor": {
            "type": "integer",
            "description": "Floor code; `0` when unknown."
          },
          "view": {
            "type": "integer",
            "description": "View code; `0` when unknown."
          }
        },
        "required": [
          "class",
          "quality",
          "sex",
          "bathroom",
          "bedding",
          "family",
          "capacity",
          "club",
          "bedrooms",
          "balcony",
          "floor",
          "view"
        ]
      },
      "HotelRoomGroup": {
        "type": "object",
        "description": "One room type of a hotel with its photos and amenities.",
        "properties": {
          "room_group_id": {
            "type": "integer",
            "description": "Id of the room group. A rate of `get_rates` belongs to one room group by name."
          },
          "name": {
            "type": "string",
            "description": "Full name of the room type.",
            "example": "Standard Double room full double bed"
          },
          "name_struct": {
            "$ref": "#/components/schemas/HotelRoomGroupNameStruct"
          },
          "rg_ext": {
            "$ref": "#/components/schemas/HotelRoomGroupCodes"
          },
          "room_amenities": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "An amenity of the room."
            },
            "description": "Amenities of the room, as tags (`bath`, `heating`, `shared-bathroom` ...)."
          },
          "images": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "URL template of a photo of the room."
                },
                "description": "Photos of the room, as URL templates with `{size}`."
              },
              {
                "type": "null"
              }
            ],
            "description": "Photos of the room, as URL templates with `{size}`."
          },
          "size": {
            "anyOf": [
              {
                "type": "number",
                "description": "Size of the room in square meters, when the supplier knows it."
              },
              {
                "type": "null"
              }
            ],
            "description": "Size of the room in square meters, when the supplier knows it."
          }
        },
        "required": [
          "room_group_id",
          "name",
          "name_struct",
          "rg_ext",
          "room_amenities"
        ]
      },
      "HotelPolicyEntry": {
        "type": "object",
        "description": "One entry of a policy of the hotel (a price for breakfast, an extra bed, parking, a deposit ...). Which keys an entry has depends on the policy; all prices are paid at the property, in the currency stated.",
        "properties": {
          "inclusion": {
            "type": "string",
            "enum": [
              "included",
              "not_included"
            ],
            "description": "Whether the service is included in the room price."
          },
          "price": {
            "type": "string",
            "description": "The amount as a decimal string, in `currency`.",
            "example": "100.00"
          },
          "currency": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 4217 currency of `price`; `null` when the service is free or the price is a percentage."
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 4217 currency of `price`; `null` when the service is free or the price is a percentage."
          },
          "price_unit": {
            "type": "string",
            "description": "What the price is for: `per_room_per_night`, `per_guest_per_stay`, `per_car_per_night`, `per_hour`, `unspecified` ..."
          },
          "meal_type": {
            "type": "string",
            "description": "Meal of the entry: `breakfast`, `lunch`, `dinner` ..."
          },
          "internet_type": {
            "type": "string",
            "description": "Kind of internet access: `wifi`, `wired`, `unspecified` ..."
          },
          "work_area": {
            "type": "string",
            "description": "Where the internet works: `hotel`, `room`, `public_area` ..."
          },
          "age_start": {
            "type": "integer",
            "description": "First age of a child the entry applies to."
          },
          "age_end": {
            "type": "integer",
            "description": "Last age of a child the entry applies to."
          },
          "amount": {
            "type": "integer",
            "description": "How many of the item the entry is about (extra beds, cots)."
          },
          "pets_type": {
            "type": "string",
            "description": "Kind of pet the entry applies to: `dog`, `cat`, `unspecified` ..."
          },
          "shuttle_type": {
            "type": "string",
            "description": "Kind of shuttle: `one_way`, `round_trip`."
          },
          "destination_type": {
            "type": "string",
            "description": "Where the shuttle goes: `airport`, `train`, `bus` ..."
          },
          "territory_type": {
            "type": "string",
            "description": "Where the car park is: `inside`, `outside`, `unspecified`."
          },
          "extra_bed": {
            "type": "string",
            "description": "For a child entry: whether an extra bed is `available`, `not_available` or `unspecified`."
          },
          "availability": {
            "type": "string",
            "description": "Whether the service is `available`, `not_available` or `unspecified`."
          },
          "pricing_method": {
            "type": "string",
            "description": "For a deposit: `fixed` or `percent`."
          },
          "payment_type": {
            "type": "string",
            "description": "For a deposit: how it is paid (`cash`, `card`, `unspecified`)."
          },
          "deposit_type": {
            "type": "string",
            "description": "For a deposit: what it covers (`breakage`, `unspecified` ...)."
          },
          "check_in_check_out_type": {
            "type": "string",
            "description": "For an early or late stay: `early_checkin`, `late_checkout` ..."
          }
        },
        "additionalProperties": true
      },
      "HotelMetapolicy": {
        "type": "object",
        "description": "The policies of the hotel: internet, meals, extra beds, children, pets, parking, deposits ... The keys are the same in `get_hotel_info` in camelCase (`childrenMeal`, `extraBed` ...); the entries inside keep their snake_case keys.",
        "properties": {
          "internet": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Internet access and its price."
          },
          "meal": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Meals that can be added to a rate, with prices."
          },
          "children_meal": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Meals for children, by age range."
          },
          "extra_bed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Extra beds and their price."
          },
          "cot": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Cots and their price."
          },
          "pets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Whether pets are allowed and what they cost."
          },
          "shuttle": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Shuttle services and their price."
          },
          "parking": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Parking and its price."
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "What a child of each age range costs and whether an extra bed is available. Read it before you assume a child stays free."
          },
          "visa": {
            "type": "object",
            "description": "Visa support of the hotel.",
            "properties": {
              "visa_support": {
                "type": "string",
                "description": "`support_enable` when the hotel supports visa invitations, `unspecified` otherwise."
              }
            },
            "required": [
              "visa_support"
            ]
          },
          "deposit": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Deposits the hotel asks for at arrival."
          },
          "no_show": {
            "type": "object",
            "description": "The no-show policy.",
            "properties": {
              "availability": {
                "type": "string",
                "description": "Whether a no-show policy exists: `available` or `unspecified`."
              },
              "time": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Time of day after which a guest counts as a no-show."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Time of day after which a guest counts as a no-show."
              },
              "day_period": {
                "type": "string",
                "description": "`after_midday`, `before_midday` or `unspecified`."
              }
            },
            "required": [
              "availability",
              "day_period"
            ]
          },
          "add_fee": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Other fees payable at the property."
          },
          "check_in_check_out": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Early check-in and late check-out and their price."
          }
        }
      },
      "HotelFacts": {
        "type": "object",
        "description": "Facts about the building.",
        "properties": {
          "floors_number": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Number of floors."
              },
              {
                "type": "null"
              }
            ],
            "description": "Number of floors."
          },
          "rooms_number": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Number of rooms."
              },
              {
                "type": "null"
              }
            ],
            "description": "Number of rooms."
          },
          "year_built": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Year the building was built."
              },
              {
                "type": "null"
              }
            ],
            "description": "Year the building was built."
          },
          "year_renovated": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Year of the last renovation."
              },
              {
                "type": "null"
              }
            ],
            "description": "Year of the last renovation."
          },
          "electricity": {
            "type": "object",
            "description": "The electricity of the hotel.",
            "properties": {
              "frequency": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "description": "A mains frequency in hertz."
                },
                "description": "Mains frequencies, for example `[60]`."
              },
              "voltage": {
                "anyOf": [
                  {
                    "type": "array",
                    "items": {
                      "type": "number",
                      "description": "A mains voltage."
                    },
                    "description": "Mains voltages, when known."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Mains voltages, when known."
              },
              "sockets": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "A socket type letter."
                },
                "description": "Socket types (`a`, `b`, `c` ...)."
              }
            },
            "required": [
              "frequency",
              "sockets"
            ]
          },
          "register": {
            "anyOf": [
              {
                "description": "Registration data of the property, for the countries that require it.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Registration data of the property, for the countries that require it."
          }
        },
        "required": [
          "floors_number",
          "rooms_number",
          "year_built",
          "year_renovated",
          "electricity"
        ]
      },
      "HotelKeysPickup": {
        "type": "object",
        "description": "How the guest gets the keys (mostly for apartments).",
        "properties": {
          "type": {
            "type": "string",
            "description": "How guests collect the keys: `unspecified`, `lockbox`, `reception`, `apartment_office` ...",
            "example": "lockbox"
          },
          "is_contactless": {
            "type": "boolean",
            "description": "`true` when the keys are collected without meeting anybody."
          },
          "phone": {
            "anyOf": [
              {
                "type": "string",
                "description": "Phone number to call for the keys."
              },
              {
                "type": "null"
              }
            ],
            "description": "Phone number to call for the keys."
          },
          "email": {
            "anyOf": [
              {
                "type": "string",
                "description": "E-mail address to write to for the keys."
              },
              {
                "type": "null"
              }
            ],
            "description": "E-mail address to write to for the keys."
          },
          "apartment_office_address": {
            "anyOf": [
              {
                "type": "string",
                "description": "Address of the office that hands over the keys."
              },
              {
                "type": "null"
              }
            ],
            "description": "Address of the office that hands over the keys."
          },
          "apartment_extra_information": {
            "anyOf": [
              {
                "type": "string",
                "description": "Free text about the key pick-up."
              },
              {
                "type": "null"
              }
            ],
            "description": "Free text about the key pick-up."
          }
        },
        "required": [
          "type",
          "is_contactless"
        ]
      },
      "HotelFull": {
        "type": "object",
        "description": "Everything the API knows about one hotel, for the hotel page before checkout: the mini fields plus the description, every room type, the policies, the facts and the contact details. It is 5 to 10 times larger than the mini card (34 KB on average, up to 130 KB). It is what `POST /hotels/search` returns by default; `get_hotel_info` returns the same data in camelCase (`HotelInfo`).",
        "properties": {
          "id": {
            "type": "string",
            "description": "The FX-Port id of the hotel. Send it as `hotel_id`.",
            "example": "test_hotel_do_not_book"
          },
          "hid": {
            "type": "integer",
            "description": "The supplier's numeric hotel id.",
            "example": 8473727
          },
          "name": {
            "type": "string",
            "description": "Name of the hotel."
          },
          "kind": {
            "type": "string",
            "description": "Type of property: `Hotel`, `Apartment`, `Resort` ..."
          },
          "hotel_chain": {
            "type": "string",
            "description": "The hotel chain, or `No chain`."
          },
          "address": {
            "type": "string",
            "description": "Street address, in English."
          },
          "postal_code": {
            "anyOf": [
              {
                "type": "string",
                "description": "Postal code; `null` for a minority of hotels."
              },
              {
                "type": "null"
              }
            ],
            "description": "Postal code; `null` for a minority of hotels."
          },
          "latitude": {
            "type": "number",
            "description": "Latitude of the hotel."
          },
          "longitude": {
            "type": "number",
            "description": "Longitude of the hotel."
          },
          "region": {
            "$ref": "#/components/schemas/HotelRegion"
          },
          "star_rating": {
            "type": "integer",
            "description": "Number of stars, 0 to 5. `0` means not rated.",
            "minimum": 0,
            "maximum": 5
          },
          "star_certificate": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/HotelStarCertificate"
              },
              {
                "type": "null"
              }
            ]
          },
          "email": {
            "anyOf": [
              {
                "type": "string",
                "description": "Contact e-mail of the hotel."
              },
              {
                "type": "null"
              }
            ],
            "description": "Contact e-mail of the hotel."
          },
          "phone": {
            "type": "string",
            "description": "Contact phone number of the hotel."
          },
          "check_in_time": {
            "type": "string",
            "description": "Time of day from which guests can check in, `HH:MM:SS`, local time. A time, not a date.",
            "example": "14:00:00"
          },
          "check_out_time": {
            "type": "string",
            "description": "Time of day by which guests must check out, `HH:MM:SS`, local time.",
            "example": "12:00:00"
          },
          "check_in_time_end": {
            "type": "string",
            "description": "Time of day until which guests can check in. Present only when the hotel states it."
          },
          "front_desk_time_start": {
            "anyOf": [
              {
                "type": "string",
                "description": "Time of day the front desk opens, `HH:MM:SS`; `null` when unknown."
              },
              {
                "type": "null"
              }
            ],
            "description": "Time of day the front desk opens, `HH:MM:SS`; `null` when unknown."
          },
          "front_desk_time_end": {
            "anyOf": [
              {
                "type": "string",
                "description": "Time of day the front desk closes, `HH:MM:SS`; `null` when unknown."
              },
              {
                "type": "null"
              }
            ],
            "description": "Time of day the front desk closes, `HH:MM:SS`; `null` when unknown."
          },
          "images": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "URL template of a photo."
                },
                "description": "URL templates of the hotel photos, best photo first. Replace `{size}` to get a picture: `120x120`, `240x240`, `640x400` and `1024x768` work, and so does `x500` (500 px high); `orig` does not. See \"Images\" in Mini and full hotel data."
              },
              {
                "type": "null"
              }
            ],
            "description": "URL templates of the hotel photos, best photo first. Replace `{size}` to get a picture: `120x120`, `240x240`, `640x400` and `1024x768` work, and so does `x500` (500 px high); `orig` does not. See \"Images\" in Mini and full hotel data."
          },
          "amenity_groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelAmenityGroup"
            },
            "description": "The amenities of the hotel by group."
          },
          "serp_filters": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A filter tag."
            },
            "description": "Tags to build result filters with (see `HotelMini`)."
          },
          "description_struct": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelDescriptionBlock"
            },
            "description": "The description of the hotel, in titled blocks."
          },
          "metapolicy_struct": {
            "$ref": "#/components/schemas/HotelMetapolicy"
          },
          "metapolicy_extra_info": {
            "anyOf": [
              {
                "type": "string",
                "description": "Free text with the house rules of the hotel."
              },
              {
                "type": "null"
              }
            ],
            "description": "Free text with the house rules of the hotel."
          },
          "room_groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelRoomGroup"
            },
            "description": "Every room type of the hotel, with photos and amenities. About 80% of the bytes of a full document."
          },
          "facts": {
            "$ref": "#/components/schemas/HotelFacts"
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A payment method accepted at the property: `visa`, `master_card`, `cash` ..."
            },
            "description": "Payment methods accepted at the property."
          },
          "keys_pickup": {
            "$ref": "#/components/schemas/HotelKeysPickup"
          },
          "is_closed": {
            "type": "boolean",
            "description": "`true` when the property is closed on the supplier side: availability calls return no rates."
          },
          "is_gender_specification_required": {
            "type": "boolean",
            "description": "`true` when the hotel needs the gender of each guest."
          },
          "deleted": {
            "type": "boolean",
            "description": "`true` when the supplier removed the hotel from its catalogue."
          },
          "distance_center": {
            "anyOf": [
              {
                "type": "number",
                "description": "Distance to the city center, when the supplier gives it."
              },
              {
                "type": "null"
              }
            ],
            "description": "Distance to the city center, when the supplier gives it."
          },
          "giata_code": {
            "type": "string",
            "description": "GIATA code of the hotel. Present only for hotels that have one."
          }
        },
        "required": [
          "id",
          "hid",
          "name",
          "kind",
          "hotel_chain",
          "address",
          "postal_code",
          "latitude",
          "longitude",
          "region",
          "star_rating",
          "star_certificate",
          "email",
          "phone",
          "check_in_time",
          "check_out_time",
          "front_desk_time_start",
          "front_desk_time_end",
          "images",
          "amenity_groups",
          "serp_filters",
          "description_struct",
          "metapolicy_struct",
          "metapolicy_extra_info",
          "room_groups",
          "facts",
          "payment_methods",
          "keys_pickup",
          "is_closed",
          "is_gender_specification_required",
          "deleted"
        ],
        "examples": [
          {
            "id": "test_hotel_do_not_book",
            "hid": 8473727,
            "name": "Test Hotel (Do Not Book) test",
            "kind": "Apartment",
            "hotel_chain": "No chain",
            "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
            "postal_code": null,
            "latitude": 14.079872,
            "longitude": -87.21677,
            "region": {
              "id": 3421,
              "country_code": "HN",
              "iata": "TGU",
              "name": "Tegucigalpa",
              "type": "City",
              "type_v2": "city"
            },
            "star_rating": 0,
            "star_certificate": null,
            "email": "test@example.com",
            "phone": "+15555550123",
            "check_in_time": "14:00:00",
            "check_out_time": "12:00:00",
            "front_desk_time_start": "00:00:00",
            "front_desk_time_end": "00:00:00",
            "images": [
              "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
            ],
            "amenity_groups": [
              {
                "amenities": [
                  "24-hour reception",
                  "Early check-in",
                  "Late check-out"
                ],
                "non_free_amenities": [],
                "group_name": "General"
              }
            ],
            "serp_filters": [
              "has_internet",
              "has_parking",
              "kitchen"
            ],
            "description_struct": [
              {
                "title": "Location",
                "paragraphs": [
                  "A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
                ]
              }
            ],
            "metapolicy_struct": {
              "internet": [
                {
                  "inclusion": "included",
                  "price": "0.00",
                  "currency": null,
                  "price_unit": "unspecified",
                  "internet_type": "unspecified",
                  "work_area": "hotel"
                }
              ],
              "meal": [
                {
                  "inclusion": "not_included",
                  "price": "300.00",
                  "currency": "USD",
                  "meal_type": "breakfast"
                }
              ],
              "children_meal": [
                {
                  "inclusion": "not_included",
                  "price": "200.00",
                  "currency": "USD",
                  "meal_type": "breakfast",
                  "age_start": 0,
                  "age_end": 5
                }
              ],
              "extra_bed": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "ALL",
                  "price_unit": "per_guest_per_night",
                  "amount": 1
                }
              ],
              "cot": [
                {
                  "inclusion": "not_included",
                  "price": "0.00",
                  "currency": "USD",
                  "price_unit": "per_room_per_night",
                  "amount": 1
                }
              ],
              "pets": [
                {
                  "inclusion": "not_included",
                  "price": "560.00",
                  "currency": "USD",
                  "price_unit": "per_room_per_stay",
                  "pets_type": "unspecified"
                }
              ],
              "shuttle": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "EUR",
                  "shuttle_type": "one_way",
                  "destination_type": "train"
                }
              ],
              "parking": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "USD",
                  "price_unit": "per_car_per_night",
                  "territory_type": "unspecified"
                }
              ],
              "children": [
                {
                  "age_start": 0,
                  "age_end": 3,
                  "price": "500.00",
                  "currency": "CHF",
                  "extra_bed": "available"
                }
              ],
              "visa": {
                "visa_support": "support_enable"
              },
              "deposit": [
                {
                  "availability": "available",
                  "price": "123.00",
                  "currency": "HNL",
                  "price_unit": "per_room_per_stay",
                  "pricing_method": "fixed",
                  "payment_type": "cash",
                  "deposit_type": "unspecified"
                }
              ],
              "no_show": {
                "availability": "available",
                "time": "09:00:00",
                "day_period": "after_midday"
              },
              "add_fee": [],
              "check_in_check_out": []
            },
            "metapolicy_extra_info": "Front desk is open 24/7.",
            "room_groups": [
              {
                "room_group_id": 95,
                "name": "Standard Double room full double bed",
                "name_struct": {
                  "main_name": "Standard Double room",
                  "bedding_type": "full double bed",
                  "bathroom": "shared bathroom"
                },
                "rg_ext": {
                  "class": 3,
                  "quality": 2,
                  "sex": 0,
                  "bathroom": 1,
                  "bedding": 3,
                  "family": 0,
                  "capacity": 2,
                  "club": 0,
                  "bedrooms": 0,
                  "balcony": 0,
                  "floor": 0,
                  "view": 0
                },
                "room_amenities": [
                  "bath",
                  "heating"
                ],
                "images": [
                  "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                ]
              }
            ],
            "facts": {
              "floors_number": null,
              "rooms_number": null,
              "year_built": null,
              "year_renovated": null,
              "electricity": {
                "frequency": [
                  60
                ],
                "voltage": null,
                "sockets": [
                  "a",
                  "b"
                ]
              },
              "register": null
            },
            "payment_methods": [
              "visa",
              "maestro",
              "cash"
            ],
            "keys_pickup": {
              "type": "lockbox",
              "phone": "+15555550123",
              "email": "test@example.com",
              "apartment_office_address": null,
              "apartment_extra_information": "The keys are in the box by the front door.",
              "is_contactless": true
            },
            "is_closed": false,
            "is_gender_specification_required": false,
            "deleted": false
          }
        ]
      },
      "HotelRawDocument": {
        "type": "object",
        "description": "The cached document of a hotel as `POST /hotels/details` returns it: everything of `HotelFull` plus `images_ext` and `policy_struct`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The FX-Port id of the hotel. Send it as `hotel_id`.",
            "example": "test_hotel_do_not_book"
          },
          "hid": {
            "type": "integer",
            "description": "The supplier's numeric hotel id.",
            "example": 8473727
          },
          "name": {
            "type": "string",
            "description": "Name of the hotel."
          },
          "kind": {
            "type": "string",
            "description": "Type of property: `Hotel`, `Apartment`, `Resort` ..."
          },
          "hotel_chain": {
            "type": "string",
            "description": "The hotel chain, or `No chain`."
          },
          "address": {
            "type": "string",
            "description": "Street address, in English."
          },
          "postal_code": {
            "anyOf": [
              {
                "type": "string",
                "description": "Postal code; `null` for a minority of hotels."
              },
              {
                "type": "null"
              }
            ],
            "description": "Postal code; `null` for a minority of hotels."
          },
          "latitude": {
            "type": "number",
            "description": "Latitude of the hotel."
          },
          "longitude": {
            "type": "number",
            "description": "Longitude of the hotel."
          },
          "region": {
            "$ref": "#/components/schemas/HotelRegion"
          },
          "star_rating": {
            "type": "integer",
            "description": "Number of stars, 0 to 5. `0` means not rated.",
            "minimum": 0,
            "maximum": 5
          },
          "star_certificate": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/HotelStarCertificate"
              },
              {
                "type": "null"
              }
            ]
          },
          "email": {
            "anyOf": [
              {
                "type": "string",
                "description": "Contact e-mail of the hotel."
              },
              {
                "type": "null"
              }
            ],
            "description": "Contact e-mail of the hotel."
          },
          "phone": {
            "type": "string",
            "description": "Contact phone number of the hotel."
          },
          "check_in_time": {
            "type": "string",
            "description": "Time of day from which guests can check in, `HH:MM:SS`, local time. A time, not a date.",
            "example": "14:00:00"
          },
          "check_out_time": {
            "type": "string",
            "description": "Time of day by which guests must check out, `HH:MM:SS`, local time.",
            "example": "12:00:00"
          },
          "check_in_time_end": {
            "type": "string",
            "description": "Time of day until which guests can check in. Present only when the hotel states it."
          },
          "front_desk_time_start": {
            "anyOf": [
              {
                "type": "string",
                "description": "Time of day the front desk opens, `HH:MM:SS`; `null` when unknown."
              },
              {
                "type": "null"
              }
            ],
            "description": "Time of day the front desk opens, `HH:MM:SS`; `null` when unknown."
          },
          "front_desk_time_end": {
            "anyOf": [
              {
                "type": "string",
                "description": "Time of day the front desk closes, `HH:MM:SS`; `null` when unknown."
              },
              {
                "type": "null"
              }
            ],
            "description": "Time of day the front desk closes, `HH:MM:SS`; `null` when unknown."
          },
          "images": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "URL template of a photo."
                },
                "description": "URL templates of the hotel photos, best photo first. Replace `{size}` to get a picture: `120x120`, `240x240`, `640x400` and `1024x768` work, and so does `x500` (500 px high); `orig` does not. See \"Images\" in Mini and full hotel data."
              },
              {
                "type": "null"
              }
            ],
            "description": "URL templates of the hotel photos, best photo first. Replace `{size}` to get a picture: `120x120`, `240x240`, `640x400` and `1024x768` work, and so does `x500` (500 px high); `orig` does not. See \"Images\" in Mini and full hotel data."
          },
          "amenity_groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelAmenityGroup"
            },
            "description": "The amenities of the hotel by group."
          },
          "serp_filters": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A filter tag."
            },
            "description": "Tags to build result filters with (see `HotelMini`)."
          },
          "description_struct": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelDescriptionBlock"
            },
            "description": "The description of the hotel, in titled blocks."
          },
          "metapolicy_struct": {
            "$ref": "#/components/schemas/HotelMetapolicy"
          },
          "metapolicy_extra_info": {
            "anyOf": [
              {
                "type": "string",
                "description": "Free text with the house rules of the hotel."
              },
              {
                "type": "null"
              }
            ],
            "description": "Free text with the house rules of the hotel."
          },
          "room_groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelRoomGroup"
            },
            "description": "Every room type of the hotel, with photos and amenities. About 80% of the bytes of a full document."
          },
          "facts": {
            "$ref": "#/components/schemas/HotelFacts"
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A payment method accepted at the property: `visa`, `master_card`, `cash` ..."
            },
            "description": "Payment methods accepted at the property."
          },
          "keys_pickup": {
            "$ref": "#/components/schemas/HotelKeysPickup"
          },
          "is_closed": {
            "type": "boolean",
            "description": "`true` when the property is closed on the supplier side: availability calls return no rates."
          },
          "is_gender_specification_required": {
            "type": "boolean",
            "description": "`true` when the hotel needs the gender of each guest."
          },
          "deleted": {
            "type": "boolean",
            "description": "`true` when the supplier removed the hotel from its catalogue."
          },
          "distance_center": {
            "anyOf": [
              {
                "type": "number",
                "description": "Distance to the city center, when the supplier gives it."
              },
              {
                "type": "null"
              }
            ],
            "description": "Distance to the city center, when the supplier gives it."
          },
          "giata_code": {
            "type": "string",
            "description": "GIATA code of the hotel. Present only for hotels that have one."
          },
          "images_ext": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "A photo with its category.",
              "properties": {
                "url": {
                  "type": "string",
                  "description": "URL template of the photo, with `{size}`."
                },
                "category_slug": {
                  "type": "string",
                  "description": "What the photo shows: `outside`, `lobby`, `rooms`, `others` ..."
                }
              },
              "required": [
                "url",
                "category_slug"
              ]
            },
            "description": "The photos of the hotel with their category, best photo first. `images` holds the same URLs without the category."
          },
          "policy_struct": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelDescriptionBlock"
            },
            "description": "The house rules of the hotel as readable text: meals, children and extra beds, deposits, visas ..."
          }
        },
        "required": [
          "id",
          "hid",
          "name",
          "kind",
          "hotel_chain",
          "address",
          "postal_code",
          "latitude",
          "longitude",
          "region",
          "star_rating",
          "star_certificate",
          "email",
          "phone",
          "check_in_time",
          "check_out_time",
          "front_desk_time_start",
          "front_desk_time_end",
          "images",
          "amenity_groups",
          "serp_filters",
          "description_struct",
          "metapolicy_struct",
          "metapolicy_extra_info",
          "room_groups",
          "facts",
          "payment_methods",
          "keys_pickup",
          "is_closed",
          "is_gender_specification_required",
          "deleted",
          "images_ext",
          "policy_struct"
        ],
        "examples": [
          {
            "id": "test_hotel_do_not_book",
            "hid": 8473727,
            "name": "Test Hotel (Do Not Book) test",
            "kind": "Apartment",
            "hotel_chain": "No chain",
            "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
            "postal_code": null,
            "latitude": 14.079872,
            "longitude": -87.21677,
            "region": {
              "id": 3421,
              "country_code": "HN",
              "iata": "TGU",
              "name": "Tegucigalpa",
              "type": "City",
              "type_v2": "city"
            },
            "star_rating": 0,
            "star_certificate": null,
            "email": "test@example.com",
            "phone": "+15555550123",
            "check_in_time": "14:00:00",
            "check_out_time": "12:00:00",
            "front_desk_time_start": "00:00:00",
            "front_desk_time_end": "00:00:00",
            "images": [
              "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
            ],
            "amenity_groups": [
              {
                "amenities": [
                  "24-hour reception",
                  "Early check-in",
                  "Late check-out"
                ],
                "non_free_amenities": [],
                "group_name": "General"
              }
            ],
            "serp_filters": [
              "has_internet",
              "has_parking",
              "kitchen"
            ],
            "description_struct": [
              {
                "title": "Location",
                "paragraphs": [
                  "A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
                ]
              }
            ],
            "metapolicy_struct": {
              "internet": [
                {
                  "inclusion": "included",
                  "price": "0.00",
                  "currency": null,
                  "price_unit": "unspecified",
                  "internet_type": "unspecified",
                  "work_area": "hotel"
                }
              ],
              "meal": [
                {
                  "inclusion": "not_included",
                  "price": "300.00",
                  "currency": "USD",
                  "meal_type": "breakfast"
                }
              ],
              "children_meal": [
                {
                  "inclusion": "not_included",
                  "price": "200.00",
                  "currency": "USD",
                  "meal_type": "breakfast",
                  "age_start": 0,
                  "age_end": 5
                }
              ],
              "extra_bed": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "ALL",
                  "price_unit": "per_guest_per_night",
                  "amount": 1
                }
              ],
              "cot": [
                {
                  "inclusion": "not_included",
                  "price": "0.00",
                  "currency": "USD",
                  "price_unit": "per_room_per_night",
                  "amount": 1
                }
              ],
              "pets": [
                {
                  "inclusion": "not_included",
                  "price": "560.00",
                  "currency": "USD",
                  "price_unit": "per_room_per_stay",
                  "pets_type": "unspecified"
                }
              ],
              "shuttle": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "EUR",
                  "shuttle_type": "one_way",
                  "destination_type": "train"
                }
              ],
              "parking": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "USD",
                  "price_unit": "per_car_per_night",
                  "territory_type": "unspecified"
                }
              ],
              "children": [
                {
                  "age_start": 0,
                  "age_end": 3,
                  "price": "500.00",
                  "currency": "CHF",
                  "extra_bed": "available"
                }
              ],
              "visa": {
                "visa_support": "support_enable"
              },
              "deposit": [
                {
                  "availability": "available",
                  "price": "123.00",
                  "currency": "HNL",
                  "price_unit": "per_room_per_stay",
                  "pricing_method": "fixed",
                  "payment_type": "cash",
                  "deposit_type": "unspecified"
                }
              ],
              "no_show": {
                "availability": "available",
                "time": "09:00:00",
                "day_period": "after_midday"
              },
              "add_fee": [],
              "check_in_check_out": []
            },
            "metapolicy_extra_info": "Front desk is open 24/7.",
            "room_groups": [
              {
                "room_group_id": 95,
                "name": "Standard Double room full double bed",
                "name_struct": {
                  "main_name": "Standard Double room",
                  "bedding_type": "full double bed",
                  "bathroom": "shared bathroom"
                },
                "rg_ext": {
                  "class": 3,
                  "quality": 2,
                  "sex": 0,
                  "bathroom": 1,
                  "bedding": 3,
                  "family": 0,
                  "capacity": 2,
                  "club": 0,
                  "bedrooms": 0,
                  "balcony": 0,
                  "floor": 0,
                  "view": 0
                },
                "room_amenities": [
                  "bath",
                  "heating"
                ],
                "images": [
                  "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                ]
              }
            ],
            "facts": {
              "floors_number": null,
              "rooms_number": null,
              "year_built": null,
              "year_renovated": null,
              "electricity": {
                "frequency": [
                  60
                ],
                "voltage": null,
                "sockets": [
                  "a",
                  "b"
                ]
              },
              "register": null
            },
            "payment_methods": [
              "visa",
              "maestro",
              "cash"
            ],
            "keys_pickup": {
              "type": "lockbox",
              "phone": "+15555550123",
              "email": "test@example.com",
              "apartment_office_address": null,
              "apartment_extra_information": "The keys are in the box by the front door.",
              "is_contactless": true
            },
            "is_closed": false,
            "is_gender_specification_required": false,
            "deleted": false,
            "images_ext": [
              {
                "url": "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg",
                "category_slug": "others"
              },
              {
                "url": "https://cdn.worldota.net/t/{size}/extranet/15/44/15440558c952ec4c3488730d1563dc9e8d25832c.JPEG",
                "category_slug": "outside"
              }
            ],
            "policy_struct": [
              {
                "title": "Meals",
                "paragraphs": [
                  "Price of an additional breakfast: 300.00 USD per person."
                ]
              }
            ]
          }
        ]
      },
      "HotelInfoRegion": {
        "type": "object",
        "description": "The city or area a hotel is in (camelCase form of `HotelRegion`).",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Supplier id of the region."
          },
          "name": {
            "type": "string",
            "description": "Name of the region, usually the city."
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code, upper case.",
            "deprecated": true,
            "x-replaced-by": "country_code"
          },
          "country_code": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code, upper case. The snake_case twin of `countryCode`, with the same value: use this name."
          },
          "iata": {
            "type": "string",
            "description": "IATA code of the main airport or city of the region."
          },
          "type": {
            "type": "string",
            "description": "Kind of region as the supplier names it: `City`, `Region` ..."
          }
        },
        "required": [
          "id",
          "name",
          "countryCode",
          "iata",
          "type"
        ]
      },
      "HotelInfoMetapolicy": {
        "type": "object",
        "description": "The policies of the hotel, in camelCase keys. The entries inside keep their snake_case keys (`price_unit`, `meal_type`, `age_start` ...).",
        "properties": {
          "addFee": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Other fees payable at the property."
          },
          "checkInCheckOut": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Early check-in and late check-out and their price."
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "What a child of each age range costs and whether an extra bed is available."
          },
          "childrenMeal": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Meals for children, by age range."
          },
          "cot": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Cots and their price."
          },
          "deposit": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Deposits the hotel asks for at arrival."
          },
          "extraBed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Extra beds and their price."
          },
          "internet": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Internet access and its price."
          },
          "meal": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Meals that can be added to a rate, with prices."
          },
          "noShow": {
            "type": "object",
            "description": "The no-show policy.",
            "properties": {
              "availability": {
                "type": "string",
                "description": "Whether a no-show policy exists."
              },
              "time": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Time of day after which a guest counts as a no-show."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Time of day after which a guest counts as a no-show."
              },
              "day_period": {
                "type": "string",
                "description": "`after_midday`, `before_midday` or `unspecified`."
              }
            },
            "required": [
              "availability",
              "day_period"
            ]
          },
          "parking": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Parking and its price."
          },
          "pets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Whether pets are allowed and what they cost."
          },
          "shuttle": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPolicyEntry"
            },
            "description": "Shuttle services and their price."
          },
          "visa": {
            "type": "object",
            "description": "Visa support of the hotel.",
            "properties": {
              "visa_support": {
                "type": "string",
                "description": "`support_enable` when the hotel supports visa invitations, `unspecified` otherwise."
              }
            },
            "required": [
              "visa_support"
            ]
          }
        }
      },
      "HotelInfoRoomGroup": {
        "type": "object",
        "description": "One room type of a hotel, with its properties decoded into words (the snake_case document has numeric codes instead).",
        "properties": {
          "roomGroupId": {
            "type": "integer",
            "description": "Id of the room group."
          },
          "name": {
            "type": "string",
            "description": "Full name of the room type."
          },
          "nameStruct": {
            "type": "object",
            "description": "The room name taken apart.",
            "properties": {
              "mainName": {
                "type": "string",
                "description": "The room name without the bedding."
              },
              "beddingType": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The bedding, when the name says it."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The bedding, when the name says it."
              },
              "bathroom": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The bathroom, when the name says it."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The bathroom, when the name says it."
              }
            },
            "required": [
              "mainName"
            ]
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "URL template of a photo of the room."
            },
            "description": "Photos of the room, as URL templates with `{size}`."
          },
          "roomAmenities": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "An amenity of the room."
            },
            "description": "Amenities of the room, as tags."
          },
          "roomClass": {
            "anyOf": [
              {
                "type": "string",
                "description": "Class of the room: `room`, `suite`, `junior-suite`, `apartment`, `studio`, `cottage`, `dorm` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "Class of the room: `room`, `suite`, `junior-suite`, `apartment`, `studio`, `cottage`, `dorm` ..."
          },
          "quality": {
            "anyOf": [
              {
                "type": "string",
                "description": "Quality of the room: `economy`, `standard`, `classic`, `superior`, `deluxe`, `executive`, `business` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "Quality of the room: `economy`, `standard`, `classic`, `superior`, `deluxe`, `executive`, `business` ..."
          },
          "bathroom": {
            "anyOf": [
              {
                "type": "string",
                "description": "`private-bathroom` or `shared-bathroom`."
              },
              {
                "type": "null"
              }
            ],
            "description": "`private-bathroom` or `shared-bathroom`."
          },
          "hasPrivateBathroom": {
            "type": "boolean",
            "description": "`true` when the bathroom is private."
          },
          "capacity": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Number of guests the room is made for."
              },
              {
                "type": "null"
              }
            ],
            "description": "Number of guests the room is made for."
          },
          "balcony": {
            "type": "boolean",
            "description": "`true` when the room has a balcony."
          },
          "view": {
            "anyOf": [
              {
                "type": "string",
                "description": "View of the room: `city-view`, `garden-view`, `pool-view`, `with-view` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "View of the room: `city-view`, `garden-view`, `pool-view`, `with-view` ..."
          },
          "floor": {
            "anyOf": [
              {
                "type": "string",
                "description": "Floor of the room, when it is special (`attic`)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Floor of the room, when it is special (`attic`)."
          },
          "bedrooms": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Number of bedrooms."
              },
              {
                "type": "null"
              }
            ],
            "description": "Number of bedrooms."
          },
          "beddingType": {
            "anyOf": [
              {
                "type": "string",
                "description": "Bedding: `double-bed`, `twin-beds`, `single-bed`, `multiple-beds` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "Bedding: `double-bed`, `twin-beds`, `single-bed`, `multiple-beds` ..."
          },
          "isFamily": {
            "type": "boolean",
            "description": "`true` for a family room."
          },
          "isClub": {
            "type": "boolean",
            "description": "`true` for a club room."
          },
          "genderRestriction": {
            "anyOf": [
              {
                "type": "string",
                "description": "Gender the room is reserved for (hostel dorms), when there is a restriction."
              },
              {
                "type": "null"
              }
            ],
            "description": "Gender the room is reserved for (hostel dorms), when there is a restriction."
          }
        },
        "required": [
          "roomGroupId",
          "name",
          "nameStruct",
          "images",
          "roomAmenities",
          "hasPrivateBathroom",
          "balcony",
          "isFamily",
          "isClub"
        ]
      },
      "HotelInfo": {
        "type": "object",
        "description": "The full document of one hotel as `get_hotel_info` returns it: the same data as `HotelFull`, in camelCase, with the room properties decoded into words. The keys are camelCase (`starRating`, `amenityGroups`); the entries inside `metapolicyStruct` keep snake_case keys.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The FX-Port id of the hotel. Send it as `hotel_id`.",
            "example": "test_hotel_do_not_book"
          },
          "hid": {
            "type": "integer",
            "description": "The supplier's numeric hotel id."
          },
          "name": {
            "type": "string",
            "description": "Name of the hotel."
          },
          "kind": {
            "type": "string",
            "description": "Type of property: `Hotel`, `Apartment`, `Resort` ..."
          },
          "hotelChain": {
            "type": "string",
            "description": "The hotel chain, or `No chain`.",
            "deprecated": true,
            "x-replaced-by": "hotel_chain"
          },
          "hotel_chain": {
            "type": "string",
            "description": "The hotel chain, or `No chain`. The snake_case twin of `hotelChain`, with the same value: use this name."
          },
          "isClosed": {
            "type": "boolean",
            "description": "`true` when the property is closed on the supplier side: availability calls return no rates.",
            "deprecated": true,
            "x-replaced-by": "is_closed"
          },
          "is_closed": {
            "type": "boolean",
            "description": "`true` when the property is closed on the supplier side: availability calls return no rates. The snake_case twin of `isClosed`, with the same value: use this name."
          },
          "isGenderSpecificationRequired": {
            "type": "boolean",
            "description": "`true` when the hotel needs the gender of each guest."
          },
          "address": {
            "type": "string",
            "description": "Street address, in English."
          },
          "postalCode": {
            "anyOf": [
              {
                "type": "string",
                "description": "Postal code; `null` for a minority of hotels."
              },
              {
                "type": "null"
              }
            ],
            "description": "Postal code; `null` for a minority of hotels.",
            "deprecated": true,
            "x-replaced-by": "postal_code"
          },
          "postal_code": {
            "anyOf": [
              {
                "type": "string",
                "description": "Postal code; `null` for a minority of hotels."
              },
              {
                "type": "null"
              }
            ],
            "description": "Postal code; `null` for a minority of hotels. The snake_case twin of `postalCode`, with the same value: use this name."
          },
          "latitude": {
            "type": "number",
            "description": "Latitude of the hotel."
          },
          "longitude": {
            "type": "number",
            "description": "Longitude of the hotel."
          },
          "region": {
            "$ref": "#/components/schemas/HotelInfoRegion"
          },
          "starRating": {
            "type": "integer",
            "description": "Number of stars, 0 to 5. `0` means not rated.",
            "minimum": 0,
            "maximum": 5,
            "deprecated": true,
            "x-replaced-by": "star_rating"
          },
          "star_rating": {
            "type": "integer",
            "description": "Number of stars, 0 to 5. `0` means not rated. The snake_case twin of `starRating`, with the same value: use this name.",
            "minimum": 0,
            "maximum": 5
          },
          "starCertificate": {
            "anyOf": [
              {
                "description": "The official star certificate of the hotel (`certificate_id`, `valid_to`), when it has one.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "The official star certificate of the hotel (`certificate_id`, `valid_to`), when it has one.",
            "deprecated": true,
            "x-replaced-by": "star_certificate"
          },
          "star_certificate": {
            "anyOf": [
              {
                "description": "The official star certificate of the hotel (`certificate_id`, `valid_to`), when it has one.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "The official star certificate of the hotel (`certificate_id`, `valid_to`), when it has one. The snake_case twin of `starCertificate`, with the same value: use this name."
          },
          "contact": {
            "type": "object",
            "description": "How to reach the hotel.",
            "properties": {
              "email": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Contact e-mail of the hotel."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Contact e-mail of the hotel."
              },
              "phone": {
                "type": "string",
                "description": "Contact phone number of the hotel."
              }
            },
            "required": [
              "phone"
            ]
          },
          "checkIn": {
            "type": "string",
            "description": "Time of day from which guests can check in, `HH:MM:SS`, local time. A time, not a date.",
            "example": "14:00:00",
            "deprecated": true,
            "x-replaced-by": "check_in_time"
          },
          "check_in_time": {
            "type": "string",
            "description": "Time of day from which guests can check in, `HH:MM:SS`, local time. A time, not a date. The snake_case twin of `checkIn`, with the same value: use this name.",
            "example": "14:00:00"
          },
          "checkOut": {
            "type": "string",
            "description": "Time of day by which guests must check out, `HH:MM:SS`, local time.",
            "example": "12:00:00",
            "deprecated": true,
            "x-replaced-by": "check_out_time"
          },
          "check_out_time": {
            "type": "string",
            "description": "Time of day by which guests must check out, `HH:MM:SS`, local time. The snake_case twin of `checkOut`, with the same value: use this name.",
            "example": "12:00:00"
          },
          "frontDeskHours": {
            "anyOf": [
              {
                "type": "object",
                "description": "Opening hours of the front desk; `null` when unknown.",
                "properties": {
                  "start": {
                    "type": "string",
                    "description": "Time of day the front desk opens."
                  },
                  "end": {
                    "type": "string",
                    "description": "Time of day the front desk closes."
                  }
                },
                "required": [
                  "start",
                  "end"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Opening hours of the front desk; `null` when unknown."
          },
          "images": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "URL template of a photo."
                },
                "description": "URL templates of the hotel photos, with `{size}`. See \"Images\" in Mini and full hotel data."
              },
              {
                "type": "null"
              }
            ],
            "description": "URL templates of the hotel photos, with `{size}`. See \"Images\" in Mini and full hotel data."
          },
          "amenityGroups": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "A group of amenities.",
              "properties": {
                "groupName": {
                  "type": "string",
                  "description": "Name of the group."
                },
                "amenities": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "description": "An amenity."
                  },
                  "description": "Free amenities of the group."
                },
                "paidAmenities": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "description": "A paid amenity."
                  },
                  "description": "Amenities of the group that cost extra."
                }
              },
              "required": [
                "groupName",
                "amenities",
                "paidAmenities"
              ]
            },
            "description": "The amenities of the hotel by group."
          },
          "serpFilters": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A filter tag."
            },
            "description": "Tags to build result filters with: `has_internet`, `has_parking`, `has_pool` ..."
          },
          "descriptionStruct": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One titled block of the description.",
              "properties": {
                "title": {
                  "type": "string",
                  "description": "Title of the block."
                },
                "paragraphs": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "description": "A paragraph of text."
                  },
                  "description": "The paragraphs of the block, in English."
                },
                "html": {
                  "type": "string",
                  "description": "The block as HTML."
                }
              },
              "required": [
                "title",
                "paragraphs",
                "html"
              ]
            },
            "description": "The description of the hotel, in titled blocks."
          },
          "metapolicyExtraInfo": {
            "anyOf": [
              {
                "type": "string",
                "description": "Free text with the house rules of the hotel."
              },
              {
                "type": "null"
              }
            ],
            "description": "Free text with the house rules of the hotel."
          },
          "metapolicyStruct": {
            "$ref": "#/components/schemas/HotelInfoMetapolicy"
          },
          "roomGroups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelInfoRoomGroup"
            },
            "description": "Every room type of the hotel. About 80% of the bytes of the document."
          },
          "facts": {
            "type": "object",
            "description": "Facts about the building.",
            "properties": {
              "floorsNumber": {
                "anyOf": [
                  {
                    "type": "integer",
                    "description": "Number of floors."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Number of floors."
              },
              "roomsNumber": {
                "anyOf": [
                  {
                    "type": "integer",
                    "description": "Number of rooms."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Number of rooms."
              },
              "yearBuilt": {
                "anyOf": [
                  {
                    "type": "integer",
                    "description": "Year the building was built."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Year the building was built."
              },
              "yearRenovated": {
                "anyOf": [
                  {
                    "type": "integer",
                    "description": "Year of the last renovation."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Year of the last renovation."
              },
              "electricity": {
                "type": "object",
                "description": "The electricity of the hotel.",
                "properties": {
                  "voltage": {
                    "type": "array",
                    "items": {
                      "type": "number",
                      "description": "A mains voltage."
                    },
                    "description": "Mains voltages."
                  },
                  "frequency": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "description": "A mains frequency in hertz."
                    },
                    "description": "Mains frequencies."
                  },
                  "sockets": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "A socket type letter."
                    },
                    "description": "Socket types."
                  }
                },
                "required": [
                  "voltage",
                  "frequency",
                  "sockets"
                ]
              }
            },
            "required": [
              "floorsNumber",
              "roomsNumber",
              "yearBuilt",
              "yearRenovated",
              "electricity"
            ]
          },
          "paymentMethods": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A payment method accepted at the property."
            },
            "description": "Payment methods accepted at the property: `visa`, `master_card`, `cash` ..."
          },
          "keysPickup": {
            "type": "object",
            "description": "How the guest gets the keys (mostly for apartments).",
            "properties": {
              "type": {
                "type": "string",
                "description": "How guests collect the keys: `unspecified`, `lockbox` ..."
              },
              "isContactless": {
                "type": "boolean",
                "description": "`true` when the keys are collected without meeting anybody."
              },
              "email": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "E-mail address to write to for the keys."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "E-mail address to write to for the keys."
              },
              "phone": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Phone number to call for the keys."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Phone number to call for the keys."
              },
              "officeAddress": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Address of the office that hands over the keys."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Address of the office that hands over the keys."
              },
              "extraInfo": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Free text about the key pick-up."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Free text about the key pick-up."
              }
            },
            "required": [
              "type",
              "isContactless"
            ]
          }
        },
        "required": [
          "id",
          "hid",
          "name",
          "kind",
          "hotelChain",
          "isClosed",
          "isGenderSpecificationRequired",
          "address",
          "postalCode",
          "latitude",
          "longitude",
          "region",
          "starRating",
          "starCertificate",
          "contact",
          "checkIn",
          "checkOut",
          "frontDeskHours",
          "images",
          "amenityGroups",
          "serpFilters",
          "descriptionStruct",
          "metapolicyExtraInfo",
          "metapolicyStruct",
          "roomGroups",
          "facts",
          "paymentMethods",
          "keysPickup"
        ],
        "examples": [
          {
            "id": "test_hotel_do_not_book",
            "hid": 8473727,
            "name": "Test Hotel (Do Not Book) test",
            "kind": "Apartment",
            "hotelChain": "No chain",
            "isClosed": false,
            "isGenderSpecificationRequired": false,
            "address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
            "postalCode": null,
            "latitude": 14.079872,
            "longitude": -87.21677,
            "region": {
              "id": 3421,
              "name": "Tegucigalpa",
              "countryCode": "HN",
              "iata": "TGU",
              "type": "City"
            },
            "starRating": 0,
            "starCertificate": null,
            "contact": {
              "email": "test@example.com",
              "phone": "+15555550123"
            },
            "checkIn": "14:00:00",
            "checkOut": "12:00:00",
            "frontDeskHours": {
              "start": "00:00:00",
              "end": "00:00:00"
            },
            "images": [
              "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
            ],
            "amenityGroups": [
              {
                "groupName": "General",
                "amenities": [
                  "24-hour reception",
                  "Early check-in",
                  "Late check-out"
                ],
                "paidAmenities": []
              }
            ],
            "serpFilters": [
              "has_internet",
              "has_parking",
              "kitchen"
            ],
            "descriptionStruct": [
              {
                "title": "Location",
                "paragraphs": [
                  "A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
                ],
                "html": "<h3>Location</h3><p>A perfect fit for a big group of travellers ...</p>"
              }
            ],
            "metapolicyExtraInfo": "Front desk is open 24/7.",
            "metapolicyStruct": {
              "addFee": [],
              "checkInCheckOut": [],
              "children": [
                {
                  "age_start": 0,
                  "age_end": 3,
                  "price": "500.00",
                  "currency": "CHF",
                  "extra_bed": "available"
                }
              ],
              "childrenMeal": [
                {
                  "inclusion": "not_included",
                  "price": "200.00",
                  "currency": "USD",
                  "meal_type": "breakfast",
                  "age_start": 0,
                  "age_end": 5
                }
              ],
              "cot": [
                {
                  "inclusion": "not_included",
                  "price": "0.00",
                  "currency": "USD",
                  "price_unit": "per_room_per_night",
                  "amount": 1
                }
              ],
              "deposit": [
                {
                  "availability": "available",
                  "price": "123.00",
                  "currency": "HNL",
                  "price_unit": "per_room_per_stay",
                  "pricing_method": "fixed",
                  "payment_type": "cash",
                  "deposit_type": "unspecified"
                }
              ],
              "extraBed": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "ALL",
                  "price_unit": "per_guest_per_night",
                  "amount": 1
                }
              ],
              "internet": [
                {
                  "inclusion": "included",
                  "price": "0.00",
                  "currency": null,
                  "price_unit": "unspecified",
                  "internet_type": "unspecified",
                  "work_area": "hotel"
                }
              ],
              "meal": [
                {
                  "inclusion": "not_included",
                  "price": "300.00",
                  "currency": "USD",
                  "meal_type": "breakfast"
                }
              ],
              "noShow": {
                "availability": "available",
                "time": "09:00:00",
                "day_period": "after_midday"
              },
              "parking": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "USD",
                  "price_unit": "per_car_per_night",
                  "territory_type": "unspecified"
                }
              ],
              "pets": [
                {
                  "inclusion": "not_included",
                  "price": "560.00",
                  "currency": "USD",
                  "price_unit": "per_room_per_stay",
                  "pets_type": "unspecified"
                }
              ],
              "shuttle": [
                {
                  "inclusion": "not_included",
                  "price": "100.00",
                  "currency": "EUR",
                  "shuttle_type": "one_way",
                  "destination_type": "train"
                }
              ],
              "visa": {
                "visa_support": "support_enable"
              }
            },
            "roomGroups": [
              {
                "roomGroupId": 95,
                "name": "Standard Double room full double bed",
                "nameStruct": {
                  "mainName": "Standard Double room",
                  "beddingType": "full double bed",
                  "bathroom": "shared bathroom"
                },
                "images": [
                  "https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
                ],
                "roomAmenities": [
                  "bath",
                  "heating"
                ],
                "roomClass": "room",
                "quality": "standard",
                "bathroom": "private-bathroom",
                "hasPrivateBathroom": true,
                "capacity": 2,
                "balcony": false,
                "view": null,
                "floor": null,
                "bedrooms": null,
                "beddingType": "double-bed",
                "isFamily": false,
                "isClub": false,
                "genderRestriction": null
              }
            ],
            "facts": {
              "floorsNumber": null,
              "roomsNumber": null,
              "yearBuilt": null,
              "yearRenovated": null,
              "electricity": {
                "voltage": [],
                "frequency": [
                  60
                ],
                "sockets": [
                  "a",
                  "b"
                ]
              }
            },
            "paymentMethods": [
              "visa",
              "maestro",
              "cash"
            ],
            "keysPickup": {
              "type": "lockbox",
              "isContactless": true,
              "email": "test@example.com",
              "phone": "+15555550123",
              "officeAddress": null,
              "extraInfo": "The keys are in the box by the front door."
            }
          }
        ]
      },
      "HotelRateRoom": {
        "type": "object",
        "description": "The room of a rate. Match it with the hotel content by room name; the document of `get_hotel_info` lists every room type with photos and amenities.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the room as the supplier sells it, in the `language` of the request.",
            "example": "Standard room"
          },
          "beddingType": {
            "anyOf": [
              {
                "type": "string",
                "description": "The bedding as a phrase: `single bed`, `full double bed`, `twin beds` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "The bedding as a phrase: `single bed`, `full double bed`, `twin beds` ..."
          },
          "miscRoomType": {
            "anyOf": [
              {
                "type": "string",
                "description": "Another description of the bed or room, when the supplier gives one (`king size bed`)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Another description of the bed or room, when the supplier gives one (`king size bed`)."
          },
          "capacity": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Number of guests the room is made for; `null` when unknown."
              },
              {
                "type": "null"
              }
            ],
            "description": "Number of guests the room is made for; `null` when unknown."
          },
          "bathroom": {
            "anyOf": [
              {
                "type": "string",
                "description": "`private-bathroom` or `shared-bathroom`."
              },
              {
                "type": "null"
              }
            ],
            "description": "`private-bathroom` or `shared-bathroom`."
          },
          "hasPrivateBathroom": {
            "type": "boolean",
            "description": "`true` when the bathroom is private."
          },
          "balcony": {
            "type": "boolean",
            "description": "`true` when the room has a balcony."
          },
          "roomClass": {
            "anyOf": [
              {
                "type": "string",
                "description": "Class of the room: `room`, `suite`, `junior-suite`, `apartment`, `studio`, `cottage`, `dorm` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "Class of the room: `room`, `suite`, `junior-suite`, `apartment`, `studio`, `cottage`, `dorm` ..."
          },
          "quality": {
            "anyOf": [
              {
                "type": "string",
                "description": "Quality of the room: `economy`, `standard`, `classic`, `superior`, `deluxe`, `executive`, `business` ..."
              },
              {
                "type": "null"
              }
            ],
            "description": "Quality of the room: `economy`, `standard`, `classic`, `superior`, `deluxe`, `executive`, `business` ..."
          },
          "view": {
            "anyOf": [
              {
                "type": "string",
                "description": "View of the room (`city-view`, `garden-view` ...); `null` when there is none."
              },
              {
                "type": "null"
              }
            ],
            "description": "View of the room (`city-view`, `garden-view` ...); `null` when there is none."
          },
          "floor": {
            "anyOf": [
              {
                "type": "string",
                "description": "Floor of the room, when it is special (`attic`)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Floor of the room, when it is special (`attic`)."
          },
          "bedrooms": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Number of bedrooms; `null` when unknown."
              },
              {
                "type": "null"
              }
            ],
            "description": "Number of bedrooms; `null` when unknown."
          },
          "isFamily": {
            "type": "boolean",
            "description": "`true` for a family room."
          },
          "isClub": {
            "type": "boolean",
            "description": "`true` for a club room."
          },
          "genderRestriction": {
            "anyOf": [
              {
                "type": "string",
                "description": "Gender the room is reserved for (hostel dorms); `null` when there is no restriction."
              },
              {
                "type": "null"
              }
            ],
            "description": "Gender the room is reserved for (hostel dorms); `null` when there is no restriction."
          },
          "nonSmoking": {
            "type": "boolean",
            "description": "`true` when the room is non-smoking."
          }
        },
        "required": [
          "name",
          "hasPrivateBathroom",
          "balcony",
          "isFamily",
          "isClub",
          "nonSmoking"
        ]
      },
      "HotelRateMeal": {
        "type": "object",
        "description": "The meal plan of a rate.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "nomeal",
              "breakfast",
              "lunch",
              "dinner",
              "half-board",
              "half-board-lunch",
              "half-board-dinner",
              "full-board",
              "all-inclusive",
              "ultra-all-inclusive"
            ],
            "description": "The meal included in the rate. `nomeal` is room only."
          },
          "included": {
            "type": "boolean",
            "description": "`true` when a meal is included (`type` is not `nomeal`)."
          },
          "childMealIncluded": {
            "type": "boolean",
            "description": "`true` when the meal of the children is included too."
          }
        },
        "required": [
          "type",
          "included"
        ]
      },
      "HotelTax": {
        "type": "object",
        "description": "One tax or fee of a rate. Amounts stay in the currency the supplier states: they are never converted to your agency currency.",
        "properties": {
          "includedInPrice": {
            "type": "boolean",
            "description": "`true` when the tax is already inside `totalPrice`; `false` when the traveler pays it at the property."
          },
          "amount": {
            "type": "number",
            "description": "The amount of the tax for the whole stay."
          },
          "currency": {
            "type": "string",
            "description": "Currency of `amount`: the supplier's currency (`EUR`) or the local currency of the hotel (`HNL`)."
          }
        },
        "required": [
          "includedInPrice",
          "amount",
          "currency"
        ]
      },
      "HotelPriceBlock": {
        "type": "object",
        "description": "The prices of a rate or of an offer. There is no `b2bPrice` in this block: the amount debited from your agency is `totalPrice - agencyCommission` (see Hotel pricing).",
        "properties": {
          "supplierCurrency": {
            "type": "string",
            "description": "Currency the supplier prices in: `EUR`.",
            "example": "EUR",
            "deprecated": true,
            "x-replaced-by": "supplier_currency"
          },
          "supplier_currency": {
            "type": "string",
            "description": "Currency the supplier prices in: `EUR`. The snake_case twin of `supplierCurrency`, with the same value: use this name.",
            "example": "EUR"
          },
          "agencyCurrency": {
            "type": "string",
            "description": "The currency of your agency. Every amount below except `originalPrice` and `taxes` is in it.",
            "example": "DZD",
            "deprecated": true,
            "x-replaced-by": "agency_currency"
          },
          "agency_currency": {
            "type": "string",
            "description": "The currency of your agency. Every amount below except `originalPrice` and `taxes` is in it. The snake_case twin of `agencyCurrency`, with the same value: use this name.",
            "example": "DZD"
          },
          "originalPrice": {
            "type": "number",
            "description": "The supplier's price for the whole stay and all rooms, in `supplierCurrency`. For information: it is not the base of what you pay."
          },
          "agencyCommission": {
            "type": "number",
            "description": "Your commission, in `agencyCurrency`. The agency price is `totalPrice` minus this amount.",
            "deprecated": true,
            "x-replaced-by": "agency_commission"
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission, in `agencyCurrency`. The agency price is `totalPrice` minus this amount. The snake_case twin of `agencyCommission`, with the same value: use this name."
          },
          "totalPrice": {
            "type": "number",
            "description": "What the traveler pays: the whole stay, all rooms, in `agencyCurrency`. You are debited `totalPrice - agencyCommission`.",
            "deprecated": true,
            "x-replaced-by": "total_price"
          },
          "total_price": {
            "type": "number",
            "description": "What the traveler pays: the whole stay, all rooms, in `agencyCurrency`. You are debited `totalPrice - agencyCommission`. The snake_case twin of `totalPrice`, with the same value: use this name."
          },
          "taxes": {
            "type": "object",
            "description": "Taxes and fees by name (`cityTax`, `cleaningFee`, `electricityFee`, `occupancyTax`, `resortFee`, `serviceFee`, `vat` ...), only the ones the rate has.",
            "properties": {},
            "additionalProperties": {
              "$ref": "#/components/schemas/HotelTax"
            }
          }
        },
        "required": [
          "supplierCurrency",
          "agencyCurrency",
          "originalPrice",
          "agencyCommission",
          "totalPrice",
          "taxes"
        ]
      },
      "HotelCancellationWindow": {
        "type": "object",
        "description": "One window of the cancellation schedule.",
        "properties": {
          "from": {
            "anyOf": [
              {
                "type": "string",
                "description": "Start of the window, a UTC date and time without zone suffix; `null` for the first window (from now)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Start of the window, a UTC date and time without zone suffix; `null` for the first window (from now)."
          },
          "until": {
            "anyOf": [
              {
                "type": "string",
                "description": "End of the window, a UTC date and time without zone suffix; `null` for the last window (to check-in and after)."
              },
              {
                "type": "null"
              }
            ],
            "description": "End of the window, a UTC date and time without zone suffix; `null` for the last window (to check-in and after)."
          },
          "originalAmount": {
            "type": "number",
            "description": "The penalty charged by the supplier in this window, in `originalCurrency`. `0` in the free window."
          },
          "originalCurrency": {
            "type": "string",
            "description": "Currency of `originalAmount`, the supplier's: `EUR`."
          },
          "currency": {
            "type": "string",
            "description": "Your agency currency, as a label. `originalAmount` is NOT in this currency."
          }
        },
        "required": [
          "from",
          "until",
          "originalAmount",
          "originalCurrency",
          "currency"
        ]
      },
      "HotelCancellationPolicy": {
        "type": "object",
        "description": "When and at what price a booking of the rate can be cancelled. The amounts are the supplier's (`originalAmount`, in `originalCurrency`); what is refunded to your agency is in the answer of `cancel_hotel`.",
        "properties": {
          "freeCancellationBefore": {
            "anyOf": [
              {
                "type": "string",
                "description": "UTC date and time (no zone suffix) until which the booking can be cancelled for free. `null` means the rate is non-refundable.",
                "example": "2026-12-01T21:00:00"
              },
              {
                "type": "null"
              }
            ],
            "description": "UTC date and time (no zone suffix) until which the booking can be cancelled for free. `null` means the rate is non-refundable.",
            "deprecated": true,
            "x-replaced-by": "free_cancellation_before"
          },
          "free_cancellation_before": {
            "anyOf": [
              {
                "type": "string",
                "description": "UTC date and time (no zone suffix) until which the booking can be cancelled for free. `null` means the rate is non-refundable.",
                "example": "2026-12-01T21:00:00"
              },
              {
                "type": "null"
              }
            ],
            "description": "UTC date and time (no zone suffix) until which the booking can be cancelled for free. `null` means the rate is non-refundable. The snake_case twin of `freeCancellationBefore`, with the same value: use this name."
          },
          "policies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelCancellationWindow"
            },
            "description": "The schedule of penalties from now to check-in: the free window first, then the windows with a penalty."
          }
        },
        "required": [
          "freeCancellationBefore",
          "policies"
        ]
      },
      "HotelRate": {
        "type": "object",
        "description": "One purchasable rate of a hotel: a room type, a meal plan, a cancellation schedule and a price for the whole stay. Only prepaid (deposit) rates are returned.",
        "properties": {
          "bookHash": {
            "type": "string",
            "description": "The token of this rate. Send it as `book_hash` to `pre_book`. It stays valid for 30 minutes.",
            "example": "h-14574656-7ce9-5a21-b673-8654c5ebd416",
            "deprecated": true,
            "x-replaced-by": "book_hash"
          },
          "book_hash": {
            "type": "string",
            "description": "The token of this rate. Send it as `book_hash` to `pre_book`. It stays valid for 30 minutes. The snake_case twin of `bookHash`, with the same value: use this name.",
            "example": "h-14574656-7ce9-5a21-b673-8654c5ebd416"
          },
          "matchHash": {
            "type": "string",
            "description": "A second identifier of the rate. You do not need it to book: send `bookHash`.",
            "example": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c",
            "deprecated": true,
            "x-replaced-by": "match_hash"
          },
          "match_hash": {
            "type": "string",
            "description": "A second identifier of the rate. You do not need it to book: send `bookHash`. The snake_case twin of `matchHash`, with the same value: use this name.",
            "example": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c"
          },
          "room": {
            "$ref": "#/components/schemas/HotelRateRoom"
          },
          "meal": {
            "$ref": "#/components/schemas/HotelRateMeal"
          },
          "dailyPrices": {
            "type": "array",
            "items": {
              "type": "number",
              "description": "The supplier's price of one night, in `supplierCurrency`."
            },
            "description": "The supplier's price of each night of the stay (first room), in `supplierCurrency`.",
            "deprecated": true,
            "x-replaced-by": "daily_prices"
          },
          "daily_prices": {
            "type": "array",
            "items": {
              "type": "number",
              "description": "The supplier's price of one night, in `supplierCurrency`."
            },
            "description": "The supplier's price of each night of the stay (first room), in `supplierCurrency`. The snake_case twin of `dailyPrices`, with the same value: use this name."
          },
          "allotment": {
            "type": "integer",
            "description": "How many rooms of this type are left at this rate."
          },
          "isPackage": {
            "type": "boolean",
            "description": "`true` when the rate is part of a package."
          },
          "anyResidency": {
            "type": "boolean",
            "description": "`false` when the rate is reserved for some residencies: it exists for the `residency` of your request only."
          },
          "deposit": {
            "anyOf": [
              {
                "description": "Reserved for deposit terms of the rate. `null` today.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Reserved for deposit terms of the rate. `null` today."
          },
          "noShow": {
            "anyOf": [
              {
                "description": "Reserved for no-show terms of the rate. `null` today.",
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Reserved for no-show terms of the rate. `null` today."
          },
          "cancellationPolicy": {
            "$ref": "#/components/schemas/HotelCancellationPolicy"
          },
          "pricing": {
            "$ref": "#/components/schemas/HotelPriceBlock"
          },
          "exchangeRate": {
            "type": "number",
            "description": "The rate of exchange from `supplierCurrency` to `agencyCurrency` that was used (`285.0` in the sandbox).",
            "deprecated": true,
            "x-replaced-by": "exchange_rate"
          },
          "exchange_rate": {
            "type": "number",
            "description": "The rate of exchange from `supplierCurrency` to `agencyCurrency` that was used (`285.0` in the sandbox). The snake_case twin of `exchangeRate`, with the same value: use this name."
          }
        },
        "required": [
          "bookHash",
          "matchHash",
          "room",
          "meal",
          "dailyPrices",
          "allotment",
          "isPackage",
          "anyResidency",
          "deposit",
          "noShow",
          "cancellationPolicy",
          "pricing",
          "exchangeRate"
        ],
        "examples": [
          {
            "bookHash": "h-14574656-7ce9-5a21-b673-8654c5ebd416",
            "matchHash": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c",
            "room": {
              "name": "Standard room",
              "beddingType": "single bed",
              "miscRoomType": null,
              "capacity": null,
              "bathroom": "private-bathroom",
              "hasPrivateBathroom": true,
              "balcony": false,
              "roomClass": "room",
              "quality": "standard",
              "view": null,
              "floor": null,
              "bedrooms": null,
              "isFamily": false,
              "isClub": false,
              "genderRestriction": null,
              "nonSmoking": true
            },
            "meal": {
              "type": "nomeal",
              "included": false,
              "childMealIncluded": false
            },
            "dailyPrices": [
              1,
              1,
              1
            ],
            "allotment": 77,
            "isPackage": false,
            "anyResidency": true,
            "deposit": null,
            "noShow": null,
            "cancellationPolicy": {
              "freeCancellationBefore": "2026-12-01T21:00:00",
              "policies": [
                {
                  "from": null,
                  "until": "2026-12-01T21:00:00",
                  "originalAmount": 0,
                  "originalCurrency": "EUR",
                  "currency": "DZD"
                },
                {
                  "from": "2026-12-01T21:00:00",
                  "until": null,
                  "originalAmount": 3,
                  "originalCurrency": "EUR",
                  "currency": "DZD"
                }
              ]
            },
            "pricing": {
              "supplierCurrency": "EUR",
              "agencyCurrency": "DZD",
              "originalPrice": 3,
              "agencyCommission": 2.97,
              "totalPrice": 1484.97,
              "taxes": {
                "cityTax": {
                  "includedInPrice": false,
                  "amount": 23.1,
                  "currency": "HNL"
                },
                "electricityFee": {
                  "includedInPrice": true,
                  "amount": 0.6,
                  "currency": "EUR"
                },
                "resortFee": {
                  "includedInPrice": true,
                  "amount": 0.68,
                  "currency": "EUR"
                }
              }
            },
            "exchangeRate": 285
          }
        ]
      },
      "HotelRatesData": {
        "type": "object",
        "description": "The rates of one hotel.",
        "properties": {
          "hotelId": {
            "anyOf": [
              {
                "type": "string",
                "description": "The id of the hotel; `null` when the hotel is unknown or has no availability for the request.",
                "example": "test_hotel_do_not_book"
              },
              {
                "type": "null"
              }
            ],
            "description": "The id of the hotel; `null` when the hotel is unknown or has no availability for the request.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hotel_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "The id of the hotel; `null` when the hotel is unknown or has no availability for the request.",
                "example": "test_hotel_do_not_book"
              },
              {
                "type": "null"
              }
            ],
            "description": "The id of the hotel; `null` when the hotel is unknown or has no availability for the request. The snake_case twin of `hotelId`, with the same value: use this name."
          },
          "hid": {
            "anyOf": [
              {
                "type": "integer",
                "description": "The supplier's numeric hotel id; `null` with `hotelId`."
              },
              {
                "type": "null"
              }
            ],
            "description": "The supplier's numeric hotel id; `null` with `hotelId`."
          },
          "rates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelRate"
            },
            "description": "Every rate of the hotel for the stay and occupancy, not sorted by price. Empty when there is none."
          }
        },
        "required": [
          "hotelId",
          "hid",
          "rates"
        ]
      },
      "HotelRatesResponse": {
        "type": "object",
        "description": "The answer of `get_rates`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the call worked, also when the hotel has no rates."
          },
          "data": {
            "$ref": "#/components/schemas/HotelRatesData"
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds the call took, as a number.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds the call took, as a number. The snake_case twin of `processingTime`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "data",
          "processingTime"
        ]
      },
      "HotelPaymentOption": {
        "type": "object",
        "description": "One way the supplier offers to pay for the order. Informational: `book_hotel` always pays with `deposit`.",
        "properties": {
          "amount": {
            "type": "string",
            "description": "The amount as a decimal string, in `currencyCode`.",
            "example": "3.00"
          },
          "currencyCode": {
            "type": "string",
            "description": "ISO 4217 currency of `amount`.",
            "example": "EUR"
          },
          "isNeedCreditCard": {
            "type": "boolean",
            "description": "`true` when the option needs a card. FX-Port never does: it pays with the `deposit` option."
          },
          "isNeedCvc": {
            "type": "boolean",
            "description": "`true` when the option needs a card security code."
          },
          "type": {
            "type": "string",
            "enum": [
              "deposit",
              "now"
            ],
            "description": "`deposit` pays from the supplier deposit of FX-Port (the one FX-Port uses); `now` is a card payment."
          }
        },
        "required": [
          "amount",
          "currencyCode",
          "isNeedCreditCard",
          "isNeedCvc",
          "type"
        ]
      },
      "HotelPreBookForm": {
        "type": "object",
        "description": "The supplier's booking form for the order that `book_hotel` finishes.",
        "properties": {
          "partnerOrderId": {
            "anyOf": [
              {
                "type": "string",
                "description": "The order id again. `null` when the supplier opened no order.",
                "example": "HMVXX4ZQ3"
              },
              {
                "type": "null"
              }
            ],
            "description": "The order id again. `null` when the supplier opened no order.",
            "deprecated": true,
            "x-replaced-by": "partner_order_id"
          },
          "partner_order_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "The order id again. `null` when the supplier opened no order.",
                "example": "HMVXX4ZQ3"
              },
              {
                "type": "null"
              }
            ],
            "description": "The order id again. `null` when the supplier opened no order. The snake_case twin of `partnerOrderId`, with the same value: use this name."
          },
          "orderId": {
            "anyOf": [
              {
                "type": "integer",
                "description": "The supplier's order number. `null` means the supplier opened no order and the hotel cannot be booked.",
                "example": 757581298
              },
              {
                "type": "null"
              }
            ],
            "description": "The supplier's order number. `null` means the supplier opened no order and the hotel cannot be booked."
          },
          "itemId": {
            "anyOf": [
              {
                "type": "integer",
                "description": "The supplier's item number inside the order.",
                "example": 232430616
              },
              {
                "type": "null"
              }
            ],
            "description": "The supplier's item number inside the order."
          },
          "paymentTypes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelPaymentOption"
            },
            "description": "The ways to pay the supplier offers: one `deposit` and one `now` option per card currency (about 40 entries)."
          }
        },
        "required": [
          "partnerOrderId",
          "orderId",
          "itemId",
          "paymentTypes"
        ]
      },
      "HotelPreBookData": {
        "type": "object",
        "description": "The locked rate.",
        "properties": {
          "hotelId": {
            "type": "string",
            "description": "The id of the hotel.",
            "example": "test_hotel_do_not_book",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hotel_id": {
            "type": "string",
            "description": "The id of the hotel. The snake_case twin of `hotelId`, with the same value: use this name.",
            "example": "test_hotel_do_not_book"
          },
          "hid": {
            "type": "integer",
            "description": "The supplier's numeric hotel id."
          },
          "rates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelRate"
            },
            "description": "The one rate you locked, with a new `bookHash` that starts with `p-`. Do not reuse it."
          }
        },
        "required": [
          "hotelId",
          "hid",
          "rates"
        ]
      },
      "HotelPreBookResponse": {
        "type": "object",
        "description": "The answer of `pre_book`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the rate was locked."
          },
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ],
            "description": "Always `ok`."
          },
          "partnerOrderId": {
            "type": "string",
            "description": "The id of the booking you are about to make. Send it as `booking_id` to `book_hotel`, then to `GET /bookings/{id}`, `get_voucher` and `cancel_hotel`.",
            "example": "HMVXX4ZQ3",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The id of the booking you are about to make. Send it as `booking_id` to `book_hotel`, then to `GET /bookings/{id}`, `get_voucher` and `cancel_hotel`. The snake_case twin of `partnerOrderId`, with the same value: use this name.",
            "example": "HMVXX4ZQ3"
          },
          "partner_order_id": {
            "type": "string",
            "description": "The id of the booking you are about to make. Send it as `booking_id` to `book_hotel`, then to `GET /bookings/{id}`, `get_voucher` and `cancel_hotel`. The snake_case twin of `partnerOrderId`, with the same value: use this name.",
            "example": "HMVXX4ZQ3"
          },
          "priceChanged": {
            "type": "boolean",
            "description": "`true` when the supplier's price moved since `get_rates` (within `price_increase_percent`). `data` holds the new price.",
            "deprecated": true,
            "x-replaced-by": "price_changed"
          },
          "price_changed": {
            "type": "boolean",
            "description": "`true` when the supplier's price moved since `get_rates` (within `price_increase_percent`). `data` holds the new price. The snake_case twin of `priceChanged`, with the same value: use this name."
          },
          "data": {
            "$ref": "#/components/schemas/HotelPreBookData"
          },
          "formData": {
            "$ref": "#/components/schemas/HotelPreBookForm"
          }
        },
        "required": [
          "success",
          "status",
          "partnerOrderId",
          "priceChanged",
          "data",
          "formData"
        ]
      },
      "HotelRatesRequest": {
        "type": "object",
        "description": "The hotel, the stay and the rooms to price. Do not send `currency`: prices come in the currency of your agency and a `currency` field is refused.",
        "properties": {
          "hotel_id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "The FX-Port id of the hotel (1 to 128 characters: letters, digits and `_ . : -`; spaces around it are trimmed): `hotels_details[].id` of a search, a hit of the name search. A JSON integer is read as its text, but it is not looked up as the supplier's numeric `hid` here: send the FX-Port id.",
            "example": "test_hotel_do_not_book"
          },
          "checkin": {
            "type": "string",
            "description": "Check-in date, `YYYY-MM-DD`: from yesterday (for time zones) to 400 days ahead. A date written with a midnight time (`2026-12-01T00:00:00Z`) is read as that day, with a `coerced_value` warning; any other time of day is `invalid_date`.",
            "example": "2026-12-01"
          },
          "checkout": {
            "type": "string",
            "description": "Check-out date, `YYYY-MM-DD`, after `checkin`. A stay is 1 to 30 nights. A midnight time is read as in `checkin`.",
            "example": "2026-12-04"
          },
          "guests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelGuestRoom"
            },
            "description": "The rooms: one `{adults, children}` object per room, 1 to 9 rooms. Rates are for exactly this occupancy."
          },
          "residency": {
            "type": "string",
            "description": "Residency of the guests, a two-letter ISO 3166-1 country code. A sub-territory code of the website's nationality list (`GB-SCT`, `USPR`, `PT-MA` ...) is read as its country, with a `coerced_value` warning: send the 2-letter country. Some rates exist for some residencies only.",
            "default": "gb",
            "example": "gb"
          },
          "language": {
            "type": "string",
            "description": "Language of the room names, such as `en`, `fr` or `zh_CN`. A longer tag (`zh-Hans-CN`, `es-419`) is read as the supplier's code, with a `coerced_value` warning.",
            "default": "en",
            "example": "en"
          },
          "id": {
            "type": "string",
            "description": "The hotel id, in its older spelling. Use `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hotelId": {
            "type": "string",
            "description": "The hotel id, in its older spelling. Use `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "checkinDate": {
            "type": "string",
            "description": "The check-in date, in an older spelling. Use `checkin`.",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "checkin_date": {
            "type": "string",
            "description": "The check-in date, in an older spelling. Use `checkin`.",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "check_in": {
            "type": "string",
            "description": "The check-in date, in an older spelling. Use `checkin`.",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "checkoutDate": {
            "type": "string",
            "description": "The check-out date, in an older spelling. Use `checkout`.",
            "deprecated": true,
            "x-replaced-by": "checkout"
          },
          "checkout_date": {
            "type": "string",
            "description": "The check-out date, in an older spelling. Use `checkout`.",
            "deprecated": true,
            "x-replaced-by": "checkout"
          },
          "check_out": {
            "type": "string",
            "description": "The check-out date, in an older spelling. Use `checkout`.",
            "deprecated": true,
            "x-replaced-by": "checkout"
          }
        },
        "required": [
          "hotel_id",
          "checkin",
          "checkout",
          "guests"
        ]
      },
      "HotelPreBookRequest": {
        "type": "object",
        "description": "The rate to lock.",
        "properties": {
          "book_hash": {
            "type": "string",
            "description": "The `bookHash` of the rate you chose, from `get_rates` (it starts with `h-`). Valid for 30 minutes. The `matchHash` of a rate is not a booking token and is not read as one: a call that sends only a `match_hash` fails with `missing_field`.",
            "example": "h-14574656-7ce9-5a21-b673-8654c5ebd416"
          },
          "price_increase_percent": {
            "type": "number",
            "description": "The price rise, in percent, you accept if the supplier raised the price since `get_rates`. `0` means none: the call fails if the price moved; `null` means not set (0). A repeated call within a minute with the same `book_hash` and the same percentage gets the same answer; another percentage is a new request. A number written as text (`\"5\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": 0,
            "maximum": 100,
            "default": 0,
            "example": 0
          },
          "language": {
            "type": "string",
            "description": "Language of the hotel name in the answer.",
            "default": "en"
          },
          "hash": {
            "type": "string",
            "description": "The rate token, in its older spelling. Use `book_hash`.",
            "deprecated": true,
            "x-replaced-by": "book_hash"
          },
          "bookHash": {
            "type": "string",
            "description": "The rate token, in its older spelling. Use `book_hash`.",
            "deprecated": true,
            "x-replaced-by": "book_hash"
          }
        },
        "required": [
          "book_hash"
        ]
      },
      "HotelBookRequest": {
        "type": "object",
        "description": "The booking to finish. The price, the hotel, the dates and the residency come from the offer locked by `pre_book`, not from this body.",
        "properties": {
          "booking_id": {
            "type": "string",
            "description": "The id of the booking: the `partnerOrderId` that `pre_book` returned. Book it once.",
            "example": "HMVXX4ZQ3"
          },
          "rooms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelBookRoom"
            },
            "description": "The guests of each room, in the same order and with the same occupancy as the search: one object per room, one entry per adult and per child."
          },
          "user": {
            "$ref": "#/components/schemas/HotelBookUser"
          },
          "language": {
            "type": "string",
            "description": "Language of the confirmation e-mail and of the voucher.",
            "default": "en"
          },
          "send_email": {
            "type": "boolean",
            "description": "`false` to stop FX-Port from sending its confirmation e-mail to `user.email`. Use it when you test. The numbers `1` and `0`, and the texts `true`, `false`, `yes`, `no`, `on`, `off`, `1` and `0`, are read as the boolean (a text with a `coerced_value` warning); `null` means the default; any other value is `400 invalid_send_email`.",
            "default": true
          },
          "commission_override": {
            "$ref": "#/components/schemas/HotelCommissionOverride"
          },
          "book_hash": {
            "type": "string",
            "description": "Do not send it: FX-Port keeps the rate you locked with `pre_book` under `booking_id` and always books that rate. A hash the offer was made from (the `bookHash` of `pre_book`, the `bookHash` of `get_rates`, a match hash) is accepted silently; any other value is ignored, with an `ignored_field` warning, and the pre-booked rate is booked. It is never an error."
          },
          "hotel_id": {
            "type": "string",
            "description": "Do not send it: FX-Port keeps the hotel you locked with `pre_book` under `booking_id` and always books that hotel. The same hotel (in another case, its numeric supplier id, the id `get_rates` was called with) is accepted silently; any other value is ignored, with an `ignored_field` warning, and the pre-booked hotel is booked. It is never an error."
          },
          "partner_order_id": {
            "type": "string",
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "partnerOrderId": {
            "type": "string",
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "bookingId": {
            "type": "string",
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "bookHash": {
            "type": "string",
            "description": "The rate token, in its older spelling. Do not send it: see `book_hash`.",
            "deprecated": true,
            "x-replaced-by": "book_hash"
          },
          "hotelId": {
            "type": "string",
            "description": "The hotel id, in its older spelling. Do not send it: see `hotel_id`.",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          }
        },
        "required": [
          "booking_id",
          "rooms",
          "user"
        ]
      },
      "HotelCancelRequest": {
        "type": "object",
        "description": "The booking to cancel.",
        "properties": {
          "booking_id": {
            "type": "string",
            "description": "The id of the booking to cancel: the `bookingId` of `book_hotel`.",
            "example": "HMVXX4ZQ3"
          },
          "partner_order_id": {
            "type": "string",
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "partnerOrderId": {
            "type": "string",
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "bookingId": {
            "type": "string",
            "description": "The booking id, in its older spelling. Use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          }
        },
        "required": [
          "booking_id"
        ]
      },
      "HotelGuestRoom": {
        "type": "object",
        "description": "The people of one room in a search or a rates request. Send one object per room.",
        "properties": {
          "adults": {
            "type": "integer",
            "description": "Number of adults in the room, 1 to 6. A number written as text (`\"2\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": 1,
            "maximum": 6,
            "example": 2
          },
          "children": {
            "type": "array",
            "items": {
              "type": "integer",
              "description": "Age of a child in years at check-in, 0 to 17. A number written as text (`\"8\"`) is read as the number, with a `coerced_value` warning.",
              "minimum": 0,
              "maximum": 17
            },
            "description": "One entry per child of the room: the age at check-in. `[]` or omitted when there is none. At most 4 children per room."
          }
        },
        "required": [
          "adults"
        ]
      },
      "HotelBookGuest": {
        "type": "object",
        "description": "A guest who sleeps in a room.",
        "properties": {
          "first_name": {
            "type": "string",
            "description": "Given name of the guest. Letters of any script, spaces, `-`, `,`, `.` and `'` are kept; digits and other symbols are replaced by a space.",
            "example": "Jane"
          },
          "last_name": {
            "type": "string",
            "description": "Family name of the guest, same rules as `first_name`.",
            "example": "Smith"
          },
          "is_child": {
            "type": "boolean",
            "description": "`true` for a child. A child must carry `is_child` and `age`, or the supplier refuses the booking with `incorrect_guests_number`. The numbers `1` and `0`, and the texts `true`, `false`, `yes`, `no`, `on`, `off`, `1` and `0`, are read as the boolean (a text with a `coerced_value` warning); `null` or an empty text means not set (an adult); any other value is `400 invalid_is_child`."
          },
          "age": {
            "type": "integer",
            "description": "Age of the child in years at check-in, 0 to 17. Required with `is_child`. A number written as text (`\"8\"`) is read as the number, with a `coerced_value` warning.",
            "minimum": 0,
            "maximum": 17
          }
        },
        "required": [
          "first_name",
          "last_name"
        ]
      },
      "HotelBookRoom": {
        "type": "object",
        "description": "The guests of one room, in the same order as the rooms of the search.",
        "properties": {
          "guests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelBookGuest"
            },
            "description": "Every guest of the room, adults and children, as many as you searched (one object per adult and per child)."
          }
        },
        "required": [
          "guests"
        ]
      },
      "HotelBookUser": {
        "type": "object",
        "description": "The person who makes the booking.",
        "properties": {
          "email": {
            "type": "string",
            "description": "E-mail address of the booker. FX-Port sends its confirmation here (unless `send_email` is `false`); the supplier gets an address of FX-Port's own.",
            "example": "traveler@example.com"
          },
          "phone": {
            "type": "string",
            "description": "Phone number of the booker, with country code.",
            "example": "+15555550123"
          },
          "comment": {
            "type": "string",
            "description": "Free text for the hotel, forwarded to the supplier."
          }
        },
        "required": [
          "email"
        ]
      },
      "HotelCommissionOverride": {
        "type": "object",
        "description": "Sets your commission for this booking instead of your default one. The traveler price becomes the price you pay plus this commission; what you are debited does not change.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "fixed",
              "percentage"
            ],
            "description": "`fixed`: `value` is your commission per room, in your agency currency. `percentage`: `value` is a percentage of the price you pay."
          },
          "value": {
            "type": "number",
            "description": "The amount or the percentage, `0` or more. A negative value is ignored.",
            "minimum": 0,
            "example": 50
          }
        },
        "required": [
          "type",
          "value"
        ]
      },
      "HotelBookingPricing": {
        "type": "object",
        "description": "The price of the booking as it was locked by `pre_book`.",
        "properties": {
          "totalPrice": {
            "type": "number",
            "description": "What the traveler pays, in `currency`.",
            "deprecated": true,
            "x-replaced-by": "total_price"
          },
          "total_price": {
            "type": "number",
            "description": "What the traveler pays, in `currency`. The snake_case twin of `totalPrice`, with the same value: use this name."
          },
          "agencyCommission": {
            "type": "number",
            "description": "Your commission, in `currency`. You are debited `totalPrice - agencyCommission`.",
            "deprecated": true,
            "x-replaced-by": "agency_commission"
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission, in `currency`. You are debited `totalPrice - agencyCommission`. The snake_case twin of `agencyCommission`, with the same value: use this name."
          },
          "currency": {
            "type": "string",
            "description": "The currency of your agency.",
            "deprecated": true,
            "x-replaced-by": "agency_currency"
          },
          "agency_currency": {
            "type": "string",
            "description": "The currency of your agency. The snake_case twin of `currency`, with the same value: use this name."
          },
          "supplierCurrency": {
            "type": "string",
            "description": "The currency the supplier priced in: `EUR`.",
            "deprecated": true,
            "x-replaced-by": "supplier_currency"
          },
          "supplier_currency": {
            "type": "string",
            "description": "The currency the supplier priced in: `EUR`. The snake_case twin of `supplierCurrency`, with the same value: use this name."
          },
          "originalPrice": {
            "type": "number",
            "description": "The supplier's price in `supplierCurrency`, for information."
          }
        },
        "required": [
          "totalPrice",
          "agencyCommission",
          "currency",
          "supplierCurrency",
          "originalPrice"
        ]
      },
      "HotelPostProcessing": {
        "type": "object",
        "description": "Internal progress of the booking. Poll the booking instead of reading it.",
        "properties": {
          "bookingDbId": {
            "type": "string",
            "description": "The internal id of the booking row (a UUID). The booking detail calls it `id`.",
            "example": "24fec1e5-a09f-48ec-8050-1adaab0a8de1"
          },
          "webhookSent": {
            "type": "boolean",
            "description": "Describes an internal step of FX-Port (the voucher rendering), not a message to you: FX-Port sends no webhooks to customers."
          },
          "bucketLink": {
            "anyOf": [
              {
                "type": "string",
                "description": "The voucher link. Always `null` here: the voucher is made after the call; read it with `get_voucher`."
              },
              {
                "type": "null"
              }
            ],
            "description": "The voucher link. Always `null` here: the voucher is made after the call; read it with `get_voucher`.",
            "deprecated": true,
            "x-replaced-by": "voucher_url"
          },
          "voucher_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "The voucher link. Always `null` here: the voucher is made after the call; read it with `get_voucher`."
              },
              {
                "type": "null"
              }
            ],
            "description": "The voucher link. Always `null` here: the voucher is made after the call; read it with `get_voucher`. The snake_case twin of `bucketLink`, with the same value: use this name."
          }
        },
        "required": [
          "bookingDbId",
          "webhookSent",
          "bucketLink"
        ]
      },
      "HotelBookingConfirmation": {
        "type": "object",
        "description": "The answer of `book_hotel`: the booking was accepted and is `pending`. Read it with `GET /bookings/{booking_id}?product=hotels` until it is `confirmed` (4 to 14 seconds).",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the supplier accepted the booking."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending"
            ],
            "description": "Always `pending`: the supplier confirms in the background. Poll the booking until it is `confirmed`."
          },
          "bookingId": {
            "type": "string",
            "description": "The id of the booking. The same value as `partnerOrderId` and as the `booking_id` you sent.",
            "example": "HMVXX4ZQ3",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The id of the booking. The same value as `partnerOrderId` and as the `booking_id` you sent. The snake_case twin of `bookingId`, with the same value: use this name.",
            "example": "HMVXX4ZQ3"
          },
          "partnerOrderId": {
            "type": "string",
            "description": "The id of the booking, as the supplier knows it.",
            "example": "HMVXX4ZQ3",
            "deprecated": true,
            "x-replaced-by": "partner_order_id"
          },
          "partner_order_id": {
            "type": "string",
            "description": "The id of the booking, as the supplier knows it. The snake_case twin of `partnerOrderId`, with the same value: use this name.",
            "example": "HMVXX4ZQ3"
          },
          "hotelId": {
            "type": "string",
            "description": "The id of the hotel that was booked.",
            "example": "test_hotel_do_not_book",
            "deprecated": true,
            "x-replaced-by": "hotel_id"
          },
          "hotel_id": {
            "type": "string",
            "description": "The id of the hotel that was booked. The snake_case twin of `hotelId`, with the same value: use this name.",
            "example": "test_hotel_do_not_book"
          },
          "checkinDate": {
            "type": "string",
            "description": "Check-in date, `YYYY-MM-DD`, taken from the locked offer.",
            "example": "2026-12-01",
            "deprecated": true,
            "x-replaced-by": "checkin"
          },
          "checkin": {
            "type": "string",
            "description": "Check-in date, `YYYY-MM-DD`, taken from the locked offer. The snake_case twin of `checkinDate`, with the same value: use this name.",
            "example": "2026-12-01"
          },
          "checkoutDate": {
            "type": "string",
            "description": "Check-out date, `YYYY-MM-DD`.",
            "example": "2026-12-04",
            "deprecated": true,
            "x-replaced-by": "checkout"
          },
          "checkout": {
            "type": "string",
            "description": "Check-out date, `YYYY-MM-DD`. The snake_case twin of `checkoutDate`, with the same value: use this name.",
            "example": "2026-12-04"
          },
          "pricing": {
            "$ref": "#/components/schemas/HotelBookingPricing"
          },
          "postProcessing": {
            "$ref": "#/components/schemas/HotelPostProcessing"
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds the call took.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds the call took. The snake_case twin of `processingTime`, with the same value: use this name."
          },
          "balanceDebited": {
            "type": "boolean",
            "description": "Sandbox only: `false`, nothing was debited. The key is absent on live.",
            "deprecated": true,
            "x-replaced-by": "balance_debited"
          },
          "balance_debited": {
            "type": "boolean",
            "description": "Sandbox only: `false`, nothing was debited. The key is absent on live. The snake_case twin of `balanceDebited`, with the same value: use this name."
          },
          "transactionCreated": {
            "type": "boolean",
            "description": "Sandbox only: `false`. Absent on live.",
            "deprecated": true,
            "x-replaced-by": "transaction_created"
          },
          "transaction_created": {
            "type": "boolean",
            "description": "Sandbox only: `false`. Absent on live. The snake_case twin of `transactionCreated`, with the same value: use this name."
          },
          "payoutCreated": {
            "type": "boolean",
            "description": "Sandbox only: `false`. Absent on live.",
            "deprecated": true,
            "x-replaced-by": "payout_created"
          },
          "payout_created": {
            "type": "boolean",
            "description": "Sandbox only: `false`. Absent on live. The snake_case twin of `payoutCreated`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "status",
          "bookingId",
          "partnerOrderId",
          "hotelId",
          "checkinDate",
          "checkoutDate",
          "pricing",
          "postProcessing",
          "processingTime"
        ]
      },
      "HotelRefund": {
        "type": "object",
        "description": "What the cancellation gave back.",
        "properties": {
          "cancellationType": {
            "type": "string",
            "enum": [
              "free",
              "partial",
              "none"
            ],
            "description": "`free`: the supplier refunded everything; `partial`: a penalty applied; `none`: no refund.",
            "deprecated": true,
            "x-replaced-by": "cancellation_type"
          },
          "cancellation_type": {
            "type": "string",
            "enum": [
              "free",
              "partial",
              "none"
            ],
            "description": "`free`: the supplier refunded everything; `partial`: a penalty applied; `none`: no refund. The snake_case twin of `cancellationType`, with the same value: use this name."
          },
          "type": {
            "type": "string",
            "enum": [
              "Free cancellation",
              "Partial cancellation",
              "No refund"
            ],
            "description": "The same as a label for people."
          },
          "agencyAmount": {
            "type": "number",
            "description": "What is credited back to your agency, in `currency`. `0` for `none`.",
            "deprecated": true,
            "x-replaced-by": "agency_refund_amount"
          },
          "agency_refund_amount": {
            "type": "number",
            "description": "What is credited back to your agency, in `currency`. `0` for `none`. The snake_case twin of `agencyAmount`, with the same value: use this name."
          },
          "travelerAmount": {
            "type": "number",
            "description": "What the traveler gets back, in `currency`.",
            "deprecated": true,
            "x-replaced-by": "traveler_refund_amount"
          },
          "traveler_refund_amount": {
            "type": "number",
            "description": "What the traveler gets back, in `currency`. The snake_case twin of `travelerAmount`, with the same value: use this name."
          },
          "currency": {
            "type": "string",
            "description": "The currency of your agency.",
            "deprecated": true,
            "x-replaced-by": "agency_currency"
          },
          "agency_currency": {
            "type": "string",
            "description": "The currency of your agency. The snake_case twin of `currency`, with the same value: use this name."
          },
          "providerAmount": {
            "type": "number",
            "description": "What the supplier refunded, in `providerCurrency`.",
            "deprecated": true,
            "x-replaced-by": "supplier_refund_amount"
          },
          "supplier_refund_amount": {
            "type": "number",
            "description": "What the supplier refunded, in `providerCurrency`. The snake_case twin of `providerAmount`, with the same value: use this name."
          },
          "providerCurrency": {
            "type": "string",
            "description": "The supplier's currency: `EUR`.",
            "deprecated": true,
            "x-replaced-by": "supplier_currency"
          },
          "supplier_currency": {
            "type": "string",
            "description": "The supplier's currency: `EUR`. The snake_case twin of `providerCurrency`, with the same value: use this name."
          }
        },
        "required": [
          "cancellationType",
          "type",
          "agencyAmount",
          "travelerAmount",
          "currency",
          "providerAmount",
          "providerCurrency"
        ]
      },
      "HotelCancellation": {
        "type": "object",
        "description": "The answer of `cancel_hotel`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the supplier cancelled the booking."
          },
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ],
            "description": "Always `ok`: the call worked. The state of the booking is `bookingStatus`."
          },
          "bookingId": {
            "type": "string",
            "description": "The id of the booking that was cancelled.",
            "example": "HMVXX4ZQ3",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The id of the booking that was cancelled. The snake_case twin of `bookingId`, with the same value: use this name.",
            "example": "HMVXX4ZQ3"
          },
          "bookingStatus": {
            "type": "string",
            "enum": [
              "refunded",
              "partial_refund",
              "cancelled"
            ],
            "description": "The new status of the booking: `refunded` (free cancellation), `partial_refund` (a penalty applied) or `cancelled` (no refund).",
            "deprecated": true,
            "x-replaced-by": "booking_status"
          },
          "booking_status": {
            "type": "string",
            "enum": [
              "refunded",
              "partial_refund",
              "cancelled"
            ],
            "description": "The new status of the booking: `refunded` (free cancellation), `partial_refund` (a penalty applied) or `cancelled` (no refund). The snake_case twin of `bookingStatus`, with the same value: use this name."
          },
          "adminCancel": {
            "type": "boolean",
            "description": "`true` when FX-Port staff cancelled the booking. Always `false` for a call of yours."
          },
          "refund": {
            "$ref": "#/components/schemas/HotelRefund"
          },
          "refundProcessed": {
            "type": "boolean",
            "description": "`true` when the refund was handled. In the sandbox nothing moves, and the flag is still `true`.",
            "deprecated": true,
            "x-replaced-by": "refund_processed"
          },
          "refund_processed": {
            "type": "boolean",
            "description": "`true` when the refund was handled. In the sandbox nothing moves, and the flag is still `true`. The snake_case twin of `refundProcessed`, with the same value: use this name."
          },
          "webhookSent": {
            "type": "boolean",
            "description": "Describes an internal step of FX-Port, not a message to you: FX-Port sends no webhooks to customers."
          },
          "processingTime": {
            "type": "number",
            "description": "Seconds the call took.",
            "deprecated": true,
            "x-replaced-by": "processing_time"
          },
          "processing_time": {
            "type": "number",
            "description": "Seconds the call took. The snake_case twin of `processingTime`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "status",
          "bookingId",
          "bookingStatus",
          "adminCancel",
          "refund",
          "refundProcessed",
          "webhookSent",
          "processingTime"
        ]
      },
      "HotelVoucherData": {
        "type": "object",
        "description": "The voucher links of a booking.",
        "properties": {
          "bookingId": {
            "type": "string",
            "description": "The id of the booking.",
            "example": "HMVXX4ZQ3",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The id of the booking. The snake_case twin of `bookingId`, with the same value: use this name.",
            "example": "HMVXX4ZQ3"
          },
          "voucherUrl": {
            "type": "string",
            "description": "The voucher PDF, with the price. A permanent public link: anyone who has it can open it.",
            "format": "uri",
            "deprecated": true,
            "x-replaced-by": "voucher_url"
          },
          "voucher_url": {
            "type": "string",
            "description": "The voucher PDF, with the price. A permanent public link: anyone who has it can open it. The snake_case twin of `voucherUrl`, with the same value: use this name.",
            "format": "uri"
          },
          "voucherUrlWithPrice": {
            "type": "string",
            "description": "The same link as `voucherUrl`.",
            "format": "uri",
            "deprecated": true,
            "x-replaced-by": "voucher_url_with_price"
          },
          "voucher_url_with_price": {
            "type": "string",
            "description": "The same link as `voucherUrl`. The snake_case twin of `voucherUrlWithPrice`, with the same value: use this name.",
            "format": "uri"
          },
          "voucherUrlNoPrice": {
            "anyOf": [
              {
                "type": "string",
                "description": "The voucher PDF without the price, to hand to the traveler. `null` when it was not made.",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "The voucher PDF without the price, to hand to the traveler. `null` when it was not made.",
            "deprecated": true,
            "x-replaced-by": "voucher_url_no_price"
          },
          "voucher_url_no_price": {
            "anyOf": [
              {
                "type": "string",
                "description": "The voucher PDF without the price, to hand to the traveler. `null` when it was not made.",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "The voucher PDF without the price, to hand to the traveler. `null` when it was not made. The snake_case twin of `voucherUrlNoPrice`, with the same value: use this name."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "confirmed",
              "failed",
              "refunded",
              "partial_refund",
              "cancelled"
            ],
            "description": "The status of the booking. A voucher stays available after a cancellation."
          }
        },
        "required": [
          "bookingId",
          "voucherUrl",
          "voucherUrlWithPrice",
          "voucherUrlNoPrice",
          "status"
        ]
      },
      "HotelVoucher": {
        "type": "object",
        "description": "The answer of `get_voucher`.",
        "properties": {
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "request_id": {
            "type": "string",
            "description": "The id of the request, also in the `X-Request-Id` header."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            },
            "description": "Present only when the request used a previous spelling of a field or something the endpoint ignored."
          },
          "success": {
            "type": "boolean",
            "description": "`true` when the voucher exists."
          },
          "data": {
            "$ref": "#/components/schemas/HotelVoucherData"
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EsimApn": {
        "type": "object",
        "description": "APN settings per operating system.",
        "properties": {
          "ios": {
            "type": "object",
            "description": "Access point name (APN) settings for one operating system.",
            "properties": {
              "apn_type": {
                "type": "string",
                "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
              },
              "apn_value": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
              }
            },
            "required": [
              "apn_type",
              "apn_value"
            ]
          },
          "android": {
            "type": "object",
            "description": "Access point name (APN) settings for one operating system.",
            "properties": {
              "apn_type": {
                "type": "string",
                "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
              },
              "apn_value": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
              }
            },
            "required": [
              "apn_type",
              "apn_value"
            ]
          }
        },
        "required": [
          "ios",
          "android"
        ]
      },
      "EsimPackage": {
        "type": "object",
        "description": "One eSIM package as sold to your agency, with the supplier's destination and operator facts flattened into `country_*` and `operator_*` keys.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Package id. Send it as `package_id` when you create an order. Always read it from the catalog: ids change when suppliers change their offer. The older name of `package_id`: same value, use `package_id`.",
            "deprecated": true,
            "x-replaced-by": "package_id"
          },
          "package_id": {
            "type": "string",
            "description": "Package id. Send it as `package_id` when you create an order. Always read it from the catalog: ids change when suppliers change their offer. The snake_case twin of `id`, with the same value: use this name."
          },
          "type": {
            "type": "string",
            "description": "Always `sim`: the package is sold as a new eSIM."
          },
          "price": {
            "type": "number",
            "description": "Base price in your agency currency, before your commission. It is not what is debited from your balance: use `balance_debit_amount`. The older name of `base_price`: same value, use `base_price`.",
            "deprecated": true,
            "x-replaced-by": "base_price"
          },
          "base_price": {
            "type": "number",
            "description": "Base price in your agency currency, before your commission. It is not what is debited from your balance: use `balance_debit_amount`. The snake_case twin of `price`, with the same value: use this name."
          },
          "amount": {
            "type": "integer",
            "description": "Data allowance in megabytes. `0` means unlimited: check `is_unlimited`."
          },
          "day": {
            "type": "integer",
            "description": "Validity in days. It starts at the first use of the eSIM or at installation, see `operator_activation_policy`."
          },
          "is_unlimited": {
            "type": "boolean",
            "description": "`true` for unlimited-data packages."
          },
          "title": {
            "type": "string",
            "description": "Label of the package: data allowance and validity, in the language of the request."
          },
          "is_fair_usage_policy": {
            "type": [
              "boolean",
              "integer"
            ],
            "description": "Whether a fair usage policy applies. A boolean on limited packages and `0` or `1` on unlimited ones: treat `0` as false and `1` as true."
          },
          "fair_usage_policy": {
            "anyOf": [
              {
                "type": "string",
                "description": "The fair usage terms, for example the speed after a daily threshold. Null when no policy applies."
              },
              {
                "type": "null"
              }
            ],
            "description": "The fair usage terms, for example the speed after a daily threshold. Null when no policy applies."
          },
          "data": {
            "type": "string",
            "description": "Data allowance as text, for example `1 GB` or `Unlimited`."
          },
          "voice": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Voice minutes included. Null for data-only packages."
              },
              {
                "type": "null"
              }
            ],
            "description": "Voice minutes included. Null for data-only packages."
          },
          "text": {
            "anyOf": [
              {
                "type": "integer",
                "description": "SMS included. Null for data-only packages."
              },
              {
                "type": "null"
              }
            ],
            "description": "SMS included. Null for data-only packages."
          },
          "country_name": {
            "type": "string",
            "description": "Destination country or region name, in the language of the request."
          },
          "country_code": {
            "type": "string",
            "description": "Destination country code (ISO 3166-1 alpha-2). An empty string for regional and worldwide bundles, whose `country_slug` is the region, for example `europe`."
          },
          "country_slug": {
            "type": "string",
            "description": "Destination slug. Send it as the `country` filter of the catalog."
          },
          "country_image_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flag or destination image, for display."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flag or destination image, for display."
          },
          "operator_name": {
            "type": "string",
            "description": "Name of the mobile network operator or brand the package runs on."
          },
          "operator_id": {
            "type": "integer",
            "description": "Identifier of the operator."
          },
          "operator_style": {
            "anyOf": [
              {
                "type": "string",
                "description": "Display hint: `light` or `dark` text on the operator's colors."
              },
              {
                "type": "null"
              }
            ],
            "description": "Display hint: `light` or `dark` text on the operator's colors."
          },
          "operator_gradient_start": {
            "anyOf": [
              {
                "type": "string",
                "description": "First color of the operator's gradient, as a hex value."
              },
              {
                "type": "null"
              }
            ],
            "description": "First color of the operator's gradient, as a hex value."
          },
          "operator_gradient_end": {
            "anyOf": [
              {
                "type": "string",
                "description": "Second color of the operator's gradient, as a hex value."
              },
              {
                "type": "null"
              }
            ],
            "description": "Second color of the operator's gradient, as a hex value."
          },
          "operator_type": {
            "type": "string",
            "description": "Scope of the package: `local` for one country, `global` for regional and worldwide bundles. There is no `regional` value.",
            "enum": [
              "local",
              "global"
            ]
          },
          "operator_is_prepaid": {
            "type": "boolean",
            "description": "Whether the underlying plan is prepaid."
          },
          "operator_esim_type": {
            "type": "string",
            "description": "Type of the plan, for example `Prepaid`."
          },
          "operator_warning": {
            "anyOf": [
              {
                "type": "string",
                "description": "A caution to show the traveler before the sale. Null when there is none."
              },
              {
                "type": "null"
              }
            ],
            "description": "A caution to show the traveler before the sale. Null when there is none."
          },
          "operator_apn_type": {
            "type": "string",
            "description": "`automatic` or `manual`: whether the APN is configured by the eSIM profile or entered by the traveler."
          },
          "operator_is_roaming": {
            "type": "boolean",
            "description": "Whether the package works through roaming rather than a local profile."
          },
          "operator_info": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A short fact about the operator or network."
            },
            "description": "Short facts about the network, the technology and whether the eSIM can be recharged."
          },
          "operator_image_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Logo of the operator."
              },
              {
                "type": "null"
              }
            ],
            "description": "Logo of the operator."
          },
          "operator_plan_type": {
            "type": "string",
            "description": "Kind of plan, for example `data` for a data-only plan."
          },
          "operator_activation_policy": {
            "type": "string",
            "description": "When validity starts, for example `first-usage`: at the first connection, not at purchase."
          },
          "operator_is_kyc_verify": {
            "type": "boolean",
            "description": "Whether the destination requires identity verification for this operator. Rare."
          },
          "operator_rechargeability": {
            "type": "boolean",
            "description": "Whether the eSIM can be topped up, see the topup guide."
          },
          "operator_other_info": {
            "anyOf": [
              {
                "type": "string",
                "description": "Longer notes on coverage and exclusions, for example territories that are not covered."
              },
              {
                "type": "null"
              }
            ],
            "description": "Longer notes on coverage and exclusions, for example territories that are not covered."
          },
          "operator_coverages": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One covered country with its networks.",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Country or area name as the supplier lists it."
                },
                "code": {
                  "type": "string",
                  "description": "Country code (ISO 3166-1 alpha-2) or area code."
                },
                "networks": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Name of the mobile network."
                      },
                      "types": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "description": "A network technology, for example `4G` or `5G`."
                        },
                        "description": "Technologies the network offers in this country."
                      }
                    },
                    "required": [
                      "name",
                      "types"
                    ]
                  },
                  "description": "Networks the eSIM can use in this country, with their technologies."
                }
              },
              "required": [
                "name",
                "code",
                "networks"
              ]
            },
            "description": "Countries covered, with their networks."
          },
          "operator_apn": {
            "anyOf": [
              {
                "type": "object",
                "description": "APN settings per operating system.",
                "properties": {
                  "ios": {
                    "type": "object",
                    "description": "Access point name (APN) settings for one operating system.",
                    "properties": {
                      "apn_type": {
                        "type": "string",
                        "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
                      },
                      "apn_value": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                      }
                    },
                    "required": [
                      "apn_type",
                      "apn_value"
                    ]
                  },
                  "android": {
                    "type": "object",
                    "description": "Access point name (APN) settings for one operating system.",
                    "properties": {
                      "apn_type": {
                        "type": "string",
                        "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
                      },
                      "apn_value": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                      }
                    },
                    "required": [
                      "apn_type",
                      "apn_value"
                    ]
                  }
                },
                "required": [
                  "ios",
                  "android"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "APN settings per operating system."
          },
          "is_regional": {
            "type": "boolean",
            "description": "Always `false`. Regional bundles are `global` packages: check `operator_type`."
          },
          "is_global": {
            "type": "boolean",
            "description": "`true` when `operator_type` is `global`."
          },
          "retail_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Equal to `total_price`."
          },
          "total_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. It includes your commission."
          },
          "balance_debit_amount": {
            "type": "number",
            "description": "The amount debited from your agency balance when you order this package. `total_price` minus this amount is your commission. The older name of `b2b_price`: same value, use `b2b_price`.",
            "deprecated": true,
            "x-replaced-by": "b2b_price"
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance when you order this package. `total_price` minus this amount is your commission. The snake_case twin of `balance_debit_amount`, with the same value: use this name."
          },
          "agency_commission": {
            "type": "string",
            "description": "Your commission rate as text, for example `12 %`; `0 %` for contracted corporate agencies. Bookings carry the commission as an amount."
          },
          "commission_rate": {
            "type": "number",
            "description": "Your commission rate as a number: `12` for a standard agency, `0` for a contracted corporate agency. Same rate as `agency_commission`."
          },
          "currency": {
            "type": "string",
            "description": "Currency of every amount on the package: the settlement currency of your agency."
          },
          "supplier_currency": {
            "type": "string",
            "description": "Currency the supplier prices the package in."
          },
          "exchange_rate": {
            "type": "number",
            "description": "Rate used to convert `supplier_currency` to your agency currency."
          }
        },
        "required": [
          "id",
          "type",
          "price",
          "amount",
          "day",
          "is_unlimited",
          "title",
          "is_fair_usage_policy",
          "fair_usage_policy",
          "data",
          "voice",
          "text",
          "country_name",
          "country_code",
          "country_slug",
          "operator_name",
          "operator_id",
          "operator_type",
          "is_regional",
          "is_global",
          "retail_price",
          "total_price",
          "balance_debit_amount",
          "agency_commission",
          "currency",
          "supplier_currency",
          "exchange_rate"
        ],
        "examples": [
          {
            "id": "france-in-7days-1gb",
            "package_id": "france-in-7days-1gb",
            "type": "sim",
            "price": 402.5,
            "base_price": 402.5,
            "amount": 1024,
            "day": 7,
            "is_unlimited": false,
            "title": "1 GB - 7 days",
            "is_fair_usage_policy": false,
            "fair_usage_policy": null,
            "data": "1 GB",
            "voice": null,
            "text": null,
            "country_name": "France",
            "country_code": "FR",
            "country_slug": "france",
            "country_image_url": "https://cdn-revamp.airalo.com/images/9753dedb-d495-47cf-b6e4-82e555564743.png",
            "operator_name": "KPN France Data only",
            "operator_id": 1704,
            "operator_style": "dark",
            "operator_gradient_start": "#F1D4FB",
            "operator_gradient_end": "#EEC0FF",
            "operator_type": "local",
            "operator_is_prepaid": false,
            "operator_esim_type": "Prepaid",
            "operator_warning": null,
            "operator_apn_type": "automatic",
            "operator_is_roaming": true,
            "operator_info": [
              "Data-only eSIM.",
              "Rechargeable online with no expiry."
            ],
            "operator_image_url": "https://cdn-revamp.airalo.com/images/01b47d41-fbb4-4032-8f5a-d1ca6a0f3688.png",
            "operator_plan_type": "data",
            "operator_activation_policy": "first-usage",
            "operator_is_kyc_verify": false,
            "operator_rechargeability": true,
            "operator_other_info": "Covers metropolitan (mainland) France and Corsica. Does not include overseas territories (Guadeloupe, Martinique, Reunion, ...",
            "operator_coverages": [
              {
                "name": "FR",
                "code": "FR",
                "networks": [
                  {
                    "name": "Orange",
                    "types": [
                      "5G"
                    ]
                  }
                ]
              }
            ],
            "operator_apn": {
              "ios": {
                "apn_type": "automatic",
                "apn_value": null
              },
              "android": {
                "apn_type": "automatic",
                "apn_value": null
              }
            },
            "is_regional": false,
            "is_global": false,
            "retail_price": 468.84,
            "total_price": 468.84,
            "balance_debit_amount": 418.6,
            "b2b_price": 418.6,
            "agency_commission": "12 %",
            "commission_rate": 12,
            "currency": "DZD",
            "supplier_currency": "USD",
            "exchange_rate": 250
          }
        ]
      },
      "EsimPackagesMeta": {
        "type": "object",
        "description": "Facts about the catalog response.",
        "properties": {
          "total_packages": {
            "type": "integer",
            "description": "Number of packages in `data`, after the filters."
          },
          "filters_applied": {
            "type": "object",
            "description": "The filters the request applied.",
            "properties": {
              "country": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The `country` filter, or null."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The `country` filter, or null."
              },
              "type": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "The `type` filter, or null."
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The `type` filter, or null."
              }
            },
            "required": [
              "country",
              "type"
            ]
          },
          "locale": {
            "type": "string",
            "description": "Language used for the text fields of this response."
          },
          "agency_currency": {
            "type": "string",
            "description": "Currency of every amount: the settlement currency of your agency. The older name of `currency`: same value, use `currency`.",
            "deprecated": true,
            "x-replaced-by": "currency"
          },
          "currency": {
            "type": "string",
            "description": "Currency of every amount: the settlement currency of your agency. The snake_case twin of `agency_currency`, with the same value: use this name."
          },
          "supplier_currency": {
            "type": "string",
            "description": "Currency the supplier prices packages in."
          },
          "exchange_rate": {
            "type": "number",
            "description": "Rate used to convert `supplier_currency` to your agency currency."
          },
          "commission_percentage": {
            "type": "string",
            "description": "Your commission rate as text, for example `12 %`."
          },
          "commission_rate": {
            "type": "number",
            "description": "Your commission rate as a number: `12` for a standard agency, `0` for a contracted corporate agency."
          }
        },
        "required": [
          "total_packages",
          "filters_applied",
          "locale",
          "agency_currency",
          "supplier_currency",
          "exchange_rate",
          "commission_percentage"
        ]
      },
      "EsimPackagesResponse": {
        "type": "object",
        "description": "The package catalog.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the catalog was returned."
          },
          "endpoint": {
            "type": "string",
            "description": "The route that answered, `/packages`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EsimPackage"
            },
            "description": "The packages that match the filters. Not paginated: filter by `country`."
          },
          "meta": {
            "$ref": "#/components/schemas/EsimPackagesMeta"
          }
        },
        "required": [
          "success",
          "data",
          "meta"
        ]
      },
      "EsimLoadingResponse": {
        "type": "object",
        "description": "The answer while the catalog or the device list is still loading after a restart. The call is not wrong: wait a few seconds and send it again.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always `false`: the data is not available yet."
          },
          "message": {
            "type": "string",
            "description": "What is missing and that it is still loading."
          },
          "data": {
            "type": "array",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "description": "Always empty."
          },
          "meta": {
            "description": "Empty for a catalog that is loading.",
            "additionalProperties": true,
            "type": "object"
          }
        },
        "required": [
          "success",
          "message",
          "data"
        ]
      },
      "EsimCompatibleDevice": {
        "type": "object",
        "description": "A device known to support eSIM.",
        "properties": {
          "model": {
            "type": "string",
            "description": "The manufacturer's model identifier."
          },
          "os": {
            "type": "string",
            "description": "Operating system family.",
            "enum": [
              "android",
              "ios"
            ]
          },
          "brand": {
            "type": "string",
            "description": "Manufacturer, as the supplier spells it (the case differs between entries)."
          },
          "name": {
            "type": "string",
            "description": "Marketing name of the device."
          }
        },
        "required": [
          "model",
          "os",
          "brand",
          "name"
        ]
      },
      "EsimCompatibleDevicesResponse": {
        "type": "object",
        "description": "The devices known to support eSIM.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the list was returned."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EsimCompatibleDevice"
            },
            "description": "The devices. About 630 entries, not paginated."
          },
          "meta": {
            "type": "object",
            "description": "Facts about the list.",
            "properties": {
              "total_devices": {
                "type": "integer",
                "description": "Number of devices in `data`."
              },
              "cached_at": {
                "type": "string",
                "description": "When the list was fetched from the supplier: the server's clock without a zone, for example `2026-11-01T15:07:43.957236`. The list is refreshed about once a week."
              },
              "cache_age_days": {
                "type": "number",
                "description": "Age of the list in days."
              }
            },
            "required": [
              "total_devices",
              "cached_at",
              "cache_age_days"
            ]
          }
        },
        "required": [
          "success",
          "data",
          "meta"
        ]
      },
      "EsimOrderRequest": {
        "type": "object",
        "description": "The body of an eSIM order. Every order creates exactly one eSIM: `quantity` and every price field are ignored.",
        "properties": {
          "package_id": {
            "type": "string",
            "description": "The `id` of a package from the catalog (its `package_id`).",
            "example": "france-in-7days-1gb",
            "maxLength": 128
          },
          "email": {
            "type": "string",
            "description": "The traveler's email address. FX-Port asks the supplier to send the QR code and a sharing link to it.",
            "example": "traveler@example.com",
            "maxLength": 254
          },
          "phone": {
            "type": "string",
            "description": "The traveler's phone number: at least 3 digits. Kept on the booking: it is not sent to the supplier.",
            "example": "+15555550123",
            "maxLength": 32
          },
          "first_name": {
            "type": "string",
            "description": "The traveler's first name. Any characters are accepted. Kept on the booking only.",
            "maxLength": 100,
            "example": "John"
          },
          "last_name": {
            "type": "string",
            "description": "The traveler's last name. Any characters are accepted. Kept on the booking only.",
            "maxLength": 100,
            "example": "Smith"
          },
          "description": {
            "type": "string",
            "description": "A note for your own records. It is sent to the supplier with the order and returned as `order.description`.",
            "maxLength": 500,
            "example": "Order for John Smith"
          },
          "agent_id": {
            "type": "string",
            "description": "The id of the user of your agency who places the order. Optional: it appears on the booking as `agent_id`. A user of another agency is refused.",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "description": "Previous name of `agent_id`.",
            "format": "uuid",
            "deprecated": true,
            "x-replaced-by": "agent_id"
          },
          "customer_email": {
            "type": "string",
            "description": "The name the booking rows use for `email`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "email"
          },
          "customer_phone": {
            "type": "string",
            "description": "The name the booking rows use for `phone`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "phone"
          },
          "customer_first_name": {
            "type": "string",
            "description": "The name the booking rows use for `first_name`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "customer_last_name": {
            "type": "string",
            "description": "The name the booking rows use for `last_name`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          }
        },
        "required": [
          "package_id",
          "email",
          "phone"
        ]
      },
      "EsimOrder": {
        "type": "object",
        "description": "The order, with the amounts in your agency currency.",
        "properties": {
          "code": {
            "type": "string",
            "description": "The supplier's order code, for example `20261101-106852`. Booking rows call it `order_id`. The older name of `order_id`: same value, use `order_id`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order code, for example `20261101-106852`. Booking rows call it `order_id`. The snake_case twin of `code`, with the same value: use this name."
          },
          "package_id": {
            "type": "string",
            "description": "The package that was ordered."
          },
          "type": {
            "type": "string",
            "description": "Always `sim`."
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "description": "The `description` you sent."
              },
              {
                "type": "null"
              }
            ],
            "description": "The `description` you sent."
          },
          "esim_type": {
            "type": "string",
            "description": "Kind of eSIM plan, for example `local`."
          },
          "created_at": {
            "type": "string",
            "description": "When the supplier created the order: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
          },
          "package": {
            "type": "string",
            "description": "Name of the package: operator, data allowance and validity."
          },
          "data": {
            "type": "string",
            "description": "Data allowance as text, for example `1 GB`."
          },
          "validity": {
            "type": "integer",
            "description": "Validity in days."
          },
          "price": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency. Equal to `total_price` here (in the catalog, `price` is a different amount: see the pricing guide)."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amounts: the settlement currency of your agency."
          },
          "total_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Your commission is `total_price` minus the amount debited from your balance."
          },
          "retail_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Your commission is `total_price` minus the amount debited from your balance. Another name of `total_price`, with the same value."
          },
          "agency_currency": {
            "type": "string",
            "description": "Same as `currency`."
          },
          "supplier_currency": {
            "type": "string",
            "description": "Currency the supplier prices the package in."
          },
          "exchange_rate": {
            "type": "number",
            "description": "Rate used to convert `supplier_currency` to your agency currency."
          },
          "voice": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Voice minutes included, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Voice minutes included, or null."
          },
          "text": {
            "anyOf": [
              {
                "type": "integer",
                "description": "SMS included, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "SMS included, or null."
          },
          "coverage": {
            "type": "string",
            "description": "Coverage text taken from the installation instructions. Can be an empty string."
          }
        },
        "required": [
          "code",
          "package_id",
          "type",
          "validity",
          "price",
          "currency",
          "total_price",
          "agency_currency",
          "supplier_currency",
          "exchange_rate"
        ]
      },
      "EsimSim": {
        "type": "object",
        "description": "The eSIM that was created. Treat the activation data as a secret of the traveler.",
        "properties": {
          "iccid": {
            "type": "string",
            "description": "The ICCID of the eSIM. Keep it: usage, topups and instructions are asked by ICCID."
          },
          "created_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "When the supplier created the eSIM: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
              },
              {
                "type": "null"
              }
            ],
            "description": "When the supplier created the eSIM: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
          },
          "lpa": {
            "anyOf": [
              {
                "type": "string",
                "description": "Host of the SM-DP+ server (the LPA address)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Host of the SM-DP+ server (the LPA address)."
          },
          "imsis": {
            "anyOf": [
              {
                "type": "string",
                "description": "IMSI of the profile, when the supplier returns it."
              },
              {
                "type": "null"
              }
            ],
            "description": "IMSI of the profile, when the supplier returns it."
          },
          "qrcode": {
            "anyOf": [
              {
                "type": "string",
                "description": "The activation string the QR code encodes: `LPA:1$<host>$<activation code>`. Sensitive: show it only to the traveler."
              },
              {
                "type": "null"
              }
            ],
            "description": "The activation string the QR code encodes: `LPA:1$<host>$<activation code>`. Sensitive: show it only to the traveler."
          },
          "qrcode_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Link to the QR code image."
              },
              {
                "type": "null"
              }
            ],
            "description": "Link to the QR code image."
          },
          "direct_apple_installation_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Link that starts the installation on an iPhone."
              },
              {
                "type": "null"
              }
            ],
            "description": "Link that starts the installation on an iPhone."
          },
          "apn_type": {
            "anyOf": [
              {
                "type": "string",
                "description": "`automatic` or `manual`."
              },
              {
                "type": "null"
              }
            ],
            "description": "`automatic` or `manual`."
          },
          "apn_value": {
            "anyOf": [
              {
                "type": "string",
                "description": "The APN to enter on a manual setup, or `N/A`."
              },
              {
                "type": "null"
              }
            ],
            "description": "The APN to enter on a manual setup, or `N/A`."
          },
          "apn": {
            "anyOf": [
              {
                "type": "object",
                "description": "APN settings per operating system.",
                "properties": {
                  "ios": {
                    "type": "object",
                    "description": "Access point name (APN) settings for one operating system.",
                    "properties": {
                      "apn_type": {
                        "type": "string",
                        "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
                      },
                      "apn_value": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                      }
                    },
                    "required": [
                      "apn_type",
                      "apn_value"
                    ]
                  },
                  "android": {
                    "type": "object",
                    "description": "Access point name (APN) settings for one operating system.",
                    "properties": {
                      "apn_type": {
                        "type": "string",
                        "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
                      },
                      "apn_value": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                      }
                    },
                    "required": [
                      "apn_type",
                      "apn_value"
                    ]
                  }
                },
                "required": [
                  "ios",
                  "android"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "APN settings per operating system."
          },
          "is_roaming": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Whether the eSIM works through roaming."
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether the eSIM works through roaming."
          },
          "confirmation_code": {
            "anyOf": [
              {
                "type": "string",
                "description": "Confirmation code to enter at installation, when the operator requires one."
              },
              {
                "type": "null"
              }
            ],
            "description": "Confirmation code to enter at installation, when the operator requires one."
          },
          "msisdn": {
            "anyOf": [
              {
                "type": "string",
                "description": "Phone number of the eSIM, for the rare plans that include one."
              },
              {
                "type": "null"
              }
            ],
            "description": "Phone number of the eSIM, for the rare plans that include one."
          },
          "sharing": {
            "anyOf": [
              {
                "type": "object",
                "description": "A shareable installation page for the eSIM.",
                "properties": {
                  "access_code": {
                    "type": "number",
                    "description": "Code the traveler enters on the sharing page."
                  },
                  "link": {
                    "type": "string",
                    "description": "Page where the traveler can open the eSIM details and QR code."
                  }
                },
                "required": [
                  "access_code",
                  "link"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A shareable installation page for the eSIM."
          }
        },
        "required": [
          "iccid"
        ]
      },
      "EsimOrderData": {
        "type": "object",
        "description": "The order and its eSIM. `sim` is null when the supplier has not returned the eSIM yet: ask for the setup instructions later.",
        "properties": {
          "order": {
            "$ref": "#/components/schemas/EsimOrder"
          },
          "sim": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EsimSim"
              },
              {
                "type": "null"
              }
            ]
          },
          "iccid": {
            "type": "string",
            "description": "The ICCID of the eSIM: the same value as `sim.iccid`. When the supplier has not returned the eSIM yet it is the placeholder `PENDING-` followed by the order code."
          }
        },
        "required": [
          "order",
          "sim"
        ]
      },
      "EsimOrderResponse": {
        "type": "object",
        "description": "The result of an order.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the order was placed."
          },
          "data": {
            "$ref": "#/components/schemas/EsimOrderData"
          },
          "booking_id": {
            "type": "string",
            "description": "The FX-Port booking reference, `FX-` followed by a number. Keep it: it is the key of the booking in every other endpoint."
          },
          "message": {
            "type": "string",
            "description": "A confirmation sentence."
          },
          "balanceDebited": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance was debited. The older name of `balance_debited`: same value, use `balance_debited`.",
            "deprecated": true,
            "x-replaced-by": "balance_debited"
          },
          "balance_debited": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance was debited. The snake_case twin of `balanceDebited`, with the same value: use this name."
          },
          "transactionCreated": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance transaction was created. The older name of `transaction_created`: same value, use `transaction_created`.",
            "deprecated": true,
            "x-replaced-by": "transaction_created"
          },
          "transaction_created": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance transaction was created. The snake_case twin of `transactionCreated`, with the same value: use this name."
          },
          "payoutCreated": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no supplier payout was created. The older name of `payout_created`: same value, use `payout_created`.",
            "deprecated": true,
            "x-replaced-by": "payout_created"
          },
          "payout_created": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no supplier payout was created. The snake_case twin of `payoutCreated`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "data",
          "booking_id",
          "message"
        ]
      },
      "EsimTopupRequest": {
        "type": "object",
        "description": "The body of an eSIM topup.",
        "properties": {
          "package_id": {
            "type": "string",
            "description": "The `id` of a topup package returned for this ICCID by the topup packages endpoint (its `package_id`).",
            "example": "france-in-7days-1gb-topup",
            "maxLength": 128
          },
          "iccid": {
            "type": "string",
            "description": "The ICCID of the eSIM to top up: 18 to 22 digits, which may be written in groups of spaces or hyphens (`8944 4654 0000 0123 456`). It must be an eSIM that your agency ordered.",
            "example": "8900000540463077957"
          },
          "email": {
            "type": "string",
            "description": "The traveler's email address. Kept on the booking: it is not sent to the supplier on a topup.",
            "example": "traveler@example.com",
            "maxLength": 254
          },
          "phone": {
            "type": "string",
            "description": "The traveler's phone number: at least 3 digits. Kept on the booking.",
            "example": "+15555550123",
            "maxLength": 32
          },
          "first_name": {
            "type": "string",
            "description": "The traveler's first name. Any characters are accepted.",
            "maxLength": 100,
            "example": "John"
          },
          "last_name": {
            "type": "string",
            "description": "The traveler's last name. Any characters are accepted.",
            "maxLength": 100,
            "example": "Smith"
          },
          "agent_id": {
            "type": "string",
            "description": "The id of the user of your agency who places the topup. Optional. A user of another agency is refused.",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "description": "Previous name of `agent_id`.",
            "format": "uuid",
            "deprecated": true,
            "x-replaced-by": "agent_id"
          },
          "customer_email": {
            "type": "string",
            "description": "The name the booking rows use for `email`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "email"
          },
          "customer_phone": {
            "type": "string",
            "description": "The name the booking rows use for `phone`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "phone"
          },
          "customer_first_name": {
            "type": "string",
            "description": "The name the booking rows use for `first_name`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "first_name"
          },
          "customer_last_name": {
            "type": "string",
            "description": "The name the booking rows use for `last_name`. Still accepted.",
            "deprecated": true,
            "x-replaced-by": "last_name"
          }
        },
        "required": [
          "package_id",
          "iccid",
          "email",
          "phone"
        ]
      },
      "EsimTopup": {
        "type": "object",
        "description": "The topup, with the amounts in your agency currency.",
        "properties": {
          "code": {
            "type": "string",
            "description": "The supplier's order code of the topup. Booking rows call it `order_id`. The older name of `order_id`: same value, use `order_id`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order code of the topup. Booking rows call it `order_id`. The snake_case twin of `code`, with the same value: use this name."
          },
          "package_id": {
            "type": "string",
            "description": "The topup package that was bought."
          },
          "package": {
            "type": "string",
            "description": "Name of the topup package."
          },
          "data": {
            "type": "string",
            "description": "Data allowance added, as text."
          },
          "validity": {
            "type": "integer",
            "description": "Validity of the topup in days."
          },
          "price": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency. Equal to `total_price` here (in the catalog, `price` is a different amount: see the pricing guide)."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amounts: the settlement currency of your agency."
          },
          "total_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Your commission is `total_price` minus the amount debited from your balance."
          },
          "retail_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Your commission is `total_price` minus the amount debited from your balance. Another name of `total_price`, with the same value."
          },
          "agency_currency": {
            "type": "string",
            "description": "Same as `currency`."
          },
          "supplier_currency": {
            "type": "string",
            "description": "Currency the supplier prices the package in."
          },
          "exchange_rate": {
            "type": "number",
            "description": "Rate used to convert `supplier_currency` to your agency currency."
          }
        },
        "required": [
          "code",
          "validity",
          "price",
          "currency",
          "total_price",
          "agency_currency",
          "supplier_currency",
          "exchange_rate"
        ]
      },
      "EsimBalanceChange": {
        "type": "object",
        "description": "The agency balance around the topup.",
        "properties": {
          "previous": {
            "type": "number",
            "description": "Agency balance in this currency before the topup. Always `0.0` in the sandbox."
          },
          "current": {
            "type": "number",
            "description": "Agency balance after the topup. Always `0.0` in the sandbox."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the balance."
          }
        },
        "required": [
          "previous",
          "current",
          "currency"
        ]
      },
      "EsimTopupData": {
        "type": "object",
        "description": "The topup, the ICCID and the balance change.",
        "properties": {
          "topup": {
            "$ref": "#/components/schemas/EsimTopup"
          },
          "iccid": {
            "type": "string",
            "description": "The ICCID that was topped up."
          },
          "balance": {
            "$ref": "#/components/schemas/EsimBalanceChange"
          }
        },
        "required": [
          "topup",
          "iccid",
          "balance"
        ]
      },
      "EsimTopupResponse": {
        "type": "object",
        "description": "The result of a topup.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the topup was applied."
          },
          "data": {
            "$ref": "#/components/schemas/EsimTopupData"
          },
          "booking_id": {
            "type": "string",
            "description": "The FX-Port booking reference of the topup, `FX-` followed by a number."
          },
          "message": {
            "type": "string",
            "description": "A confirmation sentence."
          },
          "balanceDebited": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance was debited. The older name of `balance_debited`: same value, use `balance_debited`.",
            "deprecated": true,
            "x-replaced-by": "balance_debited"
          },
          "balance_debited": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance was debited. The snake_case twin of `balanceDebited`, with the same value: use this name."
          },
          "transactionCreated": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance transaction was created. The older name of `transaction_created`: same value, use `transaction_created`.",
            "deprecated": true,
            "x-replaced-by": "transaction_created"
          },
          "transaction_created": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no balance transaction was created. The snake_case twin of `transactionCreated`, with the same value: use this name."
          },
          "payoutCreated": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no supplier payout was created. The older name of `payout_created`: same value, use `payout_created`.",
            "deprecated": true,
            "x-replaced-by": "payout_created"
          },
          "payout_created": {
            "type": "boolean",
            "description": "Sandbox only, always `false`: no supplier payout was created. The snake_case twin of `payoutCreated`, with the same value: use this name."
          }
        },
        "required": [
          "success",
          "data",
          "booking_id",
          "message"
        ]
      },
      "EsimTopupPackage": {
        "type": "object",
        "description": "One package that can top up the eSIM.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Topup package id. Send it as `package_id` when you create the topup. The older name of `package_id`: same value, use `package_id`.",
            "deprecated": true,
            "x-replaced-by": "package_id"
          },
          "package_id": {
            "type": "string",
            "description": "Topup package id. Send it as `package_id` when you create the topup. The snake_case twin of `id`, with the same value: use this name."
          },
          "type": {
            "type": "string",
            "description": "Always `topup`."
          },
          "price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Same as `total_price`. Here `price` is the retail amount, unlike the catalog."
          },
          "amount": {
            "type": "integer",
            "description": "Data allowance in megabytes. `0` means unlimited."
          },
          "day": {
            "type": "integer",
            "description": "Validity in days."
          },
          "is_unlimited": {
            "type": "boolean",
            "description": "`true` for unlimited-data packages."
          },
          "title": {
            "type": "string",
            "description": "Label of the package, in the language of the request."
          },
          "data": {
            "type": "string",
            "description": "Data allowance as text, for example `1 GB`."
          },
          "short_info": {
            "anyOf": [
              {
                "type": "string",
                "description": "A short note about the package. Usually null."
              },
              {
                "type": "null"
              }
            ],
            "description": "A short note about the package. Usually null."
          },
          "voice": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Voice minutes included, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Voice minutes included, or null."
          },
          "text": {
            "anyOf": [
              {
                "type": "integer",
                "description": "SMS included, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "SMS included, or null."
          },
          "prices": {
            "type": "object",
            "description": "Reference prices published by the supplier.",
            "properties": {
              "recommended_retail_price": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                },
                "description": "The supplier's recommended retail price in many currencies, by currency code. For reference only: it is not what you are charged."
              }
            }
          },
          "base_price": {
            "type": "number",
            "description": "Base price in your agency currency, before your commission. Same meaning as `price` in the catalog."
          },
          "commission": {
            "type": "number",
            "description": "Your commission as an amount, in your agency currency."
          },
          "total_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency."
          },
          "retail_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Another name of `total_price`, with the same value."
          },
          "balance_debit_amount": {
            "type": "number",
            "description": "The amount debited from your agency balance when you create this topup. The older name of `b2b_price`: same value, use `b2b_price`.",
            "deprecated": true,
            "x-replaced-by": "b2b_price"
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance when you create this topup. The snake_case twin of `balance_debit_amount`, with the same value: use this name."
          },
          "agency_commission": {
            "type": "string",
            "description": "Your commission rate as text, for example `12 %`."
          },
          "commission_rate": {
            "type": "number",
            "description": "Your commission rate as a number: `12` for a standard agency, `0` for a contracted corporate agency."
          },
          "currency": {
            "type": "string",
            "description": "Currency of every amount: the settlement currency of your agency."
          },
          "supplier_currency": {
            "type": "string",
            "description": "Currency the supplier prices the package in."
          }
        },
        "required": [
          "id",
          "type",
          "price",
          "amount",
          "day",
          "is_unlimited",
          "title",
          "data",
          "base_price",
          "commission",
          "total_price",
          "balance_debit_amount",
          "agency_commission",
          "currency",
          "supplier_currency"
        ]
      },
      "EsimTopupPackagesResponse": {
        "type": "object",
        "description": "The topup packages of one eSIM.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the list was returned."
          },
          "data": {
            "type": "object",
            "description": "Wrapper object: the list is `data.data`.",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/EsimTopupPackage"
                },
                "description": "The packages that can top up this eSIM. Empty when it cannot be topped up."
              }
            },
            "required": [
              "data"
            ]
          },
          "cached": {
            "type": "boolean",
            "description": "`true` when the list comes from the five-minute cache. Absent on a fresh answer."
          },
          "cache_age_seconds": {
            "type": "integer",
            "description": "Age of the cached list in seconds. Present with `cached`."
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EsimUsage": {
        "type": "object",
        "description": "The usage of one eSIM.",
        "properties": {
          "remaining": {
            "type": "integer",
            "description": "Data left, in megabytes."
          },
          "total": {
            "type": "integer",
            "description": "Data allowance of the eSIM, in megabytes."
          },
          "expired_at": {
            "type": "string",
            "description": "When the eSIM expires: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
          },
          "is_unlimited": {
            "type": "boolean",
            "description": "`true` for an unlimited eSIM."
          },
          "status": {
            "type": "string",
            "description": "State of the eSIM, as the supplier reports it. One of `ACTIVE`, `EXPIRED`, `NOT_ACTIVE`, `FINISHED`."
          },
          "remaining_voice": {
            "type": "integer",
            "description": "Voice minutes left."
          },
          "remaining_text": {
            "type": "integer",
            "description": "SMS left."
          },
          "total_voice": {
            "type": "integer",
            "description": "Voice minutes of the allowance."
          },
          "total_text": {
            "type": "integer",
            "description": "SMS of the allowance."
          }
        },
        "required": [
          "remaining",
          "total",
          "expired_at",
          "is_unlimited",
          "status",
          "remaining_voice",
          "remaining_text",
          "total_voice",
          "total_text"
        ]
      },
      "EsimUsageEnvelope": {
        "type": "object",
        "description": "The usage and the supplier's status line.",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/EsimUsage"
          },
          "meta": {
            "type": "object",
            "description": "The supplier's status line.",
            "properties": {
              "message": {
                "type": "string",
                "description": "`success` when the supplier answered."
              }
            },
            "required": [
              "message"
            ]
          }
        },
        "required": [
          "data",
          "meta"
        ]
      },
      "EsimUsageResponse": {
        "type": "object",
        "description": "The usage of one eSIM.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when usage is returned."
          },
          "data": {
            "$ref": "#/components/schemas/EsimUsageEnvelope"
          },
          "cached": {
            "type": "boolean",
            "description": "`true` when the usage comes from the 15-minute cache or is a last known value. Absent on a fresh answer."
          },
          "cache_age_seconds": {
            "type": "integer",
            "description": "Age of the cached usage in seconds. Present with `cached` when the value is within the cache window."
          },
          "stale": {
            "type": "boolean",
            "description": "`true` when the supplier could not answer and the last known usage is returned instead."
          },
          "stale_reason": {
            "type": "string",
            "description": "Why the supplier could not answer. Present with `stale`."
          },
          "retry_after": {
            "type": "integer",
            "description": "Seconds to wait before asking the supplier again. Present with `stale` when the supplier was rate limited."
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EsimUsageBulkItem": {
        "type": "object",
        "description": "The usage of one eSIM in a bulk answer.",
        "properties": {
          "iccid": {
            "type": "string",
            "description": "The ICCID. Each ICCID appears once."
          },
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EsimUsageEnvelope"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string",
            "description": "`success` fresh or cached, `stale` last known value, `rate_limited` the supplier refused (see `retry_after`), `error` (see `error`), or `PENDING` when the order has no eSIM yet (`data` is null).",
            "enum": [
              "success",
              "stale",
              "rate_limited",
              "error",
              "PENDING"
            ]
          },
          "retry_after": {
            "type": "integer",
            "description": "Seconds to wait before asking again. Present when `status` is `rate_limited`."
          },
          "error": {
            "type": "string",
            "description": "A short reason code. Present when `status` is `error` after an unexpected failure."
          }
        },
        "required": [
          "iccid",
          "data",
          "status"
        ]
      },
      "EsimUsageBulkResponse": {
        "type": "object",
        "description": "The usage of several eSIMs.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the list was built, even if single entries failed: read `status` per entry."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EsimUsageBulkItem"
            },
            "description": "One entry per eSIM of your agency."
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EsimPagination": {
        "type": "object",
        "description": "Where this page sits in the list.",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Number of eSIM bookings of your agency in this environment that match the request (`search` included)."
          },
          "page": {
            "type": "integer",
            "description": "The page returned."
          },
          "limit": {
            "type": "integer",
            "description": "The page size used. With `all=true` for the dashboard it equals `total`."
          },
          "pages": {
            "type": "integer",
            "description": "Number of pages: `total` divided by `limit`, rounded up. `0` when there are no bookings.",
            "deprecated": true,
            "x-replaced-by": "total_pages"
          },
          "total_pages": {
            "type": "integer",
            "description": "Number of pages: `total` divided by `limit`, rounded up. `0` when there are no bookings."
          },
          "has_more": {
            "type": "boolean",
            "description": "`true` when another page follows."
          }
        },
        "required": [
          "total",
          "page",
          "limit",
          "pages"
        ]
      },
      "EsimBooking": {
        "type": "object",
        "description": "One eSIM order or topup of your agency.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Internal id of the booking row (UUID). It is the `id` the account history returns.",
            "format": "uuid"
          },
          "booking_id": {
            "type": "string",
            "description": "The FX-Port booking reference, `FX-` followed by a number. Use it to look the booking up."
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order code of the eSIM or of the topup."
          },
          "status": {
            "type": "string",
            "description": "State of the booking: `confirmed`, or `failed` when the supplier refused the order (those rows have an ICCID that starts with `PENDING-`).",
            "enum": [
              "confirmed",
              "failed"
            ]
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amounts: the settlement currency of your agency."
          },
          "total_price": {
            "type": "number",
            "description": "The price to charge the traveler."
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission as an amount, in your agency currency. The amount debited from your balance is `total_price` minus this."
          },
          "type": {
            "type": "string",
            "description": "`esim` for an order, `topup` for a topup.",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "package_name": {
            "type": "string",
            "description": "Name of the package, with the destination for an eSIM: `1 GB - 7 days • France`."
          },
          "iccid": {
            "type": "string",
            "description": "The ICCID of the eSIM. A topup row carries the ICCID that was topped up."
          },
          "customer_email": {
            "type": "string",
            "description": "The traveler's email address."
          },
          "customer_first_name": {
            "type": "string",
            "description": "The traveler's first name."
          },
          "customer_last_name": {
            "type": "string",
            "description": "The traveler's last name."
          },
          "customer_phone": {
            "type": "string",
            "description": "The traveler's phone number."
          },
          "provider_name": {
            "type": "string",
            "description": "Name of the supplier that fulfilled the booking."
          },
          "supplier_id": {
            "type": "string",
            "description": "Id of the supplier that fulfilled the booking. Here `airalo`: the supplier list calls the same supplier `airalo_1`."
          },
          "created_at": {
            "type": "string",
            "description": "When the booking was created: ISO 8601, UTC."
          },
          "updated_at": {
            "type": "string",
            "description": "When the booking last changed: ISO 8601, UTC."
          },
          "environment": {
            "type": "string",
            "description": "The environment of the booking.",
            "enum": [
              "sandbox",
              "live"
            ]
          }
        },
        "required": [
          "id",
          "booking_id",
          "order_id",
          "status",
          "currency",
          "total_price",
          "agency_commission",
          "type",
          "package_name",
          "iccid",
          "customer_email",
          "customer_first_name",
          "customer_last_name",
          "customer_phone",
          "provider_name",
          "supplier_id",
          "created_at",
          "updated_at",
          "environment"
        ],
        "examples": [
          {
            "id": "639417b4-b103-4ece-bd5d-850315322278",
            "booking_id": "FX-1793546602916",
            "order_id": "20261101-106852",
            "status": "confirmed",
            "currency": "DZD",
            "total_price": 468.84,
            "agency_commission": 50.24,
            "type": "esim",
            "package_name": "1 GB - 7 days • France",
            "iccid": "8900000540463077957",
            "customer_email": "traveler@example.com",
            "customer_first_name": "John",
            "customer_last_name": "Smith",
            "customer_phone": "+15555550123",
            "provider_name": "Airalo",
            "supplier_id": "airalo",
            "created_at": "2026-11-01T15:23:22.961228+00:00",
            "updated_at": "2026-11-01T15:23:22.961228+00:00",
            "environment": "sandbox"
          }
        ]
      },
      "EsimBookingsResponse": {
        "type": "object",
        "description": "A page of eSIM bookings.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the list was returned."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EsimBooking"
            },
            "description": "The eSIM orders of this page, newest first. Topups are not listed here."
          },
          "pagination": {
            "$ref": "#/components/schemas/EsimPagination"
          }
        },
        "required": [
          "success",
          "data",
          "pagination"
        ]
      },
      "EsimTopupHistoryResponse": {
        "type": "object",
        "description": "The topups of one eSIM.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the list was returned."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EsimBooking"
            },
            "description": "The topups of this ICCID, newest first. Empty when there are none or the ICCID is not yours."
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EsimBookingOwnership": {
        "type": "object",
        "description": "Whether a booking is yours. This endpoint has no `success` key.",
        "properties": {
          "isOwner": {
            "type": "boolean",
            "description": "`true` when the booking is an eSIM booking of your agency in this environment. `false` when it belongs to another agency or does not exist: the two cases are not told apart. The older name of `is_owner`: same value, use `is_owner`.",
            "deprecated": true,
            "x-replaced-by": "is_owner"
          },
          "is_owner": {
            "type": "boolean",
            "description": "`true` when the booking is an eSIM booking of your agency in this environment. `false` when it belongs to another agency or does not exist: the two cases are not told apart. The snake_case twin of `isOwner`, with the same value: use this name."
          },
          "userAgencyId": {
            "type": "string",
            "description": "Your agency id. Present only when `isOwner` is `true`.",
            "format": "uuid"
          },
          "bookingAgencyId": {
            "type": "string",
            "description": "The agency id of the booking, which is your own agency id. Present only when `isOwner` is `true`.",
            "format": "uuid"
          }
        },
        "required": [
          "isOwner"
        ]
      },
      "EsimSetupSim": {
        "type": "object",
        "description": "The eSIM of the booking with its activation data.",
        "properties": {
          "created_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "When the supplier created the eSIM: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
              },
              {
                "type": "null"
              }
            ],
            "description": "When the supplier created the eSIM: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
          },
          "iccid": {
            "type": "string",
            "description": "The ICCID of the eSIM."
          },
          "lpa": {
            "anyOf": [
              {
                "type": "string",
                "description": "Host of the SM-DP+ server (the LPA address)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Host of the SM-DP+ server (the LPA address)."
          },
          "imsis": {
            "anyOf": [
              {
                "type": "string",
                "description": "IMSI of the profile, when the supplier returns it."
              },
              {
                "type": "null"
              }
            ],
            "description": "IMSI of the profile, when the supplier returns it."
          },
          "qrcode": {
            "anyOf": [
              {
                "type": "string",
                "description": "The activation string the QR code encodes. Sensitive: show it only to the traveler."
              },
              {
                "type": "null"
              }
            ],
            "description": "The activation string the QR code encodes. Sensitive: show it only to the traveler."
          },
          "qrcode_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Link to the QR code image."
              },
              {
                "type": "null"
              }
            ],
            "description": "Link to the QR code image."
          },
          "apn_type": {
            "anyOf": [
              {
                "type": "string",
                "description": "`automatic` or `manual`."
              },
              {
                "type": "null"
              }
            ],
            "description": "`automatic` or `manual`."
          },
          "apn_value": {
            "anyOf": [
              {
                "type": "string",
                "description": "The APN to enter on a manual setup, or `N/A`."
              },
              {
                "type": "null"
              }
            ],
            "description": "The APN to enter on a manual setup, or `N/A`."
          },
          "is_roaming": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Whether the eSIM works through roaming."
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether the eSIM works through roaming."
          },
          "confirmation_code": {
            "anyOf": [
              {
                "type": "string",
                "description": "Confirmation code to enter at installation, when required."
              },
              {
                "type": "null"
              }
            ],
            "description": "Confirmation code to enter at installation, when required."
          },
          "apn": {
            "anyOf": [
              {
                "type": "object",
                "description": "APN settings per operating system.",
                "properties": {
                  "ios": {
                    "type": "object",
                    "description": "Access point name (APN) settings for one operating system.",
                    "properties": {
                      "apn_type": {
                        "type": "string",
                        "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
                      },
                      "apn_value": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                      }
                    },
                    "required": [
                      "apn_type",
                      "apn_value"
                    ]
                  },
                  "android": {
                    "type": "object",
                    "description": "Access point name (APN) settings for one operating system.",
                    "properties": {
                      "apn_type": {
                        "type": "string",
                        "description": "`automatic` when the device configures the access point name by itself, `manual` when the traveler has to enter it."
                      },
                      "apn_value": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The access point name to enter on a manual setup; null or `N/A` when none is needed."
                      }
                    },
                    "required": [
                      "apn_type",
                      "apn_value"
                    ]
                  }
                },
                "required": [
                  "ios",
                  "android"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "APN settings per operating system."
          },
          "msisdn": {
            "anyOf": [
              {
                "type": "string",
                "description": "Phone number of the eSIM, for the rare plans that include one."
              },
              {
                "type": "null"
              }
            ],
            "description": "Phone number of the eSIM, for the rare plans that include one."
          },
          "direct_apple_installation_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Link that starts the installation on an iPhone."
              },
              {
                "type": "null"
              }
            ],
            "description": "Link that starts the installation on an iPhone."
          },
          "direct_android_installation_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Link that starts the installation on an Android phone that supports it."
              },
              {
                "type": "null"
              }
            ],
            "description": "Link that starts the installation on an Android phone that supports it."
          },
          "sharing": {
            "anyOf": [
              {
                "type": "object",
                "description": "A shareable installation page for the eSIM.",
                "properties": {
                  "access_code": {
                    "type": "number",
                    "description": "Code the traveler enters on the sharing page."
                  },
                  "link": {
                    "type": "string",
                    "description": "Page where the traveler can open the eSIM details and QR code."
                  }
                },
                "required": [
                  "access_code",
                  "link"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A shareable installation page for the eSIM."
          }
        },
        "required": [
          "iccid"
        ]
      },
      "EsimSetupInstructions": {
        "type": "object",
        "description": "The original order of a booking: the eSIM with its activation data and the installation steps.",
        "properties": {
          "code": {
            "type": "string",
            "description": "The supplier's order code. The older name of `order_id`: same value, use `order_id`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "The supplier's order code. The snake_case twin of `code`, with the same value: use this name."
          },
          "currency": {
            "type": "string",
            "description": "Currency of `price`: your agency currency."
          },
          "package_id": {
            "type": "string",
            "description": "The package that was ordered."
          },
          "quantity": {
            "type": "integer",
            "description": "Always `1`."
          },
          "type": {
            "type": "string",
            "description": "Always `sim`."
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "description": "The `description` of the order."
              },
              {
                "type": "null"
              }
            ],
            "description": "The `description` of the order."
          },
          "esim_type": {
            "type": "string",
            "description": "Kind of eSIM plan, for example `local`."
          },
          "validity": {
            "type": "integer",
            "description": "Validity in days."
          },
          "package": {
            "type": "string",
            "description": "Name of the package: operator, data allowance and validity."
          },
          "data": {
            "type": "string",
            "description": "Data allowance as text."
          },
          "price": {
            "type": "number",
            "description": "The price charged to the traveler, in your agency currency. Same as `total_price`."
          },
          "created_at": {
            "type": "string",
            "description": "When the supplier created the order: `YYYY-MM-DD HH:MM:SS`, UTC, without a zone."
          },
          "manual_installation": {
            "type": "string",
            "description": "HTML with the steps to install the eSIM by entering the activation data by hand. Show it as HTML or strip the tags."
          },
          "qrcode_installation": {
            "type": "string",
            "description": "HTML with the steps to install the eSIM by scanning the QR code."
          },
          "installation_guides": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Links to the supplier's general installation guide, by language code."
          },
          "text": {
            "anyOf": [
              {
                "type": "integer",
                "description": "SMS included, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "SMS included, or null."
          },
          "voice": {
            "anyOf": [
              {
                "type": "integer",
                "description": "Voice minutes included, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Voice minutes included, or null."
          },
          "sims": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EsimSetupSim"
            },
            "description": "The eSIMs of the order. One for an eSIM order."
          },
          "total_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency."
          },
          "retail_price": {
            "type": "number",
            "description": "The price to charge the traveler, in your agency currency. Another name of `total_price`, with the same value."
          },
          "agency_currency": {
            "type": "string",
            "description": "Same as `currency`."
          }
        },
        "required": [
          "code",
          "currency",
          "package_id",
          "type",
          "validity",
          "package",
          "data",
          "price",
          "sims",
          "total_price",
          "agency_currency"
        ]
      },
      "EsimSetupInstructionsResponse": {
        "type": "object",
        "description": "The setup data of a booking.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the data was returned."
          },
          "data": {
            "$ref": "#/components/schemas/EsimSetupInstructions"
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "EsimInstallationViaQrCode": {
        "type": "object",
        "description": "Steps to install the eSIM by scanning its QR code.",
        "properties": {
          "steps": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The steps as text, keyed by their number: `\"1\"`, `\"2\"`, ..."
          },
          "qr_code_data": {
            "type": "string",
            "description": "The activation string the QR code encodes: `LPA:1$<host>$<activation code>`."
          },
          "qr_code_url": {
            "type": "string",
            "description": "Link to the QR code image."
          }
        },
        "required": [
          "steps"
        ]
      },
      "EsimInstallationManual": {
        "type": "object",
        "description": "Steps to install the eSIM by entering its activation data by hand.",
        "properties": {
          "steps": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The steps as text, keyed by their number: `\"1\"`, `\"2\"`, ..."
          },
          "smdp_address_and_activation_code": {
            "type": "string",
            "description": "The SM-DP+ address and the activation code in one string: `LPA:1$<host>$<activation code>`."
          },
          "smdp_address": {
            "type": "string",
            "description": "The SM-DP+ address on its own. iOS entries only."
          },
          "activation_code": {
            "type": "string",
            "description": "The activation code on its own. iOS entries only."
          }
        },
        "required": [
          "steps"
        ]
      },
      "EsimNetworkSetup": {
        "type": "object",
        "description": "Steps to select the network and turn on data roaming, with the APN to enter.",
        "properties": {
          "steps": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The steps as text, keyed by their number: `\"1\"`, `\"2\"`, ..."
          },
          "apn_type": {
            "type": "string",
            "description": "`automatic` or `manual`."
          },
          "apn_value": {
            "type": "string",
            "description": "The APN to enter, or `N/A`."
          },
          "is_roaming": {
            "type": "boolean",
            "description": "Whether data roaming has to be turned on."
          }
        },
        "required": [
          "steps"
        ]
      },
      "EsimInstructionStep": {
        "type": "object",
        "description": "Installation steps for one family of devices.",
        "properties": {
          "model": {
            "anyOf": [
              {
                "type": "string",
                "description": "A device model the steps are written for, or null for every model."
              },
              {
                "type": "null"
              }
            ],
            "description": "A device model the steps are written for, or null for every model."
          },
          "version": {
            "anyOf": [
              {
                "type": "string",
                "description": "Operating system versions the steps apply to, comma separated, or null for every version."
              },
              {
                "type": "null"
              }
            ],
            "description": "Operating system versions the steps apply to, comma separated, or null for every version."
          },
          "direct_apple_installation_url": {
            "type": "string",
            "description": "Link that starts the installation on an iPhone. iOS entries only."
          },
          "direct_android_installation_url": {
            "type": "string",
            "description": "Link that starts the installation on an Android phone. Android entries only."
          },
          "installation_via_qr_code": {
            "$ref": "#/components/schemas/EsimInstallationViaQrCode"
          },
          "installation_manual": {
            "$ref": "#/components/schemas/EsimInstallationManual"
          },
          "network_setup": {
            "$ref": "#/components/schemas/EsimNetworkSetup"
          }
        },
        "required": [
          "installation_via_qr_code",
          "installation_manual",
          "network_setup"
        ]
      },
      "EsimInstructionsResponse": {
        "type": "object",
        "description": "Installation instructions for one ICCID.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the instructions were returned."
          },
          "data": {
            "type": "object",
            "description": "Wrapper object: the steps are in `data.data.instructions`.",
            "properties": {
              "data": {
                "type": "object",
                "description": "Wrapper object.",
                "properties": {
                  "instructions": {
                    "type": "object",
                    "description": "The steps per operating system.",
                    "properties": {
                      "language": {
                        "type": "string",
                        "description": "Language of the steps, upper case, for example `EN`."
                      },
                      "ios": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/EsimInstructionStep"
                        },
                        "description": "Steps for iPhones."
                      },
                      "android": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/EsimInstructionStep"
                        },
                        "description": "Steps for Android phones."
                      }
                    },
                    "required": [
                      "language",
                      "ios",
                      "android"
                    ]
                  }
                },
                "required": [
                  "instructions"
                ]
              },
              "meta": {
                "type": "object",
                "description": "The supplier's status line.",
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "`success` when the supplier answered."
                  }
                },
                "required": [
                  "message"
                ]
              }
            },
            "required": [
              "data",
              "meta"
            ]
          }
        },
        "required": [
          "success",
          "data"
        ]
      },
      "AccountBalance": {
        "type": "object",
        "description": "The balance of your agency in one currency.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the balance. Send it as `balance_id` to read its history.",
            "format": "uuid"
          },
          "agency_id": {
            "type": "string",
            "description": "The id of your agency.",
            "format": "uuid"
          },
          "currency_code": {
            "type": "string",
            "description": "The currency of the balance (ISO 4217), for example `DZD` or `EUR`. The older name of `currency`: same value, use `currency`.",
            "deprecated": true,
            "x-replaced-by": "currency"
          },
          "currency": {
            "type": "string",
            "description": "The currency of the balance (ISO 4217), for example `DZD` or `EUR`. The snake_case twin of `currency_code`, with the same value: use this name."
          },
          "available_balance": {
            "type": "number",
            "description": "The amount you can spend, in this currency."
          },
          "created_at": {
            "type": "string",
            "description": "When the balance was created: ISO 8601, UTC."
          },
          "updated_at": {
            "type": "string",
            "description": "When the balance last moved: ISO 8601, UTC."
          }
        },
        "required": [
          "id",
          "agency_id",
          "currency_code",
          "available_balance",
          "created_at",
          "updated_at"
        ]
      },
      "AccountBalancesResponse": {
        "type": "object",
        "description": "The balances of your agency.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the balances were returned."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountBalance"
            },
            "description": "One balance per currency your agency holds, ordered by currency. Always empty in the sandbox."
          },
          "meta": {
            "type": "object",
            "description": "Facts about the answer.",
            "properties": {
              "agencyId": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The older name of `agency_id`: same value, use `agency_id`.",
                "format": "uuid",
                "deprecated": true,
                "x-replaced-by": "agency_id"
              },
              "agency_id": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The snake_case twin of `agencyId`, with the same value: use this name.",
                "format": "uuid"
              },
              "environment": {
                "$ref": "#/components/schemas/Environment"
              },
              "financialOperationsEnabled": {
                "type": "boolean",
                "description": "Sandbox only, always `false`: the sandbox has no balance ledger. The older name of `financial_operations_enabled`: same value, use `financial_operations_enabled`.",
                "deprecated": true,
                "x-replaced-by": "financial_operations_enabled"
              },
              "financial_operations_enabled": {
                "type": "boolean",
                "description": "Sandbox only, always `false`: the sandbox has no balance ledger. The snake_case twin of `financialOperationsEnabled`, with the same value: use this name."
              },
              "message": {
                "type": "string",
                "description": "Sandbox only: why `data` is empty."
              }
            },
            "required": [
              "agencyId",
              "environment"
            ]
          }
        },
        "required": [
          "success",
          "data",
          "meta"
        ]
      },
      "AccountPagination": {
        "type": "object",
        "description": "Where this page sits in the list.",
        "properties": {
          "page": {
            "type": "integer",
            "description": "The page returned."
          },
          "limit": {
            "type": "integer",
            "description": "The page size used."
          },
          "total": {
            "type": "integer",
            "description": "Number of matching entries. Only the newest ones can be paged to (see the endpoint): follow `has_more`, not `total`."
          },
          "total_pages": {
            "type": "integer",
            "description": "Number of pages: `total` divided by `limit`, rounded up. Absent when the count is not available."
          },
          "hasMore": {
            "type": "boolean",
            "description": "`true` when another page can be served. The older name of `has_more`: same value, use `has_more`.",
            "deprecated": true,
            "x-replaced-by": "has_more"
          },
          "has_more": {
            "type": "boolean",
            "description": "`true` when another page can be served. The snake_case twin of `hasMore`, with the same value: use this name."
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "hasMore"
        ]
      },
      "BalanceHistoryEntry": {
        "type": "object",
        "description": "One movement of a balance.",
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "transactions",
              "flight_balance_operations",
              "hotel_balance_operations"
            ],
            "description": "Where the entry comes from. `transactions` are the authoritative balance movements; the two `*_balance_operations` sources are audit records per booking."
          },
          "id": {
            "type": "string",
            "description": "Id of the entry.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "CREDIT",
              "DEBIT",
              "REFUND",
              "credit",
              "debit",
              "refund"
            ],
            "description": "The kind of movement. `transactions` use upper case, `*_balance_operations` lower case."
          },
          "amount": {
            "type": "number",
            "description": "The amount, always positive: `type` tells the direction."
          },
          "currency": {
            "type": "string",
            "description": "The currency of the balance."
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "description": "A label of the movement, usually with the booking reference."
              },
              {
                "type": "null"
              }
            ],
            "description": "A label of the movement, usually with the booking reference."
          },
          "balanceId": {
            "type": "string",
            "description": "`transactions` only. The balance the movement was recorded against. The older name of `balance_id`: same value, use `balance_id`.",
            "format": "uuid",
            "deprecated": true,
            "x-replaced-by": "balance_id"
          },
          "balance_id": {
            "type": "string",
            "description": "`transactions` only. The balance the movement was recorded against. The snake_case twin of `balanceId`, with the same value: use this name.",
            "format": "uuid"
          },
          "bookingRecordId": {
            "type": "string",
            "description": "`*_balance_operations` only. The internal id of the booking, to match it with the account history. The older name of `booking_record_id`: same value, use `booking_record_id`.",
            "format": "uuid",
            "deprecated": true,
            "x-replaced-by": "booking_record_id"
          },
          "booking_record_id": {
            "type": "string",
            "description": "`*_balance_operations` only. The internal id of the booking, to match it with the account history. The snake_case twin of `bookingRecordId`, with the same value: use this name.",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "description": "When the movement was recorded: ISO 8601, UTC. The older name of `created_at`: same value, use `created_at`.",
            "deprecated": true,
            "x-replaced-by": "created_at"
          },
          "created_at": {
            "type": "string",
            "description": "When the movement was recorded: ISO 8601, UTC. The snake_case twin of `createdAt`, with the same value: use this name."
          }
        },
        "required": [
          "source",
          "id",
          "type",
          "amount",
          "currency",
          "createdAt"
        ]
      },
      "BalanceHistoryResponse": {
        "type": "object",
        "description": "A page of the history of one balance.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the history was returned."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BalanceHistoryEntry"
            },
            "description": "The movements of this page, newest first. Always empty in the sandbox."
          },
          "pagination": {
            "$ref": "#/components/schemas/AccountPagination"
          },
          "meta": {
            "type": "object",
            "description": "Facts about the answer.",
            "properties": {
              "agencyId": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The older name of `agency_id`: same value, use `agency_id`.",
                "format": "uuid",
                "deprecated": true,
                "x-replaced-by": "agency_id"
              },
              "agency_id": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The snake_case twin of `agencyId`, with the same value: use this name.",
                "format": "uuid"
              },
              "environment": {
                "$ref": "#/components/schemas/Environment"
              },
              "balanceId": {
                "type": "string",
                "description": "Live only. The balance of the history. The older name of `balance_id`: same value, use `balance_id`.",
                "format": "uuid",
                "deprecated": true,
                "x-replaced-by": "balance_id"
              },
              "balance_id": {
                "type": "string",
                "description": "Live only. The balance of the history. The snake_case twin of `balanceId`, with the same value: use this name.",
                "format": "uuid"
              },
              "balanceCurrency": {
                "type": "string",
                "description": "Live only. The currency of the balance. The older name of `balance_currency`: same value, use `balance_currency`.",
                "deprecated": true,
                "x-replaced-by": "balance_currency"
              },
              "balance_currency": {
                "type": "string",
                "description": "Live only. The currency of the balance. The snake_case twin of `balanceCurrency`, with the same value: use this name."
              },
              "financialOperationsEnabled": {
                "type": "boolean",
                "description": "Sandbox only, always `false`. The older name of `financial_operations_enabled`: same value, use `financial_operations_enabled`.",
                "deprecated": true,
                "x-replaced-by": "financial_operations_enabled"
              },
              "financial_operations_enabled": {
                "type": "boolean",
                "description": "Sandbox only, always `false`. The snake_case twin of `financialOperationsEnabled`, with the same value: use this name."
              },
              "message": {
                "type": "string",
                "description": "Sandbox only: why `data` is empty."
              }
            },
            "required": [
              "agencyId",
              "environment"
            ]
          }
        },
        "required": [
          "success",
          "data",
          "pagination",
          "meta"
        ]
      },
      "BookingHistoryItem": {
        "type": "object",
        "description": "The summary of one booking, as listed by the history and as the top of the booking detail. The keys after `updatedAt` depend on `product`. Only the keys that have a snake_case twin are listed twice: send `naming=snake` to get every key in snake_case.",
        "properties": {
          "product": {
            "type": "string",
            "enum": [
              "flights",
              "hotels",
              "esims",
              "transfers"
            ],
            "description": "The product of the booking."
          },
          "id": {
            "type": "string",
            "description": "Internal id of the booking (UUID). Stable and unique: store it as your key.",
            "format": "uuid"
          },
          "bookingId": {
            "type": "string",
            "description": "The public reference of the booking: the airline record locator of a flight (not unique across suppliers), the hotel booking id, `FX-` followed by a number for an eSIM, or the transfer reference. The older name of `booking_id`: same value, use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The public reference of the booking: the airline record locator of a flight (not unique across suppliers), the hotel booking id, `FX-` followed by a number for an eSIM, or the transfer reference. The snake_case twin of `bookingId`, with the same value: use this name."
          },
          "status": {
            "type": "string",
            "description": "State of the booking. The values depend on the product: for example `confirmed`, `awaiting_payment` (a held flight that is not paid), `cancelled`, `failed`, `refunded`, `partial_refund`."
          },
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "agencyId": {
            "type": "string",
            "description": "The id of your agency. The older name of `agency_id`: same value, use `agency_id`.",
            "format": "uuid",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          },
          "agency_id": {
            "type": "string",
            "description": "The id of your agency. The snake_case twin of `agencyId`, with the same value: use this name.",
            "format": "uuid"
          },
          "agentId": {
            "anyOf": [
              {
                "type": "string",
                "description": "The user of your agency who created the booking, or null when it was created through an API key without a user."
              },
              {
                "type": "null"
              }
            ],
            "description": "The user of your agency who created the booking, or null when it was created through an API key without a user. The older name of `agent_id`: same value, use `agent_id`.",
            "deprecated": true,
            "x-replaced-by": "agent_id"
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "The user of your agency who created the booking, or null when it was created through an API key without a user."
              },
              {
                "type": "null"
              }
            ],
            "description": "The user of your agency who created the booking, or null when it was created through an API key without a user. The snake_case twin of `agentId`, with the same value: use this name."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amounts: the settlement currency of your agency."
          },
          "totalPrice": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency. It includes your commission. The older name of `total_price`: same value, use `total_price`.",
            "deprecated": true,
            "x-replaced-by": "total_price"
          },
          "total_price": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency. It includes your commission. The snake_case twin of `totalPrice`, with the same value: use this name."
          },
          "netPrice": {
            "type": "number",
            "description": "The amount debited from your agency balance: `totalPrice` minus `agencyCommission`. The older name of `b2b_price`: same value, use `b2b_price`.",
            "deprecated": true,
            "x-replaced-by": "b2b_price"
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance: `totalPrice` minus `agencyCommission`. The snake_case twin of `netPrice`, with the same value: use this name."
          },
          "agencyCommission": {
            "type": "number",
            "description": "Your commission as an amount, in your agency currency. `0` when there is none. The older name of `agency_commission`: same value, use `agency_commission`.",
            "deprecated": true,
            "x-replaced-by": "agency_commission"
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission as an amount, in your agency currency. `0` when there is none. The snake_case twin of `agencyCommission`, with the same value: use this name."
          },
          "providerName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name of the supplier that fulfilled the booking."
              },
              {
                "type": "null"
              }
            ],
            "description": "Name of the supplier that fulfilled the booking. The older name of `provider_name`: same value, use `provider_name`.",
            "deprecated": true,
            "x-replaced-by": "provider_name"
          },
          "provider_name": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name of the supplier that fulfilled the booking."
              },
              {
                "type": "null"
              }
            ],
            "description": "Name of the supplier that fulfilled the booking. The snake_case twin of `providerName`, with the same value: use this name."
          },
          "supplierId": {
            "anyOf": [
              {
                "type": "string",
                "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there."
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there. The older name of `supplier_id`: same value, use `supplier_id`.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there."
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "createdAt": {
            "type": "string",
            "description": "When the booking was created: ISO 8601, UTC. The older name of `created_at`: same value, use `created_at`.",
            "deprecated": true,
            "x-replaced-by": "created_at"
          },
          "created_at": {
            "type": "string",
            "description": "When the booking was created: ISO 8601, UTC. The snake_case twin of `createdAt`, with the same value: use this name."
          },
          "updatedAt": {
            "type": "string",
            "description": "When the booking last changed: ISO 8601, UTC. The older name of `updated_at`: same value, use `updated_at`.",
            "deprecated": true,
            "x-replaced-by": "updated_at"
          },
          "updated_at": {
            "type": "string",
            "description": "When the booking last changed: ISO 8601, UTC. The snake_case twin of `updatedAt`, with the same value: use this name."
          },
          "departureAirport": {
            "type": "string",
            "description": "Flights only. IATA code of the first departure airport."
          },
          "arrivalAirport": {
            "type": "string",
            "description": "Flights only. IATA code of the final arrival airport."
          },
          "departureDate": {
            "type": "string",
            "description": "Flights only. Local departure date and time of the first segment, without a zone."
          },
          "arrivalDate": {
            "type": "string",
            "description": "Flights and transfers. Local arrival date and time, without a zone for flights."
          },
          "ticketNumber": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. The ticket number once issued, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. The ticket number once issued, or null."
          },
          "requireAcd": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Flights only. Whether the booking needs ACD documents."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Whether the booking needs ACD documents. The older name of `require_acd`: same value, use `require_acd`.",
            "deprecated": true,
            "x-replaced-by": "require_acd"
          },
          "require_acd": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Flights only. Whether the booking needs ACD documents."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Whether the booking needs ACD documents. The snake_case twin of `requireAcd`, with the same value: use this name."
          },
          "voucherUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels: the confirmation PDF with prices (same as `voucherUrlWithPrice`), null while it is generated. Transfers: the supplier voucher, null until a driver is assigned."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels: the confirmation PDF with prices (same as `voucherUrlWithPrice`), null while it is generated. Transfers: the supplier voucher, null until a driver is assigned."
          },
          "voucherUrlWithPrice": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. The confirmation PDF with prices."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The confirmation PDF with prices."
          },
          "voucherUrlNoPrice": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. The confirmation PDF without prices, to give to the traveler."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The confirmation PDF without prices, to give to the traveler."
          },
          "hotelId": {
            "type": "string",
            "description": "Hotels only. The id of the hotel."
          },
          "hotelName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Hotels only. The name of the hotel, or null when the supplier did not give one (a failed booking, for example)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Hotels only. The name of the hotel, or null when the supplier did not give one (a failed booking, for example)."
          },
          "checkinDate": {
            "type": "string",
            "description": "Hotels only. Check-in date: `YYYY-MM-DD`."
          },
          "checkoutDate": {
            "type": "string",
            "description": "Hotels only. Check-out date: `YYYY-MM-DD`."
          },
          "bookingMode": {
            "anyOf": [
              {
                "type": "string",
                "description": "Hotels only. How the booking was made, for example `standard`."
              },
              {
                "type": "null"
              }
            ],
            "description": "Hotels only. How the booking was made, for example `standard`."
          },
          "orderId": {
            "type": "string",
            "description": "eSIMs only. The supplier's order code. The older name of `order_id`: same value, use `order_id`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "eSIMs only. The supplier's order code. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "type": {
            "type": "string",
            "description": "eSIMs only. `esim` for an order, `topup` for a topup.",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "packageName": {
            "type": "string",
            "description": "eSIMs only. Name of the package. The older name of `package_name`: same value, use `package_name`.",
            "deprecated": true,
            "x-replaced-by": "package_name"
          },
          "package_name": {
            "type": "string",
            "description": "eSIMs only. Name of the package. The snake_case twin of `packageName`, with the same value: use this name."
          },
          "iccid": {
            "type": "string",
            "description": "eSIMs only. The ICCID of the eSIM."
          },
          "customerEmail": {
            "type": "string",
            "description": "eSIMs only. The traveler's email. The older name of `customer_email`: same value, use `customer_email`.",
            "deprecated": true,
            "x-replaced-by": "customer_email"
          },
          "customer_email": {
            "type": "string",
            "description": "eSIMs only. The traveler's email. The snake_case twin of `customerEmail`, with the same value: use this name."
          },
          "customer_first_name": {
            "type": "string",
            "description": "eSIMs only. The traveler's first name."
          },
          "customer_last_name": {
            "type": "string",
            "description": "eSIMs only. The traveler's last name."
          },
          "customer_phone": {
            "type": "string",
            "description": "eSIMs only. The traveler's phone number."
          },
          "flightNumber": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. The flight number the transfer was booked for."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. The flight number the transfer was booked for."
          },
          "receiptUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. FX-Port's receipt PDF, once the booking is confirmed."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. FX-Port's receipt PDF, once the booking is confirmed."
          }
        },
        "required": [
          "product",
          "id",
          "bookingId",
          "status",
          "environment",
          "agencyId",
          "agentId",
          "currency",
          "totalPrice",
          "netPrice",
          "agencyCommission",
          "providerName",
          "supplierId",
          "createdAt",
          "updatedAt"
        ]
      },
      "BookingHistoryResponse": {
        "type": "object",
        "description": "A page of the booking history.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the history was returned."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingHistoryItem"
            },
            "description": "The bookings of this page, newest first. eSIM topups are listed next to eSIM orders."
          },
          "pagination": {
            "$ref": "#/components/schemas/AccountPagination"
          },
          "meta": {
            "type": "object",
            "description": "Facts about the answer.",
            "properties": {
              "agencyId": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The older name of `agency_id`: same value, use `agency_id`.",
                "format": "uuid",
                "deprecated": true,
                "x-replaced-by": "agency_id"
              },
              "agency_id": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The snake_case twin of `agencyId`, with the same value: use this name.",
                "format": "uuid"
              },
              "environment": {
                "$ref": "#/components/schemas/Environment"
              },
              "product": {
                "type": "string",
                "description": "The `product` filter that was applied, `all` by default."
              }
            },
            "required": [
              "agencyId",
              "environment",
              "product"
            ]
          }
        },
        "required": [
          "success",
          "data",
          "pagination",
          "meta"
        ]
      },
      "AccountBookingRecord": {
        "type": "object",
        "description": "The stored record of the booking, in snake_case. The keys that exist depend on the product; a key that does not apply is absent.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Internal id of the booking (UUID).",
            "format": "uuid"
          },
          "booking_id": {
            "type": "string",
            "description": "The public reference of the booking."
          },
          "order_id": {
            "type": "string",
            "description": "eSIMs only. The supplier's order code."
          },
          "status": {
            "type": "string",
            "description": "State of the booking."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amounts."
          },
          "total_price": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency."
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission as an amount."
          },
          "departure_airport": {
            "type": "string",
            "description": "Flights only. IATA code of the first departure airport."
          },
          "arrival_airport": {
            "type": "string",
            "description": "Flights only. IATA code of the final arrival airport."
          },
          "departure_date": {
            "type": "string",
            "description": "Flights only. Local departure date and time of the first segment."
          },
          "arrival_date": {
            "type": "string",
            "description": "Flights and transfers. Local arrival date and time."
          },
          "return_departure_date": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. Departure of the return trip, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Departure of the return trip, or null."
          },
          "return_arrival_date": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. Arrival of the return trip, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Arrival of the return trip, or null."
          },
          "included_checked_bags": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. Checked bags included in the fare."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Checked bags included in the fare."
          },
          "ticket_number": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. The ticket number once issued, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. The ticket number once issued, or null."
          },
          "require_acd": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Flights only. Whether the booking needs ACD documents."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Whether the booking needs ACD documents."
          },
          "hotel_id": {
            "type": "string",
            "description": "Hotels only. The id of the hotel."
          },
          "hotel_name": {
            "type": "string",
            "description": "Hotels only. The name of the hotel."
          },
          "checkin_date": {
            "type": "string",
            "description": "Hotels only. Check-in date: `YYYY-MM-DD`."
          },
          "checkout_date": {
            "type": "string",
            "description": "Hotels only. Check-out date: `YYYY-MM-DD`."
          },
          "booking_mode": {
            "anyOf": [
              {
                "type": "string",
                "description": "Hotels only. How the booking was made."
              },
              {
                "type": "null"
              }
            ],
            "description": "Hotels only. How the booking was made."
          },
          "free_cancellation_deadline_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "Hotels only. Until when the booking can be cancelled free of charge, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Hotels only. Until when the booking can be cancelled free of charge, or null."
          },
          "type": {
            "type": "string",
            "description": "eSIMs only. `esim` or `topup`.",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "package_name": {
            "type": "string",
            "description": "eSIMs only. Name of the package."
          },
          "iccid": {
            "type": "string",
            "description": "eSIMs only. The ICCID of the eSIM."
          },
          "customer_email": {
            "type": "string",
            "description": "eSIMs only. The traveler's email."
          },
          "customer_first_name": {
            "type": "string",
            "description": "eSIMs only. The traveler's first name."
          },
          "customer_last_name": {
            "type": "string",
            "description": "eSIMs only. The traveler's last name."
          },
          "customer_phone": {
            "type": "string",
            "description": "eSIMs only. The traveler's phone number."
          },
          "provider_name": {
            "type": "string",
            "description": "eSIMs and transfers. Name of the supplier."
          },
          "supplier_id": {
            "type": "string",
            "description": "Hotels and eSIMs. Id of the supplier, as stored on the booking."
          },
          "flight_number": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. The flight number."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. The flight number."
          },
          "bucket_link": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights, hotels and transfers. The confirmation PDF with prices; for a transfer, FX-Port's receipt."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights, hotels and transfers. The confirmation PDF with prices; for a transfer, FX-Port's receipt."
          },
          "bucket_link_no_price": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. The confirmation PDF without prices."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The confirmation PDF without prices."
          },
          "voucher_link": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. The supplier voucher, once a driver is assigned."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. The supplier voucher, once a driver is assigned."
          },
          "refund_amount": {
            "anyOf": [
              {
                "type": "number",
                "description": "Flights and hotels. The amount refunded, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The amount refunded, or null."
          },
          "refund_currency": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. The currency of the refund, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The currency of the refund, or null."
          },
          "refund_method": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. How the refund was made, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. How the refund was made, or null."
          },
          "refunded_amount": {
            "anyOf": [
              {
                "type": "number",
                "description": "Transfers only. The amount refunded, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. The amount refunded, or null."
          },
          "refunded_currency": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. The currency of the refund, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. The currency of the refund, or null."
          },
          "paid_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. When the booking was paid, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. When the booking was paid, or null."
          },
          "voided_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. When the ticket was voided, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. When the ticket was voided, or null."
          },
          "created_at": {
            "type": "string",
            "description": "When the booking was created: ISO 8601, UTC."
          },
          "updated_at": {
            "type": "string",
            "description": "When the booking last changed: ISO 8601, UTC."
          },
          "environment": {
            "$ref": "#/components/schemas/Environment"
          }
        },
        "required": [
          "id",
          "booking_id",
          "status",
          "currency",
          "total_price",
          "agency_commission",
          "created_at",
          "updated_at",
          "environment"
        ]
      },
      "AccountPricingRow": {
        "type": "object",
        "description": "The price breakdown of a flight or a hotel booking.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Hotels only. Id of the pricing row.",
            "format": "uuid"
          },
          "supplier_currency": {
            "type": "string",
            "description": "Flights only. The currency the supplier prices in."
          },
          "agency_currency": {
            "type": "string",
            "description": "The currency of your agency."
          },
          "base_price": {
            "type": "number",
            "description": "Flights only. The fare before taxes."
          },
          "taxes_and_fees": {
            "type": "number",
            "description": "Flights only. Taxes and fees, without your commission."
          },
          "total_price": {
            "type": "number",
            "description": "Hotels only. What the traveler pays."
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission as an amount."
          },
          "final_price": {
            "type": "number",
            "description": "Flights only. What the traveler pays: `base_price` plus `taxes_and_fees` plus `agency_commission`."
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance: the total minus `agency_commission`."
          },
          "created_at": {
            "type": "string",
            "description": "When the row was written: ISO 8601, UTC."
          },
          "environment": {
            "$ref": "#/components/schemas/Environment"
          }
        },
        "required": [
          "agency_currency",
          "agency_commission",
          "b2b_price",
          "created_at",
          "environment"
        ]
      },
      "AccountTravelerPricingRow": {
        "type": "object",
        "description": "The price of one traveler of a flight booking.",
        "properties": {
          "traveler_id": {
            "type": "string",
            "description": "The traveler the row is for: the id of the traveler in the booking."
          },
          "traveler_type": {
            "type": "string",
            "description": "`ADULT`, `CHILD`, `INFANT` and so on."
          },
          "fare_option": {
            "type": "string",
            "description": "The fare option, for example `STANDARD`."
          },
          "supplier_currency": {
            "type": "string",
            "description": "The currency the supplier prices in."
          },
          "agency_currency": {
            "type": "string",
            "description": "The currency of your agency."
          },
          "base": {
            "type": "number",
            "description": "The fare of this traveler before taxes."
          },
          "total": {
            "type": "number",
            "description": "What this traveler's ticket costs the traveler."
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance for this traveler."
          },
          "created_at": {
            "type": "string",
            "description": "When the row was written: ISO 8601, UTC."
          },
          "environment": {
            "$ref": "#/components/schemas/Environment"
          }
        },
        "required": [
          "traveler_id",
          "traveler_type",
          "fare_option",
          "agency_currency",
          "base",
          "total",
          "b2b_price",
          "created_at",
          "environment"
        ]
      },
      "AccountBalanceOperation": {
        "type": "object",
        "description": "One balance operation of a flight or hotel booking.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the operation.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "description": "`debit`, `credit` or `refund`, in lower case."
          },
          "amount": {
            "type": "number",
            "description": "The amount, always positive."
          },
          "currency": {
            "type": "string",
            "description": "The currency of the operation."
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "description": "A label of the operation."
              },
              {
                "type": "null"
              }
            ],
            "description": "A label of the operation."
          },
          "createdAt": {
            "type": "string",
            "description": "When the operation was recorded: ISO 8601, UTC. The older name of `created_at`: same value, use `created_at`.",
            "deprecated": true,
            "x-replaced-by": "created_at"
          },
          "created_at": {
            "type": "string",
            "description": "When the operation was recorded: ISO 8601, UTC. The snake_case twin of `createdAt`, with the same value: use this name."
          }
        },
        "required": [
          "id",
          "type",
          "amount",
          "currency",
          "createdAt"
        ]
      },
      "AccountBookingDetail": {
        "type": "object",
        "description": "One booking with its stored record. The summary keys are the ones of the history; `record` repeats the booking in snake_case.",
        "properties": {
          "product": {
            "type": "string",
            "enum": [
              "flights",
              "hotels",
              "esims",
              "transfers"
            ],
            "description": "The product of the booking."
          },
          "id": {
            "type": "string",
            "description": "Internal id of the booking (UUID). Stable and unique: store it as your key.",
            "format": "uuid"
          },
          "bookingId": {
            "type": "string",
            "description": "The public reference of the booking: the airline record locator of a flight (not unique across suppliers), the hotel booking id, `FX-` followed by a number for an eSIM, or the transfer reference. The older name of `booking_id`: same value, use `booking_id`.",
            "deprecated": true,
            "x-replaced-by": "booking_id"
          },
          "booking_id": {
            "type": "string",
            "description": "The public reference of the booking: the airline record locator of a flight (not unique across suppliers), the hotel booking id, `FX-` followed by a number for an eSIM, or the transfer reference. The snake_case twin of `bookingId`, with the same value: use this name."
          },
          "status": {
            "type": "string",
            "description": "State of the booking. The values depend on the product: for example `confirmed`, `awaiting_payment` (a held flight that is not paid), `cancelled`, `failed`, `refunded`, `partial_refund`."
          },
          "environment": {
            "$ref": "#/components/schemas/Environment"
          },
          "agencyId": {
            "type": "string",
            "description": "The id of your agency. The older name of `agency_id`: same value, use `agency_id`.",
            "format": "uuid",
            "deprecated": true,
            "x-replaced-by": "agency_id"
          },
          "agency_id": {
            "type": "string",
            "description": "The id of your agency. The snake_case twin of `agencyId`, with the same value: use this name.",
            "format": "uuid"
          },
          "agentId": {
            "anyOf": [
              {
                "type": "string",
                "description": "The user of your agency who created the booking, or null when it was created through an API key without a user."
              },
              {
                "type": "null"
              }
            ],
            "description": "The user of your agency who created the booking, or null when it was created through an API key without a user. The older name of `agent_id`: same value, use `agent_id`.",
            "deprecated": true,
            "x-replaced-by": "agent_id"
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "The user of your agency who created the booking, or null when it was created through an API key without a user."
              },
              {
                "type": "null"
              }
            ],
            "description": "The user of your agency who created the booking, or null when it was created through an API key without a user. The snake_case twin of `agentId`, with the same value: use this name."
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amounts: the settlement currency of your agency."
          },
          "totalPrice": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency. It includes your commission. The older name of `total_price`: same value, use `total_price`.",
            "deprecated": true,
            "x-replaced-by": "total_price"
          },
          "total_price": {
            "type": "number",
            "description": "What the traveler pays, in your agency currency. It includes your commission. The snake_case twin of `totalPrice`, with the same value: use this name."
          },
          "netPrice": {
            "type": "number",
            "description": "The amount debited from your agency balance: `totalPrice` minus `agencyCommission`. The older name of `b2b_price`: same value, use `b2b_price`.",
            "deprecated": true,
            "x-replaced-by": "b2b_price"
          },
          "b2b_price": {
            "type": "number",
            "description": "The amount debited from your agency balance: `totalPrice` minus `agencyCommission`. The snake_case twin of `netPrice`, with the same value: use this name."
          },
          "agencyCommission": {
            "type": "number",
            "description": "Your commission as an amount, in your agency currency. `0` when there is none. The older name of `agency_commission`: same value, use `agency_commission`.",
            "deprecated": true,
            "x-replaced-by": "agency_commission"
          },
          "agency_commission": {
            "type": "number",
            "description": "Your commission as an amount, in your agency currency. `0` when there is none. The snake_case twin of `agencyCommission`, with the same value: use this name."
          },
          "providerName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name of the supplier that fulfilled the booking."
              },
              {
                "type": "null"
              }
            ],
            "description": "Name of the supplier that fulfilled the booking. The older name of `provider_name`: same value, use `provider_name`.",
            "deprecated": true,
            "x-replaced-by": "provider_name"
          },
          "provider_name": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name of the supplier that fulfilled the booking."
              },
              {
                "type": "null"
              }
            ],
            "description": "Name of the supplier that fulfilled the booking. The snake_case twin of `providerName`, with the same value: use this name."
          },
          "supplierId": {
            "anyOf": [
              {
                "type": "string",
                "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there."
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there. The older name of `supplier_id`: same value, use `supplier_id`.",
            "deprecated": true,
            "x-replaced-by": "supplier_id"
          },
          "supplier_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there."
              },
              {
                "type": "null"
              }
            ],
            "description": "Id of the supplier that fulfilled the booking, as stored on the booking. It can differ from the id the supplier list uses: `ratehawk` and `airalo` here are `worldota_1` and `airalo_1` there. The snake_case twin of `supplierId`, with the same value: use this name."
          },
          "createdAt": {
            "type": "string",
            "description": "When the booking was created: ISO 8601, UTC. The older name of `created_at`: same value, use `created_at`.",
            "deprecated": true,
            "x-replaced-by": "created_at"
          },
          "created_at": {
            "type": "string",
            "description": "When the booking was created: ISO 8601, UTC. The snake_case twin of `createdAt`, with the same value: use this name."
          },
          "updatedAt": {
            "type": "string",
            "description": "When the booking last changed: ISO 8601, UTC. The older name of `updated_at`: same value, use `updated_at`.",
            "deprecated": true,
            "x-replaced-by": "updated_at"
          },
          "updated_at": {
            "type": "string",
            "description": "When the booking last changed: ISO 8601, UTC. The snake_case twin of `updatedAt`, with the same value: use this name."
          },
          "departureAirport": {
            "type": "string",
            "description": "Flights only. IATA code of the first departure airport."
          },
          "arrivalAirport": {
            "type": "string",
            "description": "Flights only. IATA code of the final arrival airport."
          },
          "departureDate": {
            "type": "string",
            "description": "Flights only. Local departure date and time of the first segment, without a zone."
          },
          "arrivalDate": {
            "type": "string",
            "description": "Flights and transfers. Local arrival date and time, without a zone for flights."
          },
          "ticketNumber": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights only. The ticket number once issued, or null."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. The ticket number once issued, or null."
          },
          "requireAcd": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Flights only. Whether the booking needs ACD documents."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Whether the booking needs ACD documents. The older name of `require_acd`: same value, use `require_acd`.",
            "deprecated": true,
            "x-replaced-by": "require_acd"
          },
          "require_acd": {
            "anyOf": [
              {
                "type": "boolean",
                "description": "Flights only. Whether the booking needs ACD documents."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights only. Whether the booking needs ACD documents. The snake_case twin of `requireAcd`, with the same value: use this name."
          },
          "voucherUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels: the confirmation PDF with prices (same as `voucherUrlWithPrice`), null while it is generated. Transfers: the supplier voucher, null until a driver is assigned."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels: the confirmation PDF with prices (same as `voucherUrlWithPrice`), null while it is generated. Transfers: the supplier voucher, null until a driver is assigned."
          },
          "voucherUrlWithPrice": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. The confirmation PDF with prices."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The confirmation PDF with prices."
          },
          "voucherUrlNoPrice": {
            "anyOf": [
              {
                "type": "string",
                "description": "Flights and hotels. The confirmation PDF without prices, to give to the traveler."
              },
              {
                "type": "null"
              }
            ],
            "description": "Flights and hotels. The confirmation PDF without prices, to give to the traveler."
          },
          "hotelId": {
            "type": "string",
            "description": "Hotels only. The id of the hotel."
          },
          "hotelName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Hotels only. The name of the hotel, or null when the supplier did not give one (a failed booking, for example)."
              },
              {
                "type": "null"
              }
            ],
            "description": "Hotels only. The name of the hotel, or null when the supplier did not give one (a failed booking, for example)."
          },
          "checkinDate": {
            "type": "string",
            "description": "Hotels only. Check-in date: `YYYY-MM-DD`."
          },
          "checkoutDate": {
            "type": "string",
            "description": "Hotels only. Check-out date: `YYYY-MM-DD`."
          },
          "bookingMode": {
            "anyOf": [
              {
                "type": "string",
                "description": "Hotels only. How the booking was made, for example `standard`."
              },
              {
                "type": "null"
              }
            ],
            "description": "Hotels only. How the booking was made, for example `standard`."
          },
          "orderId": {
            "type": "string",
            "description": "eSIMs only. The supplier's order code. The older name of `order_id`: same value, use `order_id`.",
            "deprecated": true,
            "x-replaced-by": "order_id"
          },
          "order_id": {
            "type": "string",
            "description": "eSIMs only. The supplier's order code. The snake_case twin of `orderId`, with the same value: use this name."
          },
          "type": {
            "type": "string",
            "description": "eSIMs only. `esim` for an order, `topup` for a topup.",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "packageName": {
            "type": "string",
            "description": "eSIMs only. Name of the package. The older name of `package_name`: same value, use `package_name`.",
            "deprecated": true,
            "x-replaced-by": "package_name"
          },
          "package_name": {
            "type": "string",
            "description": "eSIMs only. Name of the package. The snake_case twin of `packageName`, with the same value: use this name."
          },
          "iccid": {
            "type": "string",
            "description": "eSIMs only. The ICCID of the eSIM."
          },
          "customerEmail": {
            "type": "string",
            "description": "eSIMs only. The traveler's email. The older name of `customer_email`: same value, use `customer_email`.",
            "deprecated": true,
            "x-replaced-by": "customer_email"
          },
          "customer_email": {
            "type": "string",
            "description": "eSIMs only. The traveler's email. The snake_case twin of `customerEmail`, with the same value: use this name."
          },
          "customer_first_name": {
            "type": "string",
            "description": "eSIMs only. The traveler's first name."
          },
          "customer_last_name": {
            "type": "string",
            "description": "eSIMs only. The traveler's last name."
          },
          "customer_phone": {
            "type": "string",
            "description": "eSIMs only. The traveler's phone number."
          },
          "flightNumber": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. The flight number the transfer was booked for."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. The flight number the transfer was booked for."
          },
          "receiptUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Transfers only. FX-Port's receipt PDF, once the booking is confirmed."
              },
              {
                "type": "null"
              }
            ],
            "description": "Transfers only. FX-Port's receipt PDF, once the booking is confirmed."
          },
          "record": {
            "$ref": "#/components/schemas/AccountBookingRecord"
          },
          "pricing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountPricingRow"
            },
            "description": "Flights and hotels only. The price breakdown."
          },
          "travelerPricing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountTravelerPricingRow"
            },
            "description": "Flights only. The price per traveler. The older name of `traveler_pricing`: same value, use `traveler_pricing`.",
            "deprecated": true,
            "x-replaced-by": "traveler_pricing"
          },
          "traveler_pricing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountTravelerPricingRow"
            },
            "description": "Flights only. The price per traveler. The snake_case twin of `travelerPricing`, with the same value: use this name."
          },
          "balanceOperations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountBalanceOperation"
            },
            "description": "Flights and hotels only. The balance operations of the booking: empty in the sandbox. The older name of `balance_operations`: same value, use `balance_operations`.",
            "deprecated": true,
            "x-replaced-by": "balance_operations"
          },
          "balance_operations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountBalanceOperation"
            },
            "description": "Flights and hotels only. The balance operations of the booking: empty in the sandbox. The snake_case twin of `balanceOperations`, with the same value: use this name."
          }
        },
        "required": [
          "product",
          "id",
          "bookingId",
          "status",
          "environment",
          "agencyId",
          "agentId",
          "currency",
          "totalPrice",
          "netPrice",
          "agencyCommission",
          "providerName",
          "supplierId",
          "createdAt",
          "updatedAt",
          "record"
        ]
      },
      "AccountBookingDetailResponse": {
        "type": "object",
        "description": "One booking.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the booking was found."
          },
          "data": {
            "$ref": "#/components/schemas/AccountBookingDetail"
          },
          "meta": {
            "type": "object",
            "description": "Facts about the answer.",
            "properties": {
              "agencyId": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The older name of `agency_id`: same value, use `agency_id`.",
                "format": "uuid",
                "deprecated": true,
                "x-replaced-by": "agency_id"
              },
              "agency_id": {
                "type": "string",
                "description": "The id of your agency, resolved from the key. The snake_case twin of `agencyId`, with the same value: use this name.",
                "format": "uuid"
              },
              "environment": {
                "$ref": "#/components/schemas/Environment"
              }
            },
            "required": [
              "agencyId",
              "environment"
            ]
          }
        },
        "required": [
          "success",
          "data",
          "meta"
        ]
      },
      "SupplierCapabilities": {
        "type": "object",
        "description": "What a flight supplier supports.",
        "properties": {
          "hold": {
            "type": "boolean",
            "description": "Flights. The supplier can hold a booking to pay later."
          },
          "void": {
            "type": "boolean",
            "description": "Flights. The supplier can void an issued ticket on the day of issue."
          },
          "flexible_dates": {
            "type": "boolean",
            "description": "Flights. The supplier supports the flexible-dates search."
          },
          "ancillaries": {
            "type": "boolean",
            "description": "Flights. The supplier sells extras such as seats and bags."
          }
        },
        "required": [
          "hold",
          "void",
          "flexible_dates",
          "ancillaries"
        ]
      },
      "Supplier": {
        "type": "object",
        "description": "A supplier your key can reach.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the supplier. Send it as `supplier_id` where an endpoint takes one."
          },
          "name": {
            "type": "string",
            "description": "The name of the supplier."
          },
          "technology": {
            "type": "string",
            "description": "How FX-Port connects to the supplier: for example `amadeus_aqc`, `duffel`, `worldota` or `airalo`. New values appear when connections are added."
          },
          "routes": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "A market: a country code, or `*` for every market."
            },
            "description": "Flights only. The markets the supplier serves."
          },
          "description": {
            "type": "string",
            "description": "What the supplier is good for. Free text: it can change."
          },
          "capabilities": {
            "$ref": "#/components/schemas/SupplierCapabilities"
          },
          "voidDeadline": {
            "type": "string",
            "description": "Flights only, for suppliers that can void. The next cut-off for a void, in UTC: the next midnight in the supplier's time zone. It is recalculated on every request. The older name of `void_deadline`: same value, use `void_deadline`.",
            "deprecated": true,
            "x-replaced-by": "void_deadline"
          },
          "void_deadline": {
            "type": "string",
            "description": "Flights only, for suppliers that can void. The next cut-off for a void, in UTC: the next midnight in the supplier's time zone. It is recalculated on every request. The snake_case twin of `voidDeadline`, with the same value: use this name."
          }
        },
        "required": [
          "id",
          "name",
          "technology",
          "description"
        ]
      },
      "SupplierProduct": {
        "type": "object",
        "description": "The suppliers of one product.",
        "properties": {
          "product": {
            "type": "string",
            "enum": [
              "flights",
              "hotels",
              "esims"
            ],
            "description": "The product."
          },
          "suppliers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Supplier"
            },
            "description": "The enabled suppliers of the product."
          }
        },
        "required": [
          "product",
          "suppliers"
        ]
      },
      "SuppliersResponse": {
        "type": "object",
        "description": "The enabled suppliers, by product.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the list was returned."
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupplierProduct"
            },
            "description": "One entry per product that has at least one enabled supplier."
          },
          "meta": {
            "type": "object",
            "description": "Facts about the answer.",
            "properties": {
              "environment": {
                "$ref": "#/components/schemas/Environment"
              },
              "permissionRequired": {
                "type": "string",
                "description": "`read`: the permission the endpoint needs. The older name of `permission_required`: same value, use `permission_required`.",
                "deprecated": true,
                "x-replaced-by": "permission_required"
              },
              "permission_required": {
                "type": "string",
                "description": "`read`: the permission the endpoint needs. The snake_case twin of `permissionRequired`, with the same value: use this name."
              }
            },
            "required": [
              "environment",
              "permissionRequired"
            ]
          }
        },
        "required": [
          "success",
          "products",
          "meta"
        ]
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key: `fxp_test_...` for the sandbox, `fxp_live_...` for live. Create keys in the dashboard."
      }
    }
  },
  "x-error-codes": [
    {
      "code": "authentication_required",
      "status": 401,
      "type": "authentication_error",
      "meaning": "No API key was sent.",
      "message": "Authentication required. Provide Authorization: Bearer <key>."
    },
    {
      "code": "invalid_api_key_format",
      "status": 401,
      "type": "authentication_error",
      "meaning": "The key does not have the shape of an FX-Port key: fxp_live_ or fxp_test_ followed by 16 to 64 letters and digits."
    },
    {
      "code": "invalid_api_key",
      "status": 401,
      "type": "authentication_error",
      "meaning": "The key is unknown, revoked or expired."
    },
    {
      "code": "ip_not_allowed",
      "status": 403,
      "type": "permission_error",
      "meaning": "The key has an IP allow-list and the request came from another address."
    },
    {
      "code": "agency_not_active",
      "status": 403,
      "type": "permission_error",
      "meaning": "The agency of the key is not approved for this environment yet."
    },
    {
      "code": "read_write_permission_required",
      "status": 403,
      "type": "permission_error",
      "meaning": "The endpoint changes data and the key has read permission only."
    },
    {
      "code": "endpoint_not_available",
      "status": 403,
      "type": "permission_error",
      "meaning": "The endpoint is for FX-Port's own applications and is not available to API keys."
    },
    {
      "code": "invalid_token",
      "status": 401,
      "type": "authentication_error",
      "meaning": "The bearer token is neither an API key nor a valid dashboard session (eSIM endpoints accept both)."
    },
    {
      "code": "unauthorized",
      "status": 401,
      "type": "authentication_error",
      "meaning": "The request is not authenticated and no more specific reason is available."
    },
    {
      "code": "forbidden",
      "status": 403,
      "type": "permission_error",
      "meaning": "The key may not do this and no more specific reason is available."
    },
    {
      "code": "not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "No such route or object."
    },
    {
      "code": "bad_request",
      "status": 400,
      "type": "validation_error",
      "meaning": "The request is not valid and no more specific code is available; read the message."
    },
    {
      "code": "conflict",
      "status": 409,
      "type": "conflict",
      "meaning": "The request conflicts with the state of the object and no more specific code is available; read the message."
    },
    {
      "code": "gone",
      "status": 410,
      "type": "not_found",
      "meaning": "The object has expired (for example an offer): search and price again."
    },
    {
      "code": "unsupported_media_type",
      "status": 415,
      "type": "validation_error",
      "meaning": "The body is not in a format the endpoint accepts (for example an upload that is not multipart/form-data)."
    },
    {
      "code": "request_failed",
      "status": 200,
      "type": "supplier_error",
      "meaning": "The call answered HTTP 200 with success false and no more specific code: read the message."
    },
    {
      "code": "bad_gateway",
      "status": 502,
      "type": "internal_error",
      "meaning": "A supplier or an upstream service answered with an error."
    },
    {
      "code": "gateway_timeout",
      "status": 504,
      "type": "internal_error",
      "meaning": "A supplier or an upstream service did not answer in time."
    },
    {
      "code": "method_not_allowed",
      "status": 405,
      "type": "validation_error",
      "meaning": "The route exists but not for this HTTP method."
    },
    {
      "code": "invalid_json",
      "status": 400,
      "type": "validation_error",
      "meaning": "The body is not valid JSON."
    },
    {
      "code": "invalid_body",
      "status": 400,
      "type": "validation_error",
      "meaning": "The body is valid JSON but not the kind of value the endpoint expects (for example an array instead of an object)."
    },
    {
      "code": "missing_field",
      "status": 400,
      "type": "validation_error",
      "meaning": "A required field is missing.",
      "param": "field"
    },
    {
      "code": "request_too_large",
      "status": 413,
      "type": "validation_error",
      "meaning": "The body is larger than the endpoint accepts."
    },
    {
      "code": "validation_error",
      "status": 422,
      "type": "validation_error",
      "meaning": "A query or path parameter is missing or has the wrong type or range; one item per problem in `errors`."
    },
    {
      "code": "rate_limit_exceeded",
      "status": 429,
      "type": "rate_limit_error",
      "meaning": "More requests than the limit of this endpoint group per minute. See the Retry-After header."
    },
    {
      "code": "concurrency_limit_exceeded",
      "status": 429,
      "type": "rate_limit_error",
      "meaning": "Too many requests of this kind in flight at the same time for your key. Wait for Retry-After (a few seconds) or send fewer in parallel."
    },
    {
      "code": "auth_rate_limited",
      "status": 429,
      "type": "rate_limit_error",
      "meaning": "Too many requests with a missing or invalid key came from your IP address. Requests with a valid key are never affected."
    },
    {
      "code": "service_overloaded",
      "status": 503,
      "type": "internal_error",
      "meaning": "The service is shedding load for a moment. Retry after the number of seconds in Retry-After."
    },
    {
      "code": "supplier_busy",
      "status": 503,
      "type": "supplier_error",
      "meaning": "The supplier is saturated: its queue is full. Retry after the number of seconds in Retry-After."
    },
    {
      "code": "internal_error",
      "status": 500,
      "type": "internal_error",
      "meaning": "An unexpected error on our side; quote the request_id."
    },
    {
      "code": "service_unavailable",
      "status": 503,
      "type": "internal_error",
      "meaning": "The service or a supplier is temporarily unavailable. Retry with backoff."
    },
    {
      "code": "missing_search_params",
      "status": 400,
      "type": "validation_error",
      "meaning": "A search names neither a `url`, nor `segments`, nor the three fields `origin`, `destination` and `departure_date`.",
      "message": "Provide either 'url' or structured params (origin, destination, departure_date, cabin_class)."
    },
    {
      "code": "invalid_airport_code",
      "status": 400,
      "type": "validation_error",
      "meaning": "An airport or city code is not three ASCII letters (`origin` and `destination` take up to five codes, as `\"ALG,ORN\"` or a list; a segment takes one), or the destination equals the origin.",
      "message": "`origin` must be a 3-letter IATA code such as ALG."
    },
    {
      "code": "return_before_departure",
      "status": 400,
      "type": "validation_error",
      "meaning": "The return date, or the date of a later segment, is before the previous flight.",
      "message": "`return_date` is before `departure_date`."
    },
    {
      "code": "invalid_cabin_class",
      "status": 400,
      "type": "validation_error",
      "meaning": "`cabin_class` is not one of `economy`, `premium_economy`, `business`, `first`, `any`, once case, spaces, dashes and underscores are ignored (`Premium Economy`, `premium-economy` and `premium` are read as `premium_economy`). `errors[0].details.allowed` lists the values.",
      "message": "`cabin_class` must be one of: economy, premium_economy, business, first, any.",
      "param": "cabin_class"
    },
    {
      "code": "invalid_passenger_count",
      "status": 400,
      "type": "validation_error",
      "meaning": "A passenger count is not a whole number from 0 to 99. Numeric text and integral floats (`\"2\"`, `2.0`) are read with a `coerced_value` warning; words, fractions, booleans, negatives and bigger numbers are refused.",
      "message": "`passengers.children` must be a whole number from 0 to 99."
    },
    {
      "code": "invalid_passenger_age",
      "status": 400,
      "type": "validation_error",
      "meaning": "A passenger age is not a whole number from 0 to 17. Numeric text, integral floats and a fraction (`0.5`, read as the years completed) are read with a `coerced_value` warning; words, booleans, negatives and ages above 17 are refused."
    },
    {
      "code": "too_many_passengers",
      "status": 400,
      "type": "validation_error",
      "meaning": "A booking has more than 18 passengers.",
      "message": "A booking takes at most 18 passengers.",
      "param": "passengers"
    },
    {
      "code": "traveler_rule_violation",
      "status": 400,
      "type": "validation_error",
      "meaning": "The passenger mix breaks a rule: more than 9 seated passengers, more lap infants than adults and seniors, or more seated infants than adults and seniors. The body keeps its older flat shape (`error: TRAVELER_RULE_VIOLATION`) with `violations[]`.",
      "message": "Total seated passengers cannot exceed 9"
    },
    {
      "code": "supplier_passenger_combination_unsupported",
      "status": 400,
      "type": "supplier_error",
      "meaning": "No selected supplier can serve the passenger mix (some suppliers need one adult or senior for every passenger under two).",
      "message": "No available flight provider supports this passenger combination."
    },
    {
      "code": "invalid_supplier_selection",
      "status": 400,
      "type": "validation_error",
      "meaning": "A `supplier_id` is unknown, disabled, not a valid token or cannot do what the call needs (flexible search). `availableSuppliers` lists the usable ids.",
      "message": "One or more requested suppliers cannot be used for this flight search."
    },
    {
      "code": "invalid_airline_filter",
      "status": 400,
      "type": "validation_error",
      "meaning": "An airline list has more than 20 entries or an entry that is not an IATA airline code."
    },
    {
      "code": "invalid_field_type",
      "status": 400,
      "type": "validation_error",
      "meaning": "A field has the wrong JSON type. `errors[0].details` names the expected and the received type.",
      "message": "`filters` must be an object."
    },
    {
      "code": "invalid_segments",
      "status": 400,
      "type": "validation_error",
      "meaning": "`segments` is not a list of 1 to 6 flights.",
      "message": "`segments` must be a list of 1 to 6 flights.",
      "param": "segments"
    },
    {
      "code": "invalid_country_code",
      "status": 400,
      "type": "validation_error",
      "meaning": "`from_cc`, `to_cc` or an entry of `countries` is not exactly two ASCII letters; or the `issuing_country` of an identity document is no country value (2 to 4 letters, optionally `-` and 1 to 3 letters or digits: `DZ`, `GB-SCT`, `USPR`)."
    },
    {
      "code": "invalid_currency",
      "status": 400,
      "type": "validation_error",
      "meaning": "A currency override is not a three-letter ISO 4217 code."
    },
    {
      "code": "invalid_url",
      "status": 400,
      "type": "validation_error",
      "meaning": "The `url` of a search is longer than 512 characters or is not a path-style search.",
      "param": "url"
    },
    {
      "code": "no_supplier_supports_flexible_search",
      "status": 422,
      "type": "validation_error",
      "meaning": "A flexible-only search on a route that no flexible-capable supplier serves (flexible dates exist on routes that touch Algeria).",
      "message": "No flight supplier supports flexible-date search for this route.",
      "param": "flexible_only"
    },
    {
      "code": "all_suppliers_failed",
      "status": 503,
      "type": "supplier_error",
      "meaning": "No supplier returned results. `supplierErrors[]` lists each supplier with a stable `cause`.",
      "message": "No supplier returned results. See supplierErrors for details."
    },
    {
      "code": "acd_exemption_invalid",
      "status": 403,
      "type": "permission_error",
      "meaning": "`acd_exempt_company_id` is not linked to your agency, or is no longer approved. A search with it answers `403`; `book_flight` answers `409` with the same code when the exemption of the search is not valid any more (search again without it).",
      "message": "The selected ACD exempt company is not linked to this agency."
    },
    {
      "code": "acd_exemption_currency",
      "status": 403,
      "type": "permission_error",
      "meaning": "ACD exemptions exist only for agencies with a DZD account.",
      "message": "ACD exemption is only available for agencies with a DZD account."
    },
    {
      "code": "invalid_identifier",
      "status": 400,
      "type": "validation_error",
      "meaning": "An id is not a valid token: letters, digits and `- _ . : = ~`, 1 to 128 characters.",
      "message": "`search_id` is not a valid identifier."
    },
    {
      "code": "invalid_booking_type",
      "status": 400,
      "type": "validation_error",
      "meaning": "`booking_type` is not `issue` or `hold` (any letter case). An unknown value never becomes an order.",
      "message": "`booking_type` must be \"issue\" (instant ticket) or \"hold\".",
      "param": "booking_type"
    },
    {
      "code": "invalid_passenger_type",
      "status": 400,
      "type": "validation_error",
      "meaning": "A passenger `type` is not one of `adult`, `senior`, `young_adult`, `child`, `seated_infant`, `infant` (or an accepted synonym)."
    },
    {
      "code": "invalid_gender",
      "status": 400,
      "type": "validation_error",
      "meaning": "A passenger `gender` is none of `M`, `F`, `MALE`, `FEMALE` (any letter case), `X`, `U` and `UNSPECIFIED` (a passport marked X)."
    },
    {
      "code": "invalid_phone_number",
      "status": 400,
      "type": "validation_error",
      "meaning": "`contact_details.phone_number` is not a phone number: 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 refused.",
      "param": "contact_details.phone_number"
    },
    {
      "code": "invalid_commission_override",
      "status": 400,
      "type": "validation_error",
      "meaning": "`commission_override` is not `{ type: percentage | fixed, value >= 0 }`.",
      "param": "commission_override"
    },
    {
      "code": "hold_not_supported",
      "status": 400,
      "type": "validation_error",
      "meaning": "`booking_type: hold` on an offer that cannot be held: read `supportHold` on the priced offer.",
      "message": "This offer cannot be held. Book it with booking_type issue."
    },
    {
      "code": "search_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "The `search_id` is unknown, older than 24 hours, a `requestId`, or belongs to another agency (the answer is the same).",
      "message": "Search not found or expired.",
      "param": "search_id"
    },
    {
      "code": "offer_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "The offer is not in that search: it comes from another supplier entry or another search.",
      "param": "offer_id"
    },
    {
      "code": "offer_not_priced",
      "status": 404,
      "type": "not_found",
      "meaning": "The offer was never priced, or its priced version is older than 12 hours: call `price_flight` first."
    },
    {
      "code": "offer_expired",
      "status": 410,
      "type": "not_found",
      "meaning": "The supplier says the offer is gone. Search and price again."
    },
    {
      "code": "offer_incomplete",
      "status": 410,
      "type": "not_found",
      "meaning": "The saved offer cannot be used any more. Search again."
    },
    {
      "code": "supplier_not_found",
      "status": 410,
      "type": "supplier_error",
      "meaning": "The supplier of the saved offer is no longer available. Search again."
    },
    {
      "code": "technology_type_unknown",
      "status": 410,
      "type": "not_found",
      "meaning": "The saved offer has a supplier technology FX-Port does not know any more. Search again."
    },
    {
      "code": "unsupported_supplier",
      "status": 400,
      "type": "validation_error",
      "meaning": "`get_ticket_number`: the booking is not an Amadeus, Travelport or Duffel booking, so its e-ticket number cannot be asked. Read the booking with `get_booking`.",
      "message": "Ticket-number retrieval is only supported for Amadeus, Travelport and Duffel bookings."
    },
    {
      "code": "offer_request_already_booked",
      "status": 409,
      "type": "supplier_error",
      "meaning": "The supplier says this search (Duffel: one booking per offer request) was already booked. Search again."
    },
    {
      "code": "offer_already_booked",
      "status": 409,
      "type": "supplier_error",
      "meaning": "The supplier says this offer was already booked."
    },
    {
      "code": "order_already_exists",
      "status": 409,
      "type": "supplier_error",
      "meaning": "The supplier says an order for this booking already exists."
    },
    {
      "code": "booking_in_progress",
      "status": 409,
      "type": "conflict",
      "meaning": "Another request is booking this offer right now. Read the booking before you retry."
    },
    {
      "code": "booking_lock_failed",
      "status": 409,
      "type": "conflict",
      "meaning": "The offer could not be locked for booking. Read the booking before you retry."
    },
    {
      "code": "validation_type",
      "status": 422,
      "type": "supplier_error",
      "meaning": "The supplier refused a detail of the booking (a field has the wrong type or format). `errors[0].field` names it."
    },
    {
      "code": "born_on_does_not_match",
      "status": 422,
      "type": "supplier_error",
      "meaning": "The date of birth does not match the passenger type of the offer (a child who is not 2 to 11 at departure, for example)."
    },
    {
      "code": "supplier_temporarily_unavailable",
      "status": 503,
      "type": "supplier_error",
      "meaning": "The supplier is skipped for a short while after repeated failures. Wait, then retry.",
      "message": "Supplier 'duffel_1' is temporarily unavailable (circuit breaker is open)."
    },
    {
      "code": "supplier_api_error",
      "status": 503,
      "type": "supplier_error",
      "meaning": "The supplier refused or failed on a price call. Retry once, otherwise search again."
    },
    {
      "code": "post_booking_failed",
      "status": 500,
      "type": "internal_error",
      "meaning": "The booking was created at the supplier but FX-Port could not finish storing it. Do not book again: look for the booking in your history and quote the request id."
    },
    {
      "code": "invalid_payment_type",
      "status": 400,
      "type": "validation_error",
      "meaning": "`payment_type` is not `balance`, `card` or `arc_bsp_cash`.",
      "param": "payment_type"
    },
    {
      "code": "invalid_cancellation_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "`cancellation_id` is not a valid token.",
      "param": "cancellation_id"
    },
    {
      "code": "invalid_booking_status",
      "status": 409,
      "type": "conflict",
      "meaning": "The booking is not in the status the call needs (`cancel_order` needs `awaiting_payment`).",
      "message": "Booking is 'confirmed', expected awaiting_payment"
    },
    {
      "code": "booking_update_failed",
      "status": 500,
      "type": "internal_error",
      "meaning": "The reservation was deleted but the FX-Port booking could not be updated. Read the booking and contact support with the request id."
    },
    {
      "code": "order_not_cancellable",
      "status": 200,
      "type": "supplier_error",
      "meaning": "The airline or the fare does not allow cancelling the order through the API (a `200` with `success: false`)."
    },
    {
      "code": "already_cancelled",
      "status": 200,
      "type": "supplier_error",
      "meaning": "The order was cancelled before (a `200` with `success: false`)."
    },
    {
      "code": "order_type_not_eligible_for_payment",
      "status": 200,
      "type": "supplier_error",
      "meaning": "A Duffel order that was bought instantly cannot be paid later (a `200` with `success: false`)."
    },
    {
      "code": "missing_booking_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "No `booking_id` (a JSON field for `get_acd_documents`, a form field for `upload_acd_documents`).",
      "message": "Missing required field: booking_id",
      "param": "booking_id"
    },
    {
      "code": "missing_traveler_ids",
      "status": 400,
      "type": "validation_error",
      "meaning": "An upload without `traveler_ids`.",
      "message": "Provide one traveler_ids value for each uploaded document.",
      "param": "traveler_ids"
    },
    {
      "code": "missing_documents",
      "status": 400,
      "type": "validation_error",
      "meaning": "An upload without a file in `documents`.",
      "message": "Provide at least one file in the documents field.",
      "param": "documents"
    },
    {
      "code": "document_count_mismatch",
      "status": 400,
      "type": "validation_error",
      "meaning": "`traveler_ids` and `documents` have different lengths: they are matched by order. The body has `received_traveler_ids` and `received_documents`."
    },
    {
      "code": "duplicate_traveler_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "A traveler appears twice in one upload. The body has `duplicate_traveler_ids`."
    },
    {
      "code": "too_many_documents",
      "status": 400,
      "type": "validation_error",
      "meaning": "More files than travelers on the booking. The body has `expected_travelers` and `received_documents`."
    },
    {
      "code": "unknown_traveler_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "A traveler id is not on the booking. `results[].error.expected_traveler_ids` lists the valid ones."
    },
    {
      "code": "empty_file",
      "status": 400,
      "type": "validation_error",
      "meaning": "A file has no content."
    },
    {
      "code": "invalid_replace_flag",
      "status": 400,
      "type": "validation_error",
      "meaning": "`replace` is not `true` or `false` (`1`, `yes`, `on`, `0`, `no`, `off` are read too).",
      "message": "replace must be 'true' or 'false'.",
      "param": "replace"
    },
    {
      "code": "validation_failed",
      "status": 400,
      "type": "validation_error",
      "meaning": "Several files of one upload have different problems; nothing was stored. Read `results[].error`."
    },
    {
      "code": "invalid_multipart",
      "status": 400,
      "type": "validation_error",
      "meaning": "The multipart body cannot be read, or has more than 40 files, 64 fields or a text field over 64 KiB."
    },
    {
      "code": "document_already_uploaded",
      "status": 409,
      "type": "conflict",
      "meaning": "The traveler already has a passport. Nothing was changed: send `replace=true` to replace it."
    },
    {
      "code": "acd_not_required",
      "status": 409,
      "type": "conflict",
      "meaning": "The booking does not need ACD documents (`get_acd_documents` answers `200` for it; only the upload is refused)."
    },
    {
      "code": "acd_waived",
      "status": 409,
      "type": "conflict",
      "meaning": "FX-Port marked ACD as not required for this booking."
    },
    {
      "code": "booking_not_eligible",
      "status": 409,
      "type": "conflict",
      "meaning": "The booking is cancelled, failed, voided, refunded or expired. The body has `booking_status`."
    },
    {
      "code": "travelers_not_available",
      "status": 409,
      "type": "conflict",
      "meaning": "The booking has no traveler data, so files cannot be matched. Contact support."
    },
    {
      "code": "concurrent_update",
      "status": 409,
      "type": "conflict",
      "meaning": "Another request changed the documents of this booking at the same moment. Read the state and send what is still missing."
    },
    {
      "code": "file_too_large",
      "status": 413,
      "type": "validation_error",
      "meaning": "A file is over 10 MiB (10,485,760 bytes). The error has `max_file_size_bytes`.",
      "message": "Uploaded document 'big.pdf' exceeds the 10 MB limit."
    },
    {
      "code": "unsupported_file_type",
      "status": 415,
      "type": "validation_error",
      "meaning": "The content of a file is not a PDF or a supported image (JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000, JPEG XL). The name and the `Content-Type` are not looked at."
    },
    {
      "code": "storage_upload_failed",
      "status": 502,
      "type": "internal_error",
      "meaning": "No file could be stored. Nothing changed: retry the request. Each `results[].error` has the fixed `reason: storage_error`."
    },
    {
      "code": "storage_unavailable",
      "status": 503,
      "type": "internal_error",
      "meaning": "Document storage is not available. Retry later."
    },
    {
      "code": "invalid_date",
      "status": 400,
      "type": "validation_error",
      "meaning": "A date is not a real calendar date written `YYYY-MM-DD`. A date written with a midnight time (`2026-12-01T00:00:00Z`) is read as that day, not refused; any other time of day is."
    },
    {
      "code": "date_in_past",
      "status": 400,
      "type": "validation_error",
      "meaning": "`checkin` is before yesterday (yesterday is accepted for time zones).",
      "param": "checkin"
    },
    {
      "code": "date_too_far",
      "status": 400,
      "type": "validation_error",
      "meaning": "`checkin` is more than 400 days ahead. `errors[0].details.max_days_ahead` is 400.",
      "param": "checkin"
    },
    {
      "code": "checkout_before_checkin",
      "status": 400,
      "type": "validation_error",
      "meaning": "`checkout` is not after `checkin`.",
      "param": "checkout"
    },
    {
      "code": "stay_too_long",
      "status": 400,
      "type": "validation_error",
      "meaning": "The stay is longer than 30 nights, the longest the supplier prices.",
      "param": "checkout"
    },
    {
      "code": "invalid_guests",
      "status": 400,
      "type": "validation_error",
      "meaning": "`guests` is not a non-empty list with one `{adults, children}` object per room.",
      "param": "guests"
    },
    {
      "code": "too_many_rooms",
      "status": 400,
      "type": "validation_error",
      "meaning": "More than 9 rooms in one request."
    },
    {
      "code": "invalid_adult_count",
      "status": 400,
      "type": "validation_error",
      "meaning": "A room has fewer than 1 or more than 6 adults, or `adults` is not a whole number (a whole number written as text is read)."
    },
    {
      "code": "too_many_children",
      "status": 400,
      "type": "validation_error",
      "meaning": "A room has more than 4 children."
    },
    {
      "code": "invalid_child_age",
      "status": 400,
      "type": "validation_error",
      "meaning": "A child age is not a whole number from 0 to 17 (a whole number written as text is read)."
    },
    {
      "code": "too_many_guests",
      "status": 400,
      "type": "validation_error",
      "meaning": "A room has more than 10 guests."
    },
    {
      "code": "invalid_rooms",
      "status": 400,
      "type": "validation_error",
      "meaning": "`rooms` of `book_hotel` is not a non-empty list of `{guests: [...]}` objects with 1 to 10 guests each.",
      "param": "rooms"
    },
    {
      "code": "invalid_radius",
      "status": 400,
      "type": "validation_error",
      "meaning": "`radius` is not a whole number of meters (a boolean, a fraction, `1e3`, text that is not plain digits). A whole number written as text is read; a number outside 100 to 70,000 is not an error: it is moved to the nearest bound.",
      "param": "radius"
    },
    {
      "code": "invalid_latitude",
      "status": 400,
      "type": "validation_error",
      "meaning": "`latitude` is not a number from -90 to 90 (a number written as text is read).",
      "param": "latitude"
    },
    {
      "code": "invalid_longitude",
      "status": 400,
      "type": "validation_error",
      "meaning": "`longitude` is not a number from -180 to 180 (a number written as text is read).",
      "param": "longitude"
    },
    {
      "code": "invalid_residency",
      "status": 400,
      "type": "validation_error",
      "meaning": "`residency` is not a two-letter ISO 3166-1 country code the hotel supplier knows (`zz`, `usa`, `gbr`, an empty text). A sub-territory code such as `GB-SCT`, `USPR` or `PT-MA` is read as its country, not refused.",
      "param": "residency"
    },
    {
      "code": "invalid_language",
      "status": 400,
      "type": "validation_error",
      "meaning": "`language` is not a language code such as `en`, `fr` or `zh_CN`. A longer tag such as `zh-Hans-CN` or `es-419` is read; `en-USA`, a 3-letter code or a text with spaces is refused.",
      "param": "language"
    },
    {
      "code": "invalid_query",
      "status": 400,
      "type": "validation_error",
      "meaning": "`query` is not a text of the right length: 2 to 100 characters for the name search, 1 to 64 for the location search (which answers this code with HTTP 422).",
      "param": "query"
    },
    {
      "code": "invalid_limit",
      "status": 400,
      "type": "validation_error",
      "meaning": "`limit` of the name search is not a whole number from 1 to 100 (a whole number written as text is read). `errors[0].details` carries `min` and `max` when the number is too large. `POST /hotels/search-by-name` caps a number above 100 (with a `coerced_value` warning); `GET /hotels/name-search` refuses it.",
      "param": "limit"
    },
    {
      "code": "invalid_min_score",
      "status": 400,
      "type": "validation_error",
      "meaning": "`min_score` of the name search is not a number from 0 to 100 (a number written as text is read). `errors[0].details` carries `min` and `max`.",
      "param": "min_score"
    },
    {
      "code": "invalid_hotel_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "A hotel id is not 1 to 128 characters: letters, digits and `_ . : -` (spaces around it are trimmed; a positive JSON integer, the numeric supplier id, is read as its text).",
      "param": "hotel_id"
    },
    {
      "code": "invalid_hotel_ids",
      "status": 400,
      "type": "validation_error",
      "meaning": "`hotel_ids` is not a list of hotel ids.",
      "param": "hotel_ids"
    },
    {
      "code": "too_many_ids",
      "status": 400,
      "type": "validation_error",
      "meaning": "More than 200 hotel ids in `hotel_ids` of `get_hotel_info`.",
      "param": "hotel_ids"
    },
    {
      "code": "invalid_rate_hash",
      "status": 400,
      "type": "validation_error",
      "meaning": "`book_hash` is not a rate token taken from `get_rates`.",
      "param": "book_hash"
    },
    {
      "code": "invalid_price_increase",
      "status": 400,
      "type": "validation_error",
      "meaning": "`price_increase_percent` is not a number from 0 to 100 (a number written as text is read; `null` means not set).",
      "param": "price_increase_percent"
    },
    {
      "code": "invalid_images_limit",
      "status": 400,
      "type": "validation_error",
      "meaning": "`images_limit` is not an integer from 0 to 30 (`get_hotel_info`, `POST /hotels/search` and `POST /hotels/details`). `errors[0].details` carries `min` and `max`.",
      "param": "images_limit"
    },
    {
      "code": "invalid_detail_level",
      "status": 400,
      "type": "validation_error",
      "meaning": "`detail_level` is not `minimal` (alias `mini`) or `full`.",
      "param": "detail_level"
    },
    {
      "code": "invalid_fields",
      "status": 400,
      "type": "validation_error",
      "meaning": "`fields` is not a list of at most 40 top-level field names separated by commas.",
      "param": "fields"
    },
    {
      "code": "invalid_send_email",
      "status": 400,
      "type": "validation_error",
      "meaning": "`send_email` of `book_hotel` is not `true` or `false`. The text `true` or `false` (also `1`, `0`, `yes`, `no`, `on`, `off`) is read as the boolean, `null` means the default; any other value is refused before anything is booked.",
      "param": "send_email"
    },
    {
      "code": "invalid_is_child",
      "status": 400,
      "type": "validation_error",
      "meaning": "`is_child` of a guest of `book_hotel` is not `true` or `false` (the text `true` or `false`, `1` and `0` are read as the boolean). `errors[0].field` names the guest, for example `rooms[0].guests[1].is_child`."
    },
    {
      "code": "booking_mode_not_available",
      "status": 400,
      "type": "validation_error",
      "meaning": "The body of `book_hotel` has a `booking_mode` other than `standard`. A booking made with an API key always books and confirms the rate: do not send `booking_mode`.",
      "param": "booking_mode"
    },
    {
      "code": "invalid_request",
      "status": 400,
      "type": "validation_error",
      "meaning": "The body of `book_hotel` names two booking ids: `partner.partner_order_id` differs from `booking_id`. Send the booking id once, as `booking_id`. (A `book_hash` or `hotel_id` that differs from the pre-booked offer is not an error: it is ignored, with an `ignored_field` warning.)"
    },
    {
      "code": "invalid_status",
      "status": 400,
      "type": "validation_error",
      "meaning": "The booking is not `confirmed`: only a confirmed booking can be cancelled. `pending`, `failed` and already cancelled bookings cannot."
    },
    {
      "code": "rate_not_found",
      "status": 400,
      "type": "supplier_error",
      "meaning": "The supplier does not know the rate token any more: the rate expired, was never valid, or its price rose by more than `price_increase_percent`. Call `get_rates` again."
    },
    {
      "code": "order_not_found",
      "status": 400,
      "type": "supplier_error",
      "meaning": "The supplier has no order for this booking id: `pre_book` did not open one (the hotel is not bookable) or the order expired."
    },
    {
      "code": "incorrect_guests_number",
      "status": 400,
      "type": "supplier_error",
      "meaning": "The guests of `rooms` do not match the searched occupancy, or a child lacks `is_child` and `age`."
    },
    {
      "code": "insufficient_balance",
      "status": 402,
      "type": "payment_error",
      "meaning": "The balance of your agency is lower than the amount to debit. Live only: the sandbox debits nothing. Top up the balance, then repeat the request."
    },
    {
      "code": "hotel_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "The supplier has no hotel with this id."
    },
    {
      "code": "pdf_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "The booking exists but its voucher is not made yet. It is made right after the booking is `confirmed`, sometimes up to 10 seconds later: call `get_voucher` again in 2 seconds."
    },
    {
      "code": "hotel_not_bookable_in_sandbox",
      "status": 409,
      "type": "conflict",
      "meaning": "A sandbox key can book the supplier test hotels only: `test_hotel_do_not_book` (any residency) and `test_hotel` (residency `dz`, `ru` or `us`).",
      "param": "hotel_id"
    },
    {
      "code": "duplicate_booking",
      "status": 409,
      "type": "conflict",
      "meaning": "A booking already exists for this id. Do not call `book_hotel` again: read its status with `GET /bookings/{booking_id}?product=hotels`. `errors[0].details` carries the `booking_id` and, for the agency that owns the booking, its `status`.",
      "param": "booking_id"
    },
    {
      "code": "cached_offer_required",
      "status": 409,
      "type": "conflict",
      "meaning": "The offer locked by `pre_book` is not found: it is older than one hour (live), or another agency locked it. Lock the rate again with `pre_book` and your own key, then book. `errors[0].details.partner_order_id` is the id you sent."
    },
    {
      "code": "trusted_offer_pricing_missing",
      "status": 409,
      "type": "conflict",
      "meaning": "The locked offer has no usable price: lock the rate again with `pre_book`."
    },
    {
      "code": "supplier_rejected_request",
      "status": 422,
      "type": "supplier_error",
      "meaning": "The hotel supplier refused the request of `search_advanced` although it is well formed. Check dates, guests, residency and radius. `errors[0].details.supplier_code` carries the supplier's code when it has a plain one."
    },
    {
      "code": "supplier_rate_limited",
      "status": 429,
      "type": "supplier_error",
      "meaning": "The hotel supplier receives too many requests (`get_hotel_info`). Retry in a minute with backoff."
    },
    {
      "code": "supplier_error",
      "status": 502,
      "type": "supplier_error",
      "meaning": "The hotel supplier could not process the request right now. Retry in a few minutes with backoff."
    },
    {
      "code": "supplier_invalid_response",
      "status": 502,
      "type": "supplier_error",
      "meaning": "The hotel supplier answered in a form that could not be used. After `book_hotel` or `cancel_hotel` the call may have been processed: read the booking before you repeat it. On `pre_book` the same code with HTTP 400 means that the supplier priced the rate but returned no usable offer: call `get_rates` again."
    },
    {
      "code": "supplier_unavailable",
      "status": 503,
      "type": "supplier_error",
      "meaning": "The hotel supplier cannot be reached. Nothing was processed: retry in a few minutes with backoff."
    },
    {
      "code": "supplier_timeout",
      "status": 503,
      "type": "supplier_error",
      "meaning": "The hotel supplier did not answer in time. A cancellation may still have happened: read the booking before you repeat `cancel_hotel`."
    },
    {
      "code": "service_busy",
      "status": 503,
      "type": "internal_error",
      "meaning": "The hotel name search is busy. Retry after the seconds in `Retry-After`."
    },
    {
      "code": "invalid_package_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "`package_id` is not a package id: 1 to 128 letters, digits, `-`, `_` or `.`.",
      "param": "package_id"
    },
    {
      "code": "invalid_iccid",
      "status": 400,
      "type": "validation_error",
      "meaning": "The ICCID is not 18 to 22 digits. Groups of spaces or hyphens are dropped first, but any other character makes it invalid.",
      "param": "iccid"
    },
    {
      "code": "too_many_iccids",
      "status": 400,
      "type": "validation_error",
      "meaning": "`iccids` lists more ICCIDs than an API key may ask for at once (50).",
      "param": "iccids"
    },
    {
      "code": "invalid_email",
      "status": 400,
      "type": "validation_error",
      "meaning": "`email` is not an email address (at most 254 characters).",
      "param": "email"
    },
    {
      "code": "invalid_phone",
      "status": 400,
      "type": "validation_error",
      "meaning": "`phone` has fewer than 3 digits or more than 32 characters.",
      "param": "phone"
    },
    {
      "code": "invalid_name",
      "status": 400,
      "type": "validation_error",
      "meaning": "`first_name` or `last_name` is not a string of at most 100 characters.",
      "param": "first_name"
    },
    {
      "code": "invalid_description",
      "status": 400,
      "type": "validation_error",
      "meaning": "`description` is not a string of at most 500 characters.",
      "param": "description"
    },
    {
      "code": "invalid_quantity",
      "status": 400,
      "type": "validation_error",
      "meaning": "`quantity` is not an integer between 1 and 50. The value is otherwise ignored: every order creates one eSIM.",
      "param": "quantity"
    },
    {
      "code": "invalid_agent_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "`agent_id` is not the UUID of a user of your agency.",
      "param": "agent_id"
    },
    {
      "code": "invalid_booking_id",
      "status": 400,
      "type": "validation_error",
      "meaning": "The booking reference is not a valid booking id: it is empty, too long (100 characters on the eSIM and account endpoints, 256 on the hotel ones) or has characters a booking id never has (letters, digits and `.`, `_`, `:`, `-` are allowed, `=`, `+` and `~` too on the eSIM and account endpoints).",
      "param": "booking_id"
    },
    {
      "code": "invalid_locale",
      "status": 400,
      "type": "validation_error",
      "meaning": "`locale` is not a language code or locale such as `en`, `zh-Hans`, `pt_BR` or `fr_FR.UTF-8` (or an `Accept-Language` value).",
      "param": "locale"
    },
    {
      "code": "invalid_country",
      "status": 400,
      "type": "validation_error",
      "meaning": "`country` is neither a country slug such as `france` nor a country name such as `United States`: it has punctuation, control characters or non-Latin letters.",
      "param": "country"
    },
    {
      "code": "invalid_type",
      "status": 400,
      "type": "validation_error",
      "meaning": "`type` is not a short name such as `local` or `global`.",
      "param": "type"
    },
    {
      "code": "invalid_search",
      "status": 400,
      "type": "validation_error",
      "meaning": "`search` is longer than 100 characters or contains control characters.",
      "param": "search"
    },
    {
      "code": "iccid_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "The ICCID is not an eSIM that your agency ordered in this environment.",
      "param": "iccid"
    },
    {
      "code": "booking_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "No booking of your agency in this environment has this reference (for the product asked, when `product` is set).",
      "param": "booking_id"
    },
    {
      "code": "duplicate_request",
      "status": 409,
      "type": "conflict",
      "meaning": "An order for the same package, or a topup of the same eSIM and package, is already being processed; or, on live, the latest confirmed order of your agency is for the same package (topup: the latest confirmed topup of the same eSIM) and is less than 60 seconds old."
    },
    {
      "code": "balance_debit_failed",
      "status": 500,
      "type": "internal_error",
      "meaning": "The supplier created the eSIM but the balance debit failed. Do not retry: contact support with the order code."
    },
    {
      "code": "booking_record_failed",
      "status": 500,
      "type": "internal_error",
      "meaning": "The supplier created the eSIM but the booking could not be saved. Do not retry: contact support with the order code."
    },
    {
      "code": "supplier_response_invalid",
      "status": 500,
      "type": "supplier_error",
      "meaning": "The supplier answered in a way that could not be processed. Do not retry: support has been alerted with the booking reference."
    },
    {
      "code": "balance_lock_timeout",
      "status": 503,
      "type": "internal_error",
      "meaning": "Another balance operation of your agency in the same currency is still running. Retry in a few seconds."
    },
    {
      "code": "duplicate_check_unavailable",
      "status": 503,
      "type": "internal_error",
      "meaning": "The duplicate check could not run, so nothing was ordered (live). Retry in a few seconds."
    },
    {
      "code": "agency_id_not_allowed",
      "status": 400,
      "type": "validation_error",
      "meaning": "An API key sent `agency_id`. The agency is taken from the key: remove the parameter.",
      "param": "agency_id"
    },
    {
      "code": "invalid_date_range",
      "status": 400,
      "type": "validation_error",
      "meaning": "`date_from` is after `date_to`.",
      "param": "date_from"
    },
    {
      "code": "invalid_product",
      "status": 400,
      "type": "validation_error",
      "meaning": "`product` is not one of `all`, `esims`, `flights`, `hotels`, `transfers` (any case; the singular names are read as the plural). The error lists them in `allowed`.",
      "param": "product"
    },
    {
      "code": "balance_not_found",
      "status": 404,
      "type": "not_found",
      "meaning": "No balance of your agency has this id (live). The sandbox has no balances and answers any id with an empty list.",
      "param": "balance_id"
    }
  ],
  "x-error-types": [
    {
      "type": "authentication_error",
      "reaction": "Fix the key or the way it is sent (Authorization: Bearer fxp_...). Retrying the same request will fail again."
    },
    {
      "type": "permission_error",
      "reaction": "The key is valid but is not allowed to do this: it needs read_write permission, an allowed IP address or an approved agency. Do not retry."
    },
    {
      "type": "validation_error",
      "reaction": "The request is wrong: read `errors[].field` and `errors[].message`, fix it and send it again. Do not retry unchanged."
    },
    {
      "type": "not_found",
      "reaction": "The object does not exist for your agency (or the id is wrong). Do not retry; check the id and the environment of your key."
    },
    {
      "type": "conflict",
      "reaction": "The request is valid but cannot be done in the current state (already booked, already uploaded, duplicate). Read the code and the details; do not repeat it blindly."
    },
    {
      "type": "payment_error",
      "reaction": "Your agency balance cannot cover the request (insufficient balance, live only). Top up the balance, then repeat the request; the details carry the amounts."
    },
    {
      "type": "rate_limit_error",
      "reaction": "Too many requests. Wait for the time in the Retry-After header, then retry with exponential backoff and jitter."
    },
    {
      "type": "supplier_error",
      "reaction": "The airline, hotel or eSIM supplier refused or could not answer. Read the message: some are permanent (fares gone, sold out), some are temporary. Search again for fares and rates; retry temporary errors once or twice with backoff."
    },
    {
      "type": "internal_error",
      "reaction": "Something failed on our side. Retry once after a short wait; if it persists contact dev@fx-port.com with the request_id."
    }
  ]
}
