FX-Port API
Hotels

Sandbox testing

Which hotelId values are safe to test with in sandbox, and why booking any other hotel fails.

Sandbox only has live inventory for a small set of designated test hotels. The supplier sandbox (WorldOTA/RateHawk test environment) does not carry real hotel inventory. Searching or booking any hotelId other than the test hotels below will return no rates, or fail at pre_book / book_hotel. This is expected — it is not an FX-Port bug.

Designated sandbox test hotel

Use this hotelId for sandbox integration testing:

hotelId = test_hotel

Pass it as the id field on search_advanced and get_rates:

{
  "id": "test_hotel",
  "checkin": "2026-11-15",
  "checkout": "2026-11-18",
  "residency": "gb",
  "language": "en",
  "guests": [{ "adults": 2, "children": [] }]
}

Supplier sandbox inventory is not under FX-Port's control and can be inconsistent. The supplier's own test/demo hotels are shared infrastructure across every partner testing against their sandbox, and their available rates, dates, and even booking behaviour can change without notice. If a request against test_hotel returns zero rates for a given date range, retry with a different checkin/checkout window (2–3 nights, starting 2–8 weeks out tends to work best) before assuming your integration is broken. This mirrors the same caveat documented for flight suppliers in Sandbox notice.

If test_hotel returns zero rates for every date range you try, use test_hotel_do_not_book as a fallback to validate your get_rates → pre_book → book_hotel request/response handling end-to-end. It is the supplier's other standard sandbox property and has reliably returned bookable rates in our own testing. Its name is the supplier's own label — bookings against it are still safe test bookings (no balance is ever debited in sandbox), but the supplier's own sandbox housekeeping can cancel/refund it shortly after booking, so don't build cancellation-timing assumptions around it.

What to expect

StepBehaviour in sandbox
search_advanced / get_ratesReturns whatever synthetic rates the supplier currently exposes for test_hotel. Empty results for some date ranges are a known supplier-side limitation — try another date window.
pre_bookLocks the rate exactly like live; no charge occurs.
book_hotelCompletes and returns a real bucket_link (voucher PDF) once the background finalization step runs — poll get-booking until status is confirmed.
BalanceNever debited. balanceDebited, transactionCreated, and payoutCreated are all false in every sandbox booking response.
cancel_hotelWorks like live, but the supplier's own sandbox housekeeping can independently cancel/refund test bookings out of band. If cancel_hotel returns invalid_status with a booking already refunded or cancelled, that's the supplier sandbox — not your integration — and can be treated as a successful end state.
WebhooksStill fire, to your configured sandbox webhook URL.

Testing checklist

Before switching to a live key, verify in sandbox against test_hotel:

  1. Locations — locations/search resolves a city to usable coordinates
  2. Name search — name-search returns fuzzy matches for a real hotel name
  3. Search — search_advanced accepts your geo or hotel-ID payload
  4. Rates — get_rates returns at least one rate with a bookHash, for one room and for a multi-room guest configuration with children ages
  5. Pre-book — pre_book with that bookHash returns a partnerOrderId
  6. Book — book_hotel with the partnerOrderId and guest names returns status: "pending" then transitions to confirmed
  7. Get booking — GET /api/v1/bookings/{id}?product=hotels shows status: "confirmed" and a populated bucket_link (voucher PDF)
  8. Voucher — get_voucher returns the same PDF URL
  9. Cancel — cancel_hotel succeeds, or reports the booking was already cancelled/refunded by the supplier sandbox (see caveat above)

Once every step above passes against test_hotel, switch to a fxp_live_ key — the request shapes are identical, only the key prefix changes.

On this page