{
  "info": {
    "_postman_id": "fxport-api-flights-sandbox-v2",
    "name": "FX-Port API - Flights (Sandbox)",
    "description": "FX-Port Flights API: Postman collection of the documentation at https://fx-port.com/docs (generated 2026-11-01).\n\n**Setup.** Set the collection variable (or an environment) `api_key` to your own key: `fxp_test_...` for the sandbox, `fxp_live_...` for live. The base URL is the same for both, the key decides. The collection holds no key: add yours, and never commit it.\n\n**Names.** Requests use snake_case names. Previous camelCase spellings still work; the response then carries a `warnings` array and `X-FX-Deprecated-Fields` header that name the spelling to use. Add `?naming=snake` to any request for snake_case keys in the response.\n\n**Errors.** Every failure has the same body: `errors[].code`, `errors[].type`, `errors[].message`, and a `request_id` to quote to support.\n\nFX-Port Flights API — sandbox/API-key collection.\n\nHold bookings are capability-based: run the hold flow only when the selected repriced offer returns supportHold=true. A PNR can expire before its displayed deadline, and only a successful pay_order response confirms issuance.\n\nSend one age for every child and infant, as the examples do: ages that do not match the counts are corrected by FX-Port and reported in the warnings of the response. Passenger limits can be supplier-specific. During automatic or multi-supplier searches, incompatible suppliers are skipped silently and compatible results are returned. HTTP 400 is returned only when no selected supplier can serve the passenger mix. Some suppliers require one adult or senior per under-2 passenger; children aged 2-11 are not subject to that limit.\n\nVoid, exchange and re-issue of issued tickets are handled by the FX-Port team (dev@fx-port.com). Refunds of issued Duffel orders are self-service: quote them with cancel_quote, then confirm them with confirm_cancel.\n\nCovered endpoints:\n- Flight search (one-way, round-trip, family, cabins, flexible dates, filters, single/multiple supplier selection)\n- Flight pricing\n- Flight booking (hold and instant issue)\n- Held-order flows (quote, pay, cancel)\n- Booking, ticket and ticket-number reads\n- Refunds of issued Duffel orders (cancel_quote, confirm_cancel)\n- ACD passport documents (get_acd_documents, upload_acd_documents)\n- Airport autocomplete and most searched airports\n\nQuestions: dev@fx-port.com",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.fx-port.com"
    },
    {
      "key": "api_key",
      "value": "fxp_test_YOUR_KEY"
    },
    {
      "key": "dep",
      "value": ""
    },
    {
      "key": "ret",
      "value": ""
    },
    {
      "key": "fallback_dep",
      "value": ""
    },
    {
      "key": "search_id",
      "value": ""
    },
    {
      "key": "offer_id",
      "value": ""
    },
    {
      "key": "supplier_id",
      "value": ""
    },
    {
      "key": "order_id",
      "value": ""
    },
    {
      "key": "booking_id",
      "value": "V64R4V"
    },
    {
      "key": "male_first_1",
      "value": "Adam"
    },
    {
      "key": "male_last_1",
      "value": "Martin"
    },
    {
      "key": "female_first_1",
      "value": "Sarah"
    },
    {
      "key": "female_last_1",
      "value": "Martin"
    },
    {
      "key": "child_first_1",
      "value": "Noah"
    },
    {
      "key": "child_last_1",
      "value": "Martin"
    },
    {
      "key": "infant_first_1",
      "value": "Lina"
    },
    {
      "key": "infant_last_1",
      "value": "Martin"
    },
    {
      "key": "adult1_dob",
      "value": "1989-03-12"
    },
    {
      "key": "adult2_dob",
      "value": "1991-08-27"
    },
    {
      "key": "child1_age",
      "value": "8"
    },
    {
      "key": "child1_dob",
      "value": "2018-04-19"
    },
    {
      "key": "infant1_dob",
      "value": "2025-02-06"
    },
    {
      "key": "adult1_passport",
      "value": "A12345678"
    },
    {
      "key": "adult2_passport",
      "value": "B87654321"
    },
    {
      "key": "child1_passport",
      "value": "C11223344"
    },
    {
      "key": "infant1_passport",
      "value": "D55667788"
    },
    {
      "key": "adult1_pexp",
      "value": "2031-07-14"
    },
    {
      "key": "adult2_pexp",
      "value": "2032-05-09"
    },
    {
      "key": "child1_pexp",
      "value": "2030-11-22"
    },
    {
      "key": "infant1_pexp",
      "value": "2033-01-30"
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const now = new Date();",
          "const fmt = d => d.toISOString().slice(0, 10);",
          "const rnd = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;",
          "const pick = arr => arr[Math.floor(Math.random() * arr.length)];",
          "const depDate = new Date(now.getTime() + 30 * 86400000);",
          "const dobForAge = age => fmt(new Date(now.getFullYear() - age, rnd(0, 11), rnd(1, 28)));",
          "const futureDate = (minY, maxY) => fmt(new Date(now.getFullYear() + rnd(minY, maxY), rnd(0, 11), rnd(1, 28)));",
          "const passport = () => 'ABCDEFGHJKLMNPRSTUVWXYZ'[rnd(0, 21)] + String(rnd(10000000, 99999999));",
          "",
          "// Travel dates are always refreshed so examples stay in the near future.",
          "pm.collectionVariables.set('dep', fmt(depDate));",
          "pm.collectionVariables.set('fallback_dep', fmt(new Date(now.getTime() + 40 * 86400000)));",
          "pm.collectionVariables.set('ret', fmt(new Date(now.getTime() + 37 * 86400000)));",
          "",
          "// Seed a randomized, internally-consistent passenger identity. Re-seeded on every",
          "// Search so booking DOBs/ages cascade from the search that produced the offer.",
          "const requestName = (pm.info && pm.info.requestName) || '';",
          "const isSearch = /search/i.test(requestName);",
          "if (isSearch || !pm.collectionVariables.get('child1_dob')) {",
          "  // Diverse, real-world first/last names from many origins. ASCII/Latin ONLY —",
          "  // no accents or diacritics (write 'Jose' not 'José') and no non-Latin scripts.",
          "  const maleFirst = ['Adam', 'Liam', 'Mehdi', 'Karim', 'Sofiane', 'Diego', 'Mateo', 'Lucas', 'Omar', 'Youssef', 'Ivan', 'Dmitri', 'Kenji', 'Hiroshi', 'Wei', 'Chen', 'Arjun', 'Rohan', 'Kwame', 'Chidi', 'Sven', 'Lars', 'Marco', 'Paolo', 'Andres', 'Rafael', 'Noah', 'Ethan', 'Hassan', 'Bilal', 'Tariq', 'Sasha', 'Nikola', 'Emeka', 'Thabo', 'Santiago', 'Gabriel', 'Henrik', 'Matteo', 'Ahmad'];",
          "  const femaleFirst = ['Sarah', 'Emma', 'Lina', 'Amel', 'Yasmine', 'Sofia', 'Maria', 'Valentina', 'Aisha', 'Fatima', 'Anya', 'Olga', 'Yuki', 'Sakura', 'Mei', 'Ling', 'Priya', 'Ananya', 'Amara', 'Zainab', 'Ingrid', 'Freya', 'Giulia', 'Chiara', 'Camila', 'Isabela', 'Olivia', 'Hana', 'Noor', 'Layla', 'Nadia', 'Mira', 'Zoe', 'Adaeze', 'Naledi', 'Lucia', 'Elena', 'Astrid', 'Bianca', 'Salma'];",
          "  const childFirst = ['Noah', 'Rayan', 'Sami', 'Aya', 'Maya', 'Nour', 'Leo', 'Mia', 'Kai', 'Aria', 'Ravi', 'Ana', 'Nina', 'Theo', 'Ivy', 'Luca', 'Zara', 'Finn'];",
          "  const infantFirst = ['Lina', 'Sami', 'Nour', 'Malak', 'Leo', 'Mia', 'Kai', 'Ivy', 'Ana', 'Theo', 'Zoe', 'Ravi'];",
          "  const surnames = ['Benali', 'Haddad', 'Smith', 'Johnson', 'Garcia', 'Martinez', 'Rossi', 'Ferrari', 'Kim', 'Park', 'Nguyen', 'Tran', 'Kumar', 'Patel', 'Okafor', 'Mensah', 'Andersson', 'Nielsen', 'Muller', 'Weber', 'Silva', 'Santos', 'Ivanov', 'Petrov', 'Suzuki', 'Tanaka', 'Wang', 'Li', 'Cohen', 'Khan'];",
          "  const family = pick(surnames);",
          "  const childAge = rnd(2, 11);",
          "",
          "  pm.collectionVariables.set('male_first_1', pick(maleFirst));",
          "  pm.collectionVariables.set('male_last_1', family);",
          "  pm.collectionVariables.set('female_first_1', pick(femaleFirst));",
          "  pm.collectionVariables.set('female_last_1', family);",
          "  pm.collectionVariables.set('child_first_1', pick(childFirst));",
          "  pm.collectionVariables.set('child_last_1', family);",
          "  pm.collectionVariables.set('infant_first_1', pick(infantFirst));",
          "  pm.collectionVariables.set('infant_last_1', family);",
          "",
          "  pm.collectionVariables.set('adult1_dob', dobForAge(rnd(28, 55)));",
          "  pm.collectionVariables.set('adult2_dob', dobForAge(rnd(28, 55)));",
          "  pm.collectionVariables.set('child1_age', String(childAge));",
          "  // Anchor child/infant DOBs to the departure date so age-at-travel matches the searched band.",
          "  pm.collectionVariables.set('child1_dob', fmt(new Date(depDate.getFullYear() - childAge, depDate.getMonth() - rnd(1, 6), rnd(1, 28))));",
          "  pm.collectionVariables.set('infant1_dob', fmt(new Date(depDate.getFullYear(), depDate.getMonth() - rnd(2, 20), rnd(1, 28))));",
          "",
          "  pm.collectionVariables.set('adult1_passport', passport());",
          "  pm.collectionVariables.set('adult2_passport', passport());",
          "  pm.collectionVariables.set('child1_passport', passport());",
          "  pm.collectionVariables.set('infant1_passport', passport());",
          "",
          "  pm.collectionVariables.set('adult1_pexp', futureDate(3, 9));",
          "  pm.collectionVariables.set('adult2_pexp', futureDate(3, 9));",
          "  pm.collectionVariables.set('child1_pexp', futureDate(3, 9));",
          "  pm.collectionVariables.set('infant1_pexp', futureDate(3, 9));",
          "}"
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "try {",
          "  const body = pm.response.json();",
          "  for (const supplier of (body.suppliers || [])) {",
          "    const searchId = supplier.searchId || supplier.search_id;",
          "    const supplierId = supplier.supplierId || supplier.supplier_id || (supplier.meta && (supplier.meta.supplierId || supplier.meta.supplier_id));",
          "    const offers = (supplier.results && supplier.results.offers) || supplier.offers || [];",
          "    if (supplier.success && searchId) {",
          "      pm.collectionVariables.set('search_id', searchId);",
          "      if (supplierId) pm.collectionVariables.set('supplier_id', supplierId);",
          "      if (offers.length && offers[0].id) pm.collectionVariables.set('offer_id', offers[0].id);",
          "      break;",
          "    }",
          "  }",
          "} catch (error) {}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "01 - Flight Search",
      "description": "Search endpoints require a read or read+write key.\n\nPassenger age-array lengths must match their counts. Passenger limits can be supplier-specific. Incompatible suppliers are skipped silently when another selected supplier can return results; HTTP 400 is returned only when no selected supplier remains compatible. Some suppliers require seated_infants + infants <= adults + seniors, while children aged 2-11 are not subject to that limit.\n\nSupplier selection: omit selectors to call all enabled suppliers; use supplier_id for one supplier, supplier_ids for an exact enabled subset, preferred_suppliers for a lenient subset, and max_suppliers to cap automatic selection by priority.",
      "item": [
        {
          "name": "Search - One Way Economy",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"ORY\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Basic one-way search. Test script saves search_id, supplier_id, and the first offer_id for downstream requests.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Round Trip Economy",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Basic round-trip search without market country filters.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Family",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 2,\n    \"children\": 1,\n    \"child_ages\": [{{child1_age}}],\n    \"infants\": 1,\n    \"infant_ages\": [1]\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Round-trip search for 2 adults, 1 child, and 1 lap infant. child_ages and infant_ages are both present and match their counts. The generated child age is reused as the child's date_of_birth in 'Book Flight - Family Example', so search and booking remain consistent. Run this before the family booking.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Duffel - 1 Adult + 3 Children",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Duffel returns the full family', () => {",
                  "  pm.response.to.have.status(200);",
                  "  const supplier = (pm.response.json().suppliers || [])[0] || {};",
                  "  const offers = ((supplier.results || {}).offers || []);",
                  "  const types = (((offers[0] || {}).travelerPricing) || []).map(item => item.travelerType);",
                  "  pm.expect(offers.length).to.be.above(0);",
                  "  pm.expect(types).to.eql(['ADULT', 'CHILD', 'CHILD', 'CHILD']);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"CDG\",\n  \"destination\": \"LHR\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1,\n    \"children\": 3,\n    \"child_ages\": [8, 6, 4]\n  },\n  \"supplier_id\": \"duffel_1\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "One adult may travel with multiple normal children. Verified through Duffel sandbox search, price, and issue.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Duffel - Age 2 Child + Lap Infant",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Duffel returns child and lap infant', () => {",
                  "  pm.response.to.have.status(200);",
                  "  const supplier = (pm.response.json().suppliers || [])[0] || {};",
                  "  const offers = ((supplier.results || {}).offers || []);",
                  "  const types = (((offers[0] || {}).travelerPricing) || []).map(item => item.travelerType);",
                  "  pm.expect(offers.length).to.be.above(0);",
                  "  pm.expect(types).to.eql(['ADULT', 'CHILD', 'HELD_INFANT']);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"CDG\",\n  \"destination\": \"LHR\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1,\n    \"children\": 1,\n    \"child_ages\": [2],\n    \"infants\": 1,\n    \"infant_ages\": [0]\n  },\n  \"supplier_id\": \"duffel_1\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "A traveler who is truly age 2 is a normal child, not a seated infant. Verified through Duffel sandbox search, price, and issue with one lap infant.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Complete Passenger Mix - Seated Infant",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "const suppliers = (body.suppliers || []).filter(s => s.success && (((s.results || {}).offers || []).length > 0));",
                  "const supplierIds = suppliers.map(s => ((s.meta || {}).supplierId || s.supplierId));",
                  "pm.test('At least one requested supplier returned offers', () => {",
                  "  pm.expect(body.success).to.eql(true);",
                  "  pm.expect(supplierIds.length).to.be.above(0);",
                  "});",
                  "pm.test('Every supplier preserves all six traveler types', () => {",
                  "  suppliers.forEach(s => {",
                  "    const travelerPricing = s.results.offers[0].travelerPricing || [];",
                  "    const travelerTypes = travelerPricing.map(item => item.travelerType);",
                  "    pm.expect(travelerPricing.length).to.eql(6);",
                  "    pm.expect(travelerTypes).to.include.members(['ADULT', 'CHILD', 'HELD_INFANT', 'SEATED_INFANT']);",
                  "  });",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 2,\n    \"seniors\": 0,\n    \"young_adults\": 0,\n    \"children\": 2,\n    \"child_ages\": [10, 6],\n    \"infants\": 1,\n    \"infant_ages\": [1],\n    \"seated_infants\": 1,\n    \"seated_infant_ages\": [1]\n  },\n  \"supplier_ids\": [\"amadeus_aqc_dz_1\", \"duffel_1\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Validated complete passenger mix against multiple suppliers: 2 adults, 2 children, 1 lap infant, and 1 seated infant. Two adult hosts satisfy suppliers that require one adult/senior per under-2 passenger. Incompatible suppliers are skipped silently when another selected supplier returns results.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Supplier Fallback - 1 Adult + Seated and Lap Infant",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (pm.response.code === 200) {",
                  "  pm.test('Compatible results do not include a compatibility warning', () => {",
                  "    const offers = (body.suppliers || []).flatMap(s => ((s.results || {}).offers || []));",
                  "    pm.expect(offers.length).to.be.above(0);",
                  "    pm.expect(body.error).not.to.eql('SUPPLIER_PASSENGER_COMBINATION_UNSUPPORTED');",
                  "  });",
                  "}",
                  "pm.test('Response follows supplier availability contract', () => {",
                  "  pm.expect([200, 503]).to.include(pm.response.code);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{fallback_dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1,\n    \"seated_infants\": 1,\n    \"seated_infant_ages\": [0],\n    \"infants\": 1,\n    \"infant_ages\": [0]\n  },\n  \"flexible\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Supplier-specific passenger limits are resolved automatically on the stable ALG-CDG test market. Incompatible suppliers are skipped silently. If any compatible supplier returns offers, the response is HTTP 200 without a compatibility warning; HTTP 400 is used only when none remain compatible.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Adult Senior Young - Duffel",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "const supplier = (body.suppliers || [])[0] || {};",
                  "const offers = (supplier.results && supplier.results.offers) || [];",
                  "const travelerPricing = (offers[0] && offers[0].travelerPricing) || [];",
                  "pm.test('Supplier returned a three-traveler offer', () => {",
                  "  pm.expect(body.success).to.eql(true);",
                  "  pm.expect(supplier.success).to.eql(true);",
                  "  pm.expect(offers.length).to.be.above(0);",
                  "  pm.expect(travelerPricing.length).to.eql(3);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1,\n    \"seniors\": 1,\n    \"young_adults\": 1,\n    \"young_adult_ages\": [16]\n  },\n  \"supplier_id\": \"duffel_1\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Validated mixed-category search for one adult, one senior, and one 16-year-old young traveler. Duffel prices seniors as adults internally; FX-Port preserves the requested logical traveler types in unified responses.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Business Class",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"business\",\n  \"passengers\": {\n    \"adults\": 1\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "One-way search restricted to the business cabin (cabin_class=business).\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Premium Economy",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"premium_economy\",\n  \"passengers\": {\n    \"adults\": 1\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "One-way search restricted to the premium economy cabin (cabin_class=premium_economy).\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - All Cabin Classes",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"any\",\n  \"passengers\": {\n    \"adults\": 1\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Search across every cabin class. Set cabin_class=any to send no cabin restriction to the suppliers, returning offers in all available cabins.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - All Suppliers Flexible",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"flexible\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Search all enabled suppliers at once (no supplier_id) with flexible=true. Regular offers may come from every compatible supplier; suppliers with flexible-date support may additionally return a +/-3-day flexPrices matrix. Always check for matrix presence.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - One Way All Suppliers Flexible",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"flexible\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "One-way search across all enabled suppliers with flexible=true. Suppliers without flexible-date support can still return regular offers; capable suppliers may also return a flexPrices calendar. Do not assume every supplier populates the matrix.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Round Trip All Suppliers Include Airline",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"included_airlines\": [\"AH\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Round-trip search across all enabled suppliers filtered to a specific airline. included_airlines restricts results to offers operated or marketed by the listed IATA codes. Suppliers that do not carry that airline will return empty offers.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Round Trip All Suppliers Exclude Airline",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"excluded_airlines\": [\"AH\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Round-trip search across all enabled suppliers excluding a specific airline. excluded_airlines removes offers marketed or operated by the listed IATA codes from all suppliers.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Direct Only",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"ORY\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"direct\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Direct-only search. The backend maps direct=true to stops=0.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Refundable Only",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"refundable\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Sends a 'refundable: true' hint. Not a guaranteed post-filter on every supplier technology — a search can still return a mix of refundable and non-refundable offers. Always check conditions.refundBeforeDeparture.allowed on each returned offer rather than assuming every offer is refundable.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Checked Bags Only",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"checked_bags\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Search filtered to offers with checked baggage included when supported by the supplier.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Open Jaw",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"segments\": [\n    {\n      \"origin\": \"ALG\",\n      \"destination\": \"CDG\",\n      \"date\": \"{{dep}}\"\n    },\n    {\n      \"origin\": \"ORY\",\n      \"destination\": \"ALG\",\n      \"date\": \"{{ret}}\"\n    }\n  ],\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Open-jaw search using exactly two non-reversed segments. Multi-destination search is not currently listed in this public collection.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Include Airlines Allow-List",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"included_airlines\": [\n    \"AF\",\n    \"AH\"\n  ]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Restricts results to the listed IATA carrier codes. 'preferred_airlines' is accepted as a compatibility alias with identical (strict allow-list) behavior — it is not a soft ranking preference. 'max_price' also exists but is not enforced by every supplier yet (currently a no-op for Duffel); do not rely on it as a hard cap — always check each offer's own pricing.finalPrice client-side.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          },
          "description": "Restricts results to the listed IATA carrier codes. 'preferred_airlines' is accepted as a compatibility alias with identical (strict allow-list) behavior — it is not a soft ranking preference. 'max_price' also exists but is not enforced by every supplier yet (currently a no-op for Duffel); do not rely on it as a hard cap — always check each offer's own pricing.finalPrice client-side."
        },
        {
          "name": "Search - Selected Suppliers Array (Exact Subset)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_ids\": [\n    \"amadeus_aqc_dz_1\",\n    \"duffel_1\"\n  ]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Calls the enabled suppliers listed in supplier_ids. Unknown, disabled, or flexible-incompatible IDs return HTTP 400. Traveler limits may be supplier-specific: incompatible suppliers are skipped silently, and HTTP 400 is returned only when none remain compatible.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Supplier Duffel Only",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Duffel search returned offers', () => {",
                  "  pm.response.to.have.status(200);",
                  "  const body = pm.response.json();",
                  "  const supplier = (body.suppliers || [])[0] || {};",
                  "  const offers = (supplier.results && supplier.results.offers) || [];",
                  "  pm.expect(body.success).to.eql(true);",
                  "  pm.expect(supplier.success).to.eql(true);",
                  "  pm.expect(offers.length).to.be.above(0);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"ORY\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"duffel_1\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Searches only Duffel by passing supplier_id=duffel_1. No other suppliers are called.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Supplier Amadeus Algeria Only",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"ORN\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"amadeus_aqc_dz_1\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Searches only Amadeus Algeria on the sandbox-friendly ALG-ORN route by passing supplier_id=amadeus_aqc_dz_1. No other suppliers are called.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Include Airlines Amadeus Algeria",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"amadeus_aqc_dz_1\",\n  \"included_airlines\": [\"AF\", \"AH\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Whitelist search: include means only these airline IATA codes are allowed. For Amadeus Algeria, FX-Port sends includedCarrierCodes only because Amadeus rejects combining includedCarrierCodes and excludedCarrierCodes.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Exclude Airline Amadeus Algeria",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"amadeus_aqc_dz_1\",\n  \"excluded_airlines\": [\"TK\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Blacklist search: excludes offers involving the requested airline IATA code. Amadeus Algeria also keeps its default BA exclusion when no include whitelist is present.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Include Airline Duffel",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"duffel_1\",\n  \"included_airlines\": [\"AF\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Whitelist search on ALG-CDG: only offers involving AF are kept. Duffel v2 offer-request docs do not expose a native airline include request field, so FX-Port applies this to returned offers while preserving the normal response shape.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Search - Exclude Airline Duffel",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"ORY\",\n  \"departure_date\": \"{{dep}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"duffel_1\",\n  \"excluded_airlines\": [\"BA\"]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/get_flights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "get_flights"
              ]
            },
            "description": "Blacklist search: excludes offers involving the requested airline IATA code. Duffel v2 offer-request docs do not expose a native airline exclude request field, so FX-Port applies this to returned offers while preserving the normal response shape.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-flights) · [Search flights](https://fx-port.com/docs/flights/search) · [Streaming search results](https://fx-port.com/docs/streaming) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Filters](https://fx-port.com/docs/flights/search/filters) · [Supplier selection](https://fx-port.com/docs/flights/search/suppliers) · [Open-jaw and multi-city](https://fx-port.com/docs/flights/search/complex-itineraries)"
          }
        },
        {
          "name": "Flexible Search - Amadeus Algeria",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"origin\": \"ALG\",\n  \"destination\": \"CDG\",\n  \"departure_date\": \"{{dep}}\",\n  \"return_date\": \"{{ret}}\",\n  \"cabin_class\": \"economy\",\n  \"passengers\": {\n    \"adults\": 1\n  },\n  \"supplier_id\": \"amadeus_aqc_dz_1\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/flexible_search",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "flexible_search"
              ]
            },
            "description": "Runs the flexible-search endpoint, which forces flexible=true and flexible_only=true. This request targets a currently capable supplier; integrations should treat flexible-date support as a supplier capability and always check flexPrices.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/flexible-search) · [Flexible-date search](https://fx-port.com/docs/flights/flexible-search) · [Search flights](https://fx-port.com/docs/flights/search)"
          }
        }
      ]
    },
    {
      "name": "02 - Pricing",
      "description": "Pricing example. Depends on search_id/offer_id from a successful search. Baggage inclusion (include_bags) is not yet public — it will be released later and announced by email.",
      "item": [
        {
          "name": "Price Flight",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Pricing succeeded', () => {",
                  "  pm.response.to.have.status(200);",
                  "  const body = pm.response.json();",
                  "  pm.expect(body.success).to.eql(true);",
                  "  pm.expect(((body.results || {}).offers || []).length).to.eql(1);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"search_id\": \"{{search_id}}\",\n  \"offer_id\": \"{{offer_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/price_flight",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "price_flight"
              ]
            },
            "description": "Refreshes pricing for the selected offer. search_id must be the supplier-level searchId returned in the search response.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/price-flight) · [Price a flight offer](https://fx-port.com/docs/flights/price) · [Flight pricing](https://fx-port.com/docs/flights/pricing) · [Search flights](https://fx-port.com/docs/flights/search)"
          }
        }
      ]
    },
    {
      "name": "03 - Booking",
      "description": "Create sandbox bookings with a read+write key. Price immediately before booking. Use booking_type=hold only when the selected repriced offer returns supportHold=true; otherwise issue immediately or choose another offer.",
      "item": [
        {
          "name": "Book Flight - Hold",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "  const body = pm.response.json();",
                  "  const booking = body.booking || (body.data && body.data.booking) || {};",
                  "  const orderId = booking.reference || booking.orderId || body.orderId || body.order_id;",
                  "  const pnr = booking.pnr || booking.bookingId || body.bookingId || body.booking_id;",
                  "  if (orderId) pm.collectionVariables.set('order_id', orderId);",
                  "  if (pnr) pm.collectionVariables.set('booking_id', pnr);",
                  "  pm.test('Hold booking succeeded', () => {",
                  "    pm.response.to.have.status(200);",
                  "    pm.expect(body.success).to.eql(true);",
                  "    pm.expect(orderId).to.be.ok;",
                  "  });",
                  "} catch (error) {}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"search_id\": \"{{search_id}}\",\n  \"offer_id\": \"{{offer_id}}\",\n  \"booking_type\": \"hold\",\n  \"send_email\": false,\n  \"passengers\": [\n    {\n      \"type\": \"adult\",\n      \"gender\": \"MALE\",\n      \"first_name\": \"{{male_first_1}}\",\n      \"last_name\": \"{{male_last_1}}\",\n      \"date_of_birth\": \"{{adult1_dob}}\",\n      \"identity_documents\": [\n        {\n          \"type\": \"passport\",\n          \"number\": \"{{adult1_passport}}\",\n          \"issuing_country\": \"DZ\",\n          \"expiry_date\": \"{{adult1_pexp}}\"\n        }\n      ]\n    }\n  ],\n  \"contact_details\": {\n    \"email\": \"sandbox.flight@example.com\",\n    \"phone_number\": \"+213554657687\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/book_flight",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "book_flight"
              ]
            },
            "description": "Creates an unpaid reservation only when the selected repriced offer returns supportHold=true. The response can use status=pending before storage normalizes it to awaiting_payment. No ticket is issued and no balance is charged. Test script saves order_id and booking_id when present.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/book-flight) · [Book a flight](https://fx-port.com/docs/flights/book) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Booking flows](https://fx-port.com/docs/flights/booking-flows) · [Upload ACD documents](https://fx-port.com/docs/flights/acd-documents)"
          }
        },
        {
          "name": "Book Flight - Issue",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "  const body = pm.response.json();",
                  "  const booking = body.booking || (body.data && body.data.booking) || {};",
                  "  const orderId = booking.reference || booking.orderId || body.orderId || body.order_id;",
                  "  const pnr = booking.pnr || booking.bookingId || body.bookingId || body.booking_id;",
                  "  if (orderId) pm.collectionVariables.set('order_id', orderId);",
                  "  if (pnr) pm.collectionVariables.set('booking_id', pnr);",
                  "  pm.test('Instant issue succeeded', () => {",
                  "    pm.response.to.have.status(200);",
                  "    pm.expect(body.success).to.eql(true);",
                  "    pm.expect(booking.status).to.eql('confirmed');",
                  "    pm.expect(orderId).to.be.ok;",
                  "  });",
                  "} catch (error) {}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"search_id\": \"{{search_id}}\",\n  \"offer_id\": \"{{offer_id}}\",\n  \"booking_type\": \"issue\",\n  \"send_email\": false,\n  \"passengers\": [\n    {\n      \"type\": \"adult\",\n      \"gender\": \"MALE\",\n      \"first_name\": \"{{male_first_1}}\",\n      \"last_name\": \"{{male_last_1}}\",\n      \"date_of_birth\": \"{{adult1_dob}}\",\n      \"identity_documents\": [\n        {\n          \"type\": \"passport\",\n          \"number\": \"{{adult1_passport}}\",\n          \"issuing_country\": \"DZ\",\n          \"expiry_date\": \"{{adult1_pexp}}\"\n        }\n      ]\n    }\n  ],\n  \"contact_details\": {\n    \"email\": \"sandbox.flight@example.com\",\n    \"phone_number\": \"+213554657687\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/book_flight",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "book_flight"
              ]
            },
            "description": "Creates an instant/issued booking in sandbox. send_email=false keeps examples quiet.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/book-flight) · [Book a flight](https://fx-port.com/docs/flights/book) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Booking flows](https://fx-port.com/docs/flights/booking-flows) · [Upload ACD documents](https://fx-port.com/docs/flights/acd-documents)"
          }
        },
        {
          "name": "Book Flight - Family Example",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "  const body = pm.response.json();",
                  "  const booking = body.booking || (body.data && body.data.booking) || {};",
                  "  const orderId = booking.reference || booking.orderId || body.orderId || body.order_id;",
                  "  const pnr = booking.pnr || booking.bookingId || body.bookingId || body.booking_id;",
                  "  if (orderId) pm.collectionVariables.set('order_id', orderId);",
                  "  if (pnr) pm.collectionVariables.set('booking_id', pnr);",
                  "} catch (error) {}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"search_id\": \"{{search_id}}\",\n  \"offer_id\": \"{{offer_id}}\",\n  \"booking_type\": \"issue\",\n  \"send_email\": false,\n  \"passengers\": [\n    {\n      \"type\": \"adult\",\n      \"gender\": \"MALE\",\n      \"first_name\": \"{{male_first_1}}\",\n      \"last_name\": \"{{male_last_1}}\",\n      \"date_of_birth\": \"{{adult1_dob}}\",\n      \"identity_documents\": [{ \"type\": \"passport\", \"number\": \"{{adult1_passport}}\", \"issuing_country\": \"DZ\", \"expiry_date\": \"{{adult1_pexp}}\" }]\n    },\n    {\n      \"type\": \"adult\",\n      \"gender\": \"FEMALE\",\n      \"first_name\": \"{{female_first_1}}\",\n      \"last_name\": \"{{female_last_1}}\",\n      \"date_of_birth\": \"{{adult2_dob}}\",\n      \"identity_documents\": [{ \"type\": \"passport\", \"number\": \"{{adult2_passport}}\", \"issuing_country\": \"DZ\", \"expiry_date\": \"{{adult2_pexp}}\" }]\n    },\n    {\n      \"type\": \"child\",\n      \"gender\": \"MALE\",\n      \"first_name\": \"{{child_first_1}}\",\n      \"last_name\": \"{{child_last_1}}\",\n      \"date_of_birth\": \"{{child1_dob}}\",\n      \"identity_documents\": [{ \"type\": \"passport\", \"number\": \"{{child1_passport}}\", \"issuing_country\": \"DZ\", \"expiry_date\": \"{{child1_pexp}}\" }]\n    },\n    {\n      \"type\": \"infant\",\n      \"gender\": \"FEMALE\",\n      \"first_name\": \"{{infant_first_1}}\",\n      \"last_name\": \"{{infant_last_1}}\",\n      \"date_of_birth\": \"{{infant1_dob}}\",\n      \"identity_documents\": [{ \"type\": \"passport\", \"number\": \"{{infant1_passport}}\", \"issuing_country\": \"DZ\", \"expiry_date\": \"{{infant1_pexp}}\" }]\n    }\n  ],\n  \"contact_details\": {\n    \"email\": \"sandbox.flight@example.com\",\n    \"phone_number\": \"+213554657687\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/book_flight",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "book_flight"
              ]
            },
            "description": "Family booking payload (2 adults, 1 child, 1 infant). Run 'Search - Family' first so the child age and passenger counts match. Names, dates of birth, passport numbers, and expiry dates are randomized by the collection pre-request script.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/book-flight) · [Book a flight](https://fx-port.com/docs/flights/book) · [Flight passengers & ages](https://fx-port.com/docs/flights/passengers) · [Booking flows](https://fx-port.com/docs/flights/booking-flows) · [Upload ACD documents](https://fx-port.com/docs/flights/acd-documents)"
          }
        }
      ]
    },
    {
      "name": "04 - Held Orders",
      "description": "Quote, pay, or cancel held reservations by order_id. A displayed deadline and canPay=true are not guarantees: the PNR may already be expired, cancelled, past its payment deadline, or released early by the airline.",
      "item": [
        {
          "name": "Get Order Quote",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const orderId = pm.collectionVariables.get('order_id') || pm.environment.get('order_id');",
                  "if (!orderId) {",
                  "  throw new Error('order_id is empty. Run Hold Booking or Book Flight first to populate it.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "  const body = pm.response.json();",
                  "  const quote = body.orderQuote || (body.data && body.data.orderQuote) || body;",
                  "  const orderId = quote.orderId || quote.order_id || body.order_id;",
                  "  if (orderId) pm.collectionVariables.set('order_id', orderId);",
                  "  const paymentStatus = quote.paymentStatus || {};",
                  "  pm.test('Held-order quote succeeded', () => {",
                  "    pm.response.to.have.status(200);",
                  "    pm.expect(body.success).to.eql(true);",
                  "    pm.expect(paymentStatus.awaitingPayment).to.eql(true);",
                  "  });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"order_id\": \"{{order_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/get_order_quote",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "get_order_quote"
              ]
            },
            "description": "Returns the current order price and payment state for a held booking. Accepts order_id (preferred), booking_id, or pnr. PNRs are not globally unique, so prefer order_id. b2bPrice/totalAmount is the agency debit (= finalPrice - agencyCommission). canPay=true is not a guarantee: payment can still fail if the PNR expired, was cancelled, passed its deadline, or was released early by the airline.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-order-quote) · [Get an order quote](https://fx-port.com/docs/flights/get-order-quote) · [Pay for a held order](https://fx-port.com/docs/flights/pay-order) · [Booking flows](https://fx-port.com/docs/flights/booking-flows)"
          }
        },
        {
          "name": "Pay Order",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const orderId = pm.collectionVariables.get('order_id') || pm.environment.get('order_id');",
                  "if (!orderId) {",
                  "  throw new Error('order_id is empty. Run Hold Booking or Get Order Quote first to populate it.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Held-order payment succeeded', () => {",
                  "  pm.response.to.have.status(200);",
                  "  const body = pm.response.json();",
                  "  pm.expect(body.success).to.eql(true);",
                  "  pm.expect(body.paymentSucceeded).to.eql(true);",
                  "  pm.expect((body.paymentDetails || {}).status).to.eql('succeeded');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"order_id\": \"{{order_id}}\",\n  \"payment_type\": \"balance\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/pay_order",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "pay_order"
              ]
            },
            "description": "Pays and issues a held order. Prefer order_id; amount and currency are resolved server-side. Payment cannot revive an expired or cancelled PNR, a hold past its deadline, or a reservation released early by the airline. Treat paymentSucceeded and paymentDetails.status as authoritative. Get Order Quote is optional and canPay is not a guarantee.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/pay-order) · [Pay for a held order](https://fx-port.com/docs/flights/pay-order) · [Get an order quote](https://fx-port.com/docs/flights/get-order-quote) · [Booking flows](https://fx-port.com/docs/flights/booking-flows)"
          }
        },
        {
          "name": "Cancel Held Reservation",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const orderId = pm.collectionVariables.get('order_id') || pm.environment.get('order_id');",
                  "if (!orderId) {",
                  "  throw new Error('order_id is empty. Run Hold Booking or Book Flight first to populate it.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"order_id\": \"{{order_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/cancel_order",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "cancel_order"
              ]
            },
            "description": "Cancels an unpaid held reservation before ticket issuance when that booking's supplier supports hold cancellation. Accepts order_id (preferred), booking_id, or pnr. This releases the PNR; it is not a refund.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/cancel-order) · [Cancel a held reservation](https://fx-port.com/docs/flights/cancel-order) · [Post-booking operations](https://fx-port.com/docs/flights/post-booking)"
          }
        }
      ]
    },
    {
      "name": "05 - Booking and Ticket Reads",
      "description": "Read-only flight booking and ticket lookup endpoints. Defaults use known sandbox booking V64R4V.",
      "item": [
        {
          "name": "Get Booking",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const bookingId = pm.collectionVariables.get('booking_id') || pm.environment.get('booking_id');",
                  "if (!bookingId) {",
                  "  throw new Error('booking_id is empty. Run Book Flight, Hold Booking, or a flight booking read that populates it first.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_id\": \"{{booking_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/get_booking",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "get_booking"
              ]
            },
            "description": "Fetch by PNR, supplier order id, or internal UUID. Environment and agency are resolved from the API key.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-booking) · [Get a booking](https://fx-port.com/docs/flights/get-booking) · [Post-booking operations](https://fx-port.com/docs/flights/post-booking)"
          }
        },
        {
          "name": "Get Ticket PDF",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const bookingId = pm.collectionVariables.get('booking_id') || pm.environment.get('booking_id');",
                  "if (!bookingId) {",
                  "  throw new Error('booking_id is empty. Run Book Flight or a flight booking read that populates it first.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_id\": \"{{booking_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/flights/get_ticket",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "get_ticket"
              ]
            },
            "description": "Returns the ticket PDF download URL when the PDF is ready. If ticket_ready=false, retry later.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-ticket) · [Get the ticket PDF](https://fx-port.com/docs/flights/get-ticket) · [Get a booking](https://fx-port.com/docs/flights/get-booking)"
          }
        },
        {
          "name": "Get Ticket Number",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Not a server error', () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/get_ticket_number",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "get_ticket_number"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_id\": \"{{booking_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "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.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-ticket-number) · [Get the e-ticket number](https://fx-port.com/docs/flights/get-ticket-number) · [Get a booking](https://fx-port.com/docs/flights/get-booking)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "06 - Airport Utilities",
      "description": "API-facing airport autocomplete and fallback ranking utilities. Internal airport metrics are excluded.",
      "item": [
        {
          "name": "Search Airports",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/airports?query=ALG&locale=en",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "airports"
              ],
              "query": [
                {
                  "key": "query",
                  "value": "ALG"
                },
                {
                  "key": "locale",
                  "value": "en"
                }
              ]
            },
            "description": "Airport autocomplete. Query can be an IATA code, city, or airport name.\n\n<!-- fx-generated -->\n**Permission:** Public (no API key needed)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/search-airports) · [Search airports](https://fx-port.com/docs/flights/airports)"
          }
        },
        {
          "name": "Most Searched Airports",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Not a server error', () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/top-airports?airport_type=to&locale=en&limit=1",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "top-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`.\n\n<!-- fx-generated -->\n**Permission:** Public (no API key needed)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/top-airports) · [Search airports](https://fx-port.com/docs/flights/airports#top-airports)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "07 - Refunds of Issued Orders",
      "item": [
        {
          "name": "Quote the Refund of an Issued Order",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Not a server error', () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/cancel_quote",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "cancel_quote"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"order_id\": \"{{order_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "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`.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/cancel-quote) · [Quote a cancellation](https://fx-port.com/docs/flights/cancel-quote) · [Confirm a cancellation](https://fx-port.com/docs/flights/confirm-cancel) · [Post-booking operations](https://fx-port.com/docs/flights/post-booking)"
          },
          "response": []
        },
        {
          "name": "Confirm the Cancellation (Irreversible)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Not a server error', () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/confirm_cancel",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "confirm_cancel"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"cancellation_id\": \"ore_0000BC0VG9WBaFAZ83u3yy\",\n  \"order_id\": \"{{order_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "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`.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/confirm-cancel) · [Confirm a cancellation](https://fx-port.com/docs/flights/confirm-cancel) · [Quote a cancellation](https://fx-port.com/docs/flights/cancel-quote) · [Post-booking operations](https://fx-port.com/docs/flights/post-booking)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "08 - ACD Passport Documents",
      "item": [
        {
          "name": "Get ACD Documents State",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Not a server error', () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/get_acd_documents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "get_acd_documents"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_id\": \"{{booking_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "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.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/get-acd-documents) · [Upload ACD documents](https://fx-port.com/docs/flights/acd-documents)"
          },
          "response": []
        },
        {
          "name": "Upload ACD Passport Documents",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Not a server error', () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/flights/upload_acd_documents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "flights",
                "upload_acd_documents"
              ]
            },
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "booking_id",
                  "type": "text",
                  "value": "{{booking_id}}"
                },
                {
                  "key": "traveler_ids",
                  "type": "text",
                  "value": "1"
                },
                {
                  "key": "documents",
                  "type": "file",
                  "src": []
                },
                {
                  "key": "replace",
                  "type": "text",
                  "value": "false",
                  "disabled": true
                }
              ]
            },
            "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.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/upload-acd-documents) · [Upload ACD documents](https://fx-port.com/docs/flights/acd-documents)"
          },
          "response": []
        }
      ]
    }
  ]
}
