FX-Port API
Hotels

POST /api/v1/hotels/book_hotel

Finalize a pre-booked hotel offer with guest details. Requires Read+Write permission.

Finalize the booking for a rate you already locked with pre_book. The backend recovers price, hotel, and dates from the cached pre-book offer — you only need to send partner_order_id, the guests per room, and contact details.

Permission: Read+Write

Always call pre_book first. Booking without a matching cached pre-book offer returns 409 cached_offer_required (live) — sandbox is more lenient for local testing, but the request shape is identical either way.

Request body

FieldTypeRequiredDescription
partner_order_idstringYesFrom pre_book
roomsarrayYesOne object per room, matching the room order used in get_rates — see Guests & rooms
rooms[].guestsarrayYes{ first_name, last_name } per guest staying in that room
userobjectYes{ email, phone } — booking contact, receives the confirmation
languagestringNoDefault "en"
send_emailbooleanNofalse to suppress the confirmation email. Default true.

Request example

curl --request POST \
  --url https://api.fx-port.com/api/v1/hotels/book_hotel \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "partner_order_id": "HMTLZLWGQ",
    "language": "en",
    "rooms": [
      {
        "guests": [
          { "first_name": "Amine", "last_name": "Benali" },
          { "first_name": "Sara", "last_name": "Benali" }
        ]
      }
    ],
    "user": {
      "email": "[email protected]",
      "phone": "+213555000001"
    },
    "send_email": false
  }'

Multi-room example

{
  "partner_order_id": "HMTLZLWGQ",
  "language": "en",
  "rooms": [
    { "guests": [{ "first_name": "Amine", "last_name": "Benali" }, { "first_name": "Sara", "last_name": "Benali" }] },
    { "guests": [{ "first_name": "Karim", "last_name": "Haddad" }] }
  ],
  "user": { "email": "[email protected]", "phone": "+213555000001" }
}

Name encoding: first_name / last_name must be plain Latin (ASCII) letters only — no accents/diacritics, no non-Latin scripts. See Guests & rooms.

Response example

{
  "environment": "sandbox",
  "success": true,
  "status": "pending",
  "bookingId": "HMTLZLWGQ",
  "partnerOrderId": "HMTLZLWGQ",
  "hotelId": "test_hotel",
  "checkinDate": "2026-11-15",
  "checkoutDate": "2026-11-18",
  "pricing": {
    "totalPrice": 43812.75,
    "agencyCommission": 87.30,
    "currency": "DZD",
    "supplierCurrency": "EUR",
    "originalPrice": 291.00
  },
  "postProcessing": {
    "bookingDbId": "24fec1e5-a09f-48ec-8050-1adaab0a8de1",
    "webhookSent": false,
    "bucketLink": null
  },
  "processingTime": 2.986,
  "balanceDebited": false,
  "transactionCreated": false,
  "payoutCreated": false
}

The response returns status: "pending" immediately. Confirmation finishes in the background (supplier confirmation + voucher PDF generation) and typically completes within a few seconds. Poll GET /api/v1/bookings/{id}?product=hotels with bookingId until status becomes confirmed (or failed).

Field reference

FieldDescription
bookingId / partnerOrderIdSame value — use this to poll get-booking, fetch the voucher, or later cancel
statuspending right after this call; becomes confirmed or failed in the background
pricing.totalPriceFinal amount charged, matching what pre_book returned
postProcessing.bucketLinknull until the voucher PDF finishes generating — poll get-booking
balanceDebited / transactionCreated / payoutCreatedAlways false in sandbox — no financial side effects occur. In live, these reflect the real ledger state.

Next step

Poll GET /api/v1/bookings/{id}?product=hotels until confirmed, then fetch the voucher.

On this page