{
  "info": {
    "_postman_id": "fxport-api-hotels-sandbox-v2",
    "name": "FX-Port API - Hotels (Sandbox)",
    "description": "FX-Port Hotels 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\nAPI-facing FX-Port Hotels collection for external/sandbox consumers.\n\nAuth is defined once at the collection root as Bearer {{api_key}}, so every request inherits the same sandbox API key. These requests are for API use only, not internal dashboard/admin operations.\n\nIncluded API-facing endpoints:\n- Hotel city/location lookup (coordinates for the geo search)\n- Geo search: availability and prices of every hotel around a point, or of one hotel by id\n- Hotel name lookup and hotel content (mini cards and full documents)\n- Rates, pre-book and booking (a booking confirms at once: there is no hold or pay-later option)\n- Booking lookup, voucher and cancellation\n\nIn the sandbox only the supplier's test hotels can be booked: see the variable sandbox_hotel_id.\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": "hotel_city",
      "value": "Paris"
    },
    {
      "key": "hotel_country_code",
      "value": "FR"
    },
    {
      "key": "hotel_latitude",
      "value": "48.8566"
    },
    {
      "key": "hotel_longitude",
      "value": "2.3522"
    },
    {
      "key": "hotel_name",
      "value": "Hilton"
    },
    {
      "key": "hotel_id",
      "value": "test_hotel_do_not_book"
    },
    {
      "key": "sandbox_hotel_id",
      "value": "test_hotel_do_not_book"
    },
    {
      "key": "sandbox_hotel_id_alt",
      "value": "test_hotel"
    },
    {
      "key": "sandbox_latitude",
      "value": "35.69111"
    },
    {
      "key": "sandbox_longitude",
      "value": "-0.64167"
    },
    {
      "key": "sandbox_city",
      "value": "Oran"
    },
    {
      "key": "sandbox_country_code",
      "value": "DZ"
    },
    {
      "key": "sandbox_residency",
      "value": "gb"
    },
    {
      "key": "hotel_checkin",
      "value": ""
    },
    {
      "key": "hotel_checkout",
      "value": ""
    },
    {
      "key": "hotel_rate_hash",
      "value": ""
    },
    {
      "key": "hotel_partner_order_id",
      "value": ""
    },
    {
      "key": "hotel_booking_id",
      "value": ""
    },
    {
      "key": "guest_first_name",
      "value": "Adam"
    },
    {
      "key": "guest_first_name_2",
      "value": "Sarah"
    },
    {
      "key": "guest_last_name",
      "value": "Martin"
    },
    {
      "key": "guest_email",
      "value": "adam.martin@example.com"
    },
    {
      "key": "guest_phone",
      "value": "+213555010101"
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const now = new Date();",
          "const fmt = d => d.toISOString().slice(0, 10);",
          "const pick = arr => arr[Math.floor(Math.random() * arr.length)];",
          "const rnd = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;",
          "pm.collectionVariables.set('hotel_checkin', fmt(new Date(now.getTime() + 30 * 86400000)));",
          "pm.collectionVariables.set('hotel_checkout', fmt(new Date(now.getTime() + 33 * 86400000)));",
          "",
          "// Randomize guest identities on search so the booking guests cascade from the same run.",
          "const isSearch = /search/i.test((pm.info && pm.info.requestName) || '');",
          "if (isSearch || !pm.collectionVariables.get('guest_first_name')) {",
          "  // Diverse guest names from many origins. ASCII/Latin ONLY — no accents/diacritics",
          "  // (write 'Jose' not 'José') and no non-Latin scripts.",
          "  const firstNames = ['Adam', 'Mehdi', 'Karim', 'Omar', 'Diego', 'Mateo', 'Lucas', 'Ivan', 'Kenji', 'Wei', 'Arjun', 'Kwame', 'Lars', 'Marco', 'Rafael', 'Noah', 'Hassan', 'Santiago', 'Henrik', 'Ahmad'];",
          "  const firstNames2 = ['Sarah', 'Lina', 'Yasmine', 'Sofia', 'Maria', 'Valentina', 'Aisha', 'Anya', 'Yuki', 'Mei', 'Priya', 'Amara', 'Ingrid', 'Giulia', 'Camila', 'Olivia', 'Noor', 'Elena', 'Astrid', 'Salma'];",
          "  const surnames = ['Benali', 'Smith', 'Garcia', 'Rossi', 'Kim', 'Nguyen', 'Kumar', 'Okafor', 'Andersson', 'Muller', 'Silva', 'Ivanov', 'Suzuki', 'Wang', 'Cohen', 'Martinez', 'Haddad', 'Patel', 'Santos', 'Khan'];",
          "  const first = pick(firstNames);",
          "  const family = pick(surnames);",
          "  pm.collectionVariables.set('guest_first_name', first);",
          "  pm.collectionVariables.set('guest_first_name_2', pick(firstNames2));",
          "  pm.collectionVariables.set('guest_last_name', family);",
          "  pm.collectionVariables.set('guest_email', `${first.toLowerCase()}.${family.toLowerCase()}@example.com`);",
          "  pm.collectionVariables.set('guest_phone', `+2135${rnd(50, 59)}${rnd(100000, 999999)}`);",
          "}"
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "pm.test('HTTP status is successful or accepted', function () {",
          "  pm.expect(pm.response.code).to.be.within(200, 299);",
          "});",
          "",
          "try {",
          "  const body = pm.response.json();",
          "  const hotels = body.hotels || body.hotels_details || (body.worldota_search_result && body.worldota_search_result.data && body.worldota_search_result.data.hotels) || (Array.isArray(body) ? body : []);",
          "  if (Array.isArray(hotels) && hotels.length && hotels[0].id) pm.collectionVariables.set('hotel_id', hotels[0].id);",
          "  const locations = body.locations || [];",
          "  if (locations.length) {",
          "    if (locations[0].latitude) pm.collectionVariables.set('hotel_latitude', String(locations[0].latitude));",
          "    if (locations[0].longitude) pm.collectionVariables.set('hotel_longitude', String(locations[0].longitude));",
          "    if (locations[0].city) pm.collectionVariables.set('hotel_city', locations[0].city);",
          "    if (locations[0].countryCode) pm.collectionVariables.set('hotel_country_code', locations[0].countryCode);",
          "  }",
          "  const rates = (body.data && body.data.rates) || (body.data && body.data.hotels && body.data.hotels[0] && body.data.hotels[0].rates) || (body.hotels && body.hotels[0] && body.hotels[0].rates) || [];",
          "  if (rates.length && (rates[0].bookHash || rates[0].book_hash || rates[0].hash)) pm.collectionVariables.set('hotel_rate_hash', rates[0].bookHash || rates[0].book_hash || rates[0].hash);",
          "  const partnerOrderId = body.partnerOrderId || body.partner_order_id || (body.data && (body.data.partnerOrderId || body.data.partner_order_id || body.data.order_id));",
          "  if (partnerOrderId) pm.collectionVariables.set('hotel_partner_order_id', partnerOrderId);",
          "  const bookingId = body.bookingId || body.booking_id || body.partnerOrderId || body.partner_order_id || (body.data && (body.data.bookingId || body.data.booking_id || body.data.partnerOrderId || body.data.partner_order_id));",
          "  if (bookingId) pm.collectionVariables.set('hotel_booking_id', bookingId);",
          "} catch (error) {}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "01 - Lookup",
      "item": [
        {
          "name": "Search City / Geo Location",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/locations/search?query={{hotel_city}}&locale=en&limit=5",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "locations",
                "search"
              ],
              "query": [
                {
                  "key": "query",
                  "value": "{{hotel_city}}"
                },
                {
                  "key": "locale",
                  "value": "en"
                },
                {
                  "key": "limit",
                  "value": "5"
                }
              ]
            },
            "description": "Authenticated hotel city/location lookup. Use latitude/longitude from the response for availability search.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/search-hotel-locations) · [Search locations](https://fx-port.com/docs/hotels/locations)"
          }
        },
        {
          "name": "Search Hotel Name - Query Params",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/name-search?query={{hotel_name}}&limit=5&min_score=50&include_location=true",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "name-search"
              ],
              "query": [
                {
                  "key": "query",
                  "value": "{{hotel_name}}"
                },
                {
                  "key": "limit",
                  "value": "5"
                },
                {
                  "key": "min_score",
                  "value": "50"
                },
                {
                  "key": "include_location",
                  "value": "true"
                }
              ]
            },
            "description": "Hotel name autocomplete/search using query parameters.\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-hotel-name-search) · [Hotel name search](https://fx-port.com/docs/hotels/name-search)"
          }
        },
        {
          "name": "Search Hotel Name - JSON Body",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"query\": \"{{hotel_name}}\",\n  \"limit\": 5,\n  \"min_score\": 50,\n  \"include_location\": true,\n  \"include_details\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/search-by-name",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "search-by-name"
              ]
            },
            "description": "Existing JSON body hotel name search endpoint.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/post-hotel-name-search) · [Hotel name search](https://fx-port.com/docs/hotels/name-search)"
          }
        },
        {
          "name": "Static Hotel Lookup - Minimal Fields",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "[\"{{hotel_id}}\"]",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/search?detail_level=minimal",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "search"
              ],
              "query": [
                {
                  "key": "detail_level",
                  "value": "minimal"
                }
              ]
            },
            "description": "Cached hotel document lookup by hotel IDs. Read-only; does not perform availability or rates search.\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-hotel-content) · [Mini and full hotel data](https://fx-port.com/docs/hotels/hotel-data)"
          }
        },
        {
          "name": "Hotel Details",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "[\"{{hotel_id}}\"]",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/details",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "details"
              ]
            },
            "description": "Full cached hotel detail lookup by hotel IDs.\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-hotel-details) · [Mini and full hotel data](https://fx-port.com/docs/hotels/hotel-data)"
          }
        },
        {
          "name": "Hotel Info - Mini or Full",
          "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/hotels/get_hotel_info",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "get_hotel_info"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"hotel_id\": \"{{hotel_id}}\",\n  \"language\": \"en\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Returns the content of a hotel for the page the user opens before checkout: description, photos, every room type, policies, facts and contact details, in camelCase (`HotelInfo`). It is the only content call that asks the supplier when FX-Port does not hold the hotel yet, and only for a single id (`hotel_id`): use it for the ids of `missing_hotel_ids`. With `hotel_ids` it returns only the hotels already cached and leaves out the others; an unknown single id answers `404 hotel_not_found`. With `detail_level=minimal` the answer is the compact card instead, which is what a result list needs. A hotel is looked up by its FX-Port id, then by its numeric supplier id (`hid`). Cached content is English whatever `language` you send; `language` only applies to the live fetch. The full document is 5 to 143 KB per hotel (26 KB in the middle, 38 KB on average): cache it by hotel id.\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-hotel-info) · [Hotel details](https://fx-port.com/docs/hotels/hotel-details) · [Mini and full hotel data](https://fx-port.com/docs/hotels/hotel-data)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "02 - Availability And Rates",
      "item": [
        {
          "name": "Geo Search - Hotels Around a Point",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"checkin\": \"{{hotel_checkin}}\",\n  \"checkout\": \"{{hotel_checkout}}\",\n  \"residency\": \"{{sandbox_residency}}\",\n  \"language\": \"en\",\n  \"guests\": [\n    {\"adults\": 2, \"children\": []},\n    {\"adults\": 1, \"children\": [8]},\n    {\"adults\": 3, \"children\": [3, 13, 4]}\n  ],\n  \"longitude\": {{sandbox_longitude}},\n  \"latitude\": {{sandbox_latitude}},\n  \"radius\": 15000\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/search_advanced",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "search_advanced"
              ]
            },
            "description": "Hotel availability search by latitude/longitude using sandbox coordinates.\n\nThis example demonstrates:\n- Geographic search with lat/lng (Oran, Algeria sandbox location)\n- Multi-room setup (3 rooms):\n  * Room 1: 2 adults\n  * Room 2: 1 adult + 1 child age 8\n  * Room 3: 3 adults + 3 children ages 3, 13, 4\n\nThe answer is always one JSON body: stream=true is not used by API keys. A search around a point takes 4 to 20 seconds and can return thousands of hotels, so keep the radius to what the screen needs. In the sandbox, search and prices run on real hotels, but only the supplier test hotels can be booked (see the variable sandbox_hotel_id).\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/search-hotels) · [Geo search](https://fx-port.com/docs/hotels/search) · [Mini and full hotel data](https://fx-port.com/docs/hotels/hotel-data) · [Guests and rooms](https://fx-port.com/docs/hotels/guests-and-rooms)"
          }
        },
        {
          "name": "Search Availability By Hotel ID",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"checkin\": \"{{hotel_checkin}}\",\n  \"checkout\": \"{{hotel_checkout}}\",\n  \"residency\": \"{{sandbox_residency}}\",\n  \"language\": \"en\",\n  \"guests\": [\n    {\"adults\": 2, \"children\": []}\n  ],\n  \"id\": \"{{sandbox_hotel_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/search_advanced",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "search_advanced"
              ]
            },
            "description": "Availability search for a specific hotel ID using the supplier test hotel.\n\nSandbox hotels (the only ones that can be booked in the sandbox):\n- {{sandbox_hotel_id}}: use this one, it works with every residency\n- {{sandbox_hotel_id_alt}}: works only with residency dz, ru or us\n\nAny other hotel can be searched and priced, but pre_book answers 409 hotel_not_bookable_in_sandbox.\n\nGuests format: 1 room with 2 adults. Supports multi-room setup with children ages:\n- Single room: [{\"adults\": 2, \"children\": []}]\n- Multi-room: [{\"adults\": 2, \"children\": []}, {\"adults\": 1, \"children\": [8]}]\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/search-hotels) · [Geo search](https://fx-port.com/docs/hotels/search) · [Mini and full hotel data](https://fx-port.com/docs/hotels/hotel-data) · [Guests and rooms](https://fx-port.com/docs/hotels/guests-and-rooms)"
          }
        },
        {
          "name": "Search Specific Hotel - Multi-Room Example",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"checkin\": \"{{hotel_checkin}}\",\n  \"checkout\": \"{{hotel_checkout}}\",\n  \"residency\": \"{{sandbox_residency}}\",\n  \"language\": \"en\",\n  \"guests\": [\n    {\"adults\": 2, \"children\": []},\n    {\"adults\": 1, \"children\": [8]},\n    {\"adults\": 3, \"children\": [3, 13, 4]}\n  ],\n  \"id\": \"{{sandbox_hotel_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/search_advanced",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "search_advanced"
              ]
            },
            "description": "Availability search for sandbox hotel with multi-room setup.\n\nRoom Configuration:\n- Room 1: 2 adults (0 children)\n- Room 2: 1 adult + 1 child age 8\n- Room 3: 3 adults + 3 children (ages 3, 13, 4)\n\nTotal: 6 adults + 4 children\n\nGuests array format: Each object represents one room with adults count and children ages array.\n\nThis demonstrates the complete room format documented in the API reference.\n\n<!-- fx-generated -->\n**Permission:** Read (a read-only key is enough)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/search-hotels) · [Geo search](https://fx-port.com/docs/hotels/search) · [Mini and full hotel data](https://fx-port.com/docs/hotels/hotel-data) · [Guests and rooms](https://fx-port.com/docs/hotels/guests-and-rooms)"
          }
        },
        {
          "name": "Get Rates For Hotel",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"checkin\": \"{{hotel_checkin}}\",\n  \"checkout\": \"{{hotel_checkout}}\",\n  \"residency\": \"{{sandbox_residency}}\",\n  \"language\": \"en\",\n  \"guests\": [\n    {\"adults\": 2, \"children\": []}\n  ],\n  \"id\": \"{{sandbox_hotel_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/get_rates",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "get_rates"
              ]
            },
            "description": "Get detailed rates/pricing for the selected sandbox hotel.\n\nFirst run 'Search Availability By Hotel ID' to get search results, then use this endpoint to fetch detailed rates for the selected hotel.\n\nGuests format supports:\n- 1 room: [{\"adults\": 2, \"children\": []}]\n- 2 rooms: [{\"adults\": 2, \"children\": []}, {\"adults\": 1, \"children\": [8]}]\n- 3 rooms: [{\"adults\": 2, \"children\": []}, {\"adults\": 1, \"children\": [8]}, {\"adults\": 3, \"children\": [3, 13, 4]}]\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-hotel-rates) · [Get hotel rates](https://fx-port.com/docs/hotels/rates) · [Hotel pricing](https://fx-port.com/docs/hotels/pricing)"
          }
        },
        {
          "name": "Pre Book Selected Rate",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"hash\": \"{{hotel_rate_hash}}\",\n  \"language\": \"en\",\n  \"price_increase_percent\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/pre_book",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "pre_book"
              ]
            },
            "description": "Pre-book a selected hotel rate and cache the trusted offer for final booking. Requires read_write.\n\n<!-- fx-generated -->\n**Permission:** Read+Write (the key needs write permission)\n\n**Docs:** [API reference](https://fx-port.com/docs/api/pre-book-hotel) · [Pre-book a rate](https://fx-port.com/docs/hotels/pre-book) · [Sandbox testing](https://fx-port.com/docs/hotels/sandbox-testing)"
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const hash = pm.collectionVariables.get('hotel_rate_hash') || pm.environment.get('hotel_rate_hash');",
                  "if (!hash) {",
                  "  throw new Error('hotel_rate_hash is empty. Run Get Rates For Hotel first to populate it.');",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "03 - Booking And Reads",
      "item": [
        {
          "name": "Book Hotel",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"partner_order_id\": \"{{hotel_partner_order_id}}\",\n  \"language\": \"en\",\n  \"rooms\": [\n    {\n      \"guests\": [\n        {\n          \"first_name\": \"{{guest_first_name}}\",\n          \"last_name\": \"{{guest_last_name}}\"\n        },\n        {\n          \"first_name\": \"{{guest_first_name_2}}\",\n          \"last_name\": \"{{guest_last_name}}\"\n        }\n      ]\n    }\n  ],\n  \"user\": {\n    \"email\": \"{{guest_email}}\",\n    \"phone\": \"{{guest_phone}}\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/book_hotel",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "book_hotel"
              ]
            },
            "description": "Finalize a pre-booked hotel offer. Requires read_write. The booking may return status pending first while confirmation finishes in the background; use Lookup Hotel Booking to poll until it becomes confirmed or failed.\n\nGuest names must use plain Latin (ASCII) letters only — no accents/diacritics (write 'Jose', not 'Jose' with an accent) and no non-Latin scripts (transliterate Arabic/Cyrillic/Chinese/etc.). Allowed: A-Z, a-z, spaces, hyphens.\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-hotel) · [Book a hotel](https://fx-port.com/docs/hotels/book) · [Guests and rooms](https://fx-port.com/docs/hotels/guests-and-rooms) · [Get a hotel booking](https://fx-port.com/docs/hotels/get-booking)"
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const partnerOrderId = pm.collectionVariables.get('hotel_partner_order_id') || pm.environment.get('hotel_partner_order_id');",
                  "if (!partnerOrderId) {",
                  "  throw new Error('hotel_partner_order_id is empty. Run Pre Book Selected Rate first to populate it.');",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Cancel Hotel",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const bookingId = pm.collectionVariables.get('hotel_booking_id') || pm.environment.get('hotel_booking_id');",
                  "if (!bookingId) {",
                  "  throw new Error('hotel_booking_id is empty. Run Book Hotel first, or run Lookup Hotel Booking after a pending booking response to populate it.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_id\": \"{{hotel_booking_id}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/cancel_hotel",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "cancel_hotel"
              ]
            },
            "description": "Cancel a confirmed hotel booking owned by the authenticated agency. Requires read_write.\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-hotel) · [Cancel a hotel booking](https://fx-port.com/docs/hotels/cancel)"
          }
        },
        {
          "name": "Get Hotel Voucher",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const bookingId = pm.collectionVariables.get('hotel_booking_id') || pm.environment.get('hotel_booking_id');",
                  "if (!bookingId) {",
                  "  throw new Error('hotel_booking_id is empty. Run Book Hotel first, or run Lookup Hotel Booking after a pending booking response to populate it.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/hotels/get_voucher?booking_id={{hotel_booking_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotels",
                "get_voucher"
              ],
              "query": [
                {
                  "key": "booking_id",
                  "value": "{{hotel_booking_id}}"
                }
              ]
            },
            "description": "Retrieve the hotel voucher link after voucher generation. Read-only. Run this after Lookup Hotel Booking shows a confirmed booking with voucher-ready post-processing.\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-hotel-voucher) · [Get the hotel voucher](https://fx-port.com/docs/hotels/voucher)"
          }
        },
        {
          "name": "Lookup Hotel Booking",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const bookingId = pm.collectionVariables.get('hotel_booking_id') || pm.environment.get('hotel_booking_id');",
                  "if (!bookingId) {",
                  "  throw new Error('hotel_booking_id is empty. Run Book Hotel first to populate it.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/bookings/{{hotel_booking_id}}?product=hotels",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "bookings",
                "{{hotel_booking_id}}"
              ],
              "query": [
                {
                  "key": "product",
                  "value": "hotels"
                }
              ]
            },
            "description": "Read-only booking lookup through the public account booking endpoint, scoped to hotels. Poll this after Book Hotel when the booking response status is pending; this matches the platform confirmation-page flow.\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-account-booking) · [Booking detail](https://fx-port.com/docs/account/booking-detail)"
          }
        }
      ]
    }
  ]
}
