Lock a rate before booking
Locks the rate you chose: the supplier re-checks the price and opens an order, and FX-Port keeps the priced offer for one hour under the partnerOrderId of the answer.
/api/v1/hotels/pre_bookBook it with book_hotel, passing that id as booking_id. If the price rose since get_rates by more than price_increase_percent the call fails with rate_not_found: call get_rates again. If it rose by less, priceChanged is true and data holds the new price. Calling pre_book again with the same rate and the same price_increase_percent within a minute returns the same answer (the same partnerOrderId, no second order at the supplier); another percentage is a new request. Take 4 to 5 seconds. Always check formData.orderId: null means the supplier opened no order. In the sandbox a hotel other than a supplier test hotel answers 409 hotel_not_bookable_in_sandbox with the list of test hotels.
curl https://api.fx-port.com/api/v1/hotels/pre_book \
-H "Authorization: Bearer fxp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"book_hash": "h-14574656-7ce9-5a21-b673-8654c5ebd416",
"price_increase_percent": 0
}'{
"environment": "sandbox",
"success": true,
"status": "ok",
"partnerOrderId": "HMVXX4ZQ3",
"priceChanged": false,
"data": {
"hotelId": "test_hotel_do_not_book",
"hid": 8473727,
"rates": [
{
"bookHash": "p-58bfbfa0-fd1c-4be9-8a2a-54942cae6627",
"matchHash": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c",
"room": {
"name": "Standard room",
"beddingType": "single bed",
"miscRoomType": null,
"capacity": null,
"bathroom": "private-bathroom",
"hasPrivateBathroom": true,
"balcony": false,
"roomClass": "room",
"quality": "standard",
"view": null,
"floor": null,
"bedrooms": null,
"isFamily": false,
"isClub": false,
"genderRestriction": null,
"nonSmoking": true
},
"meal": {
"type": "nomeal",
"included": false,
"childMealIncluded": false
},
"dailyPrices": [
1,
1,
1
],
"allotment": 77,
"isPackage": false,
"anyResidency": true,
"deposit": null,
"noShow": null,
"cancellationPolicy": {
"freeCancellationBefore": "2026-12-01T21:00:00",
"policies": [
{
"from": null,
"until": "2026-12-01T21:00:00",
"originalAmount": 0,
"originalCurrency": "EUR",
"currency": "DZD"
},
{
"from": "2026-12-01T21:00:00",
"until": null,
"originalAmount": 3,
"originalCurrency": "EUR",
"currency": "DZD"
}
]
},
"pricing": {
"supplierCurrency": "EUR",
"agencyCurrency": "DZD",
"originalPrice": 3,
"agencyCommission": 2.97,
"totalPrice": 1484.97,
"taxes": {
"cityTax": {
"includedInPrice": false,
"amount": 23.1,
"currency": "HNL"
},
"electricityFee": {
"includedInPrice": true,
"amount": 0.6,
"currency": "EUR"
},
"resortFee": {
"includedInPrice": true,
"amount": 0.68,
"currency": "EUR"
}
}
},
"exchangeRate": 285
}
]
},
"formData": {
"partnerOrderId": "HMVXX4ZQ3",
"orderId": 757581298,
"itemId": 232430616,
"paymentTypes": [
{
"amount": "3.00",
"currencyCode": "EUR",
"isNeedCreditCard": false,
"isNeedCvc": false,
"type": "deposit"
},
{
"amount": "3.00",
"currencyCode": "USD",
"isNeedCreditCard": true,
"isNeedCvc": true,
"type": "now"
}
]
}
}Request
Body
Required, JSON.
book_hashstringrequiredThe bookHash of the rate you chose, from get_rates (it starts with h-). Valid for 30 minutes. The matchHash of a rate is not a booking token and is not read as one: a call that sends only a match_hash fails with missing_field.
price_increase_percentnumberThe price rise, in percent, you accept if the supplier raised the price since get_rates. 0 means none: the call fails if the price moved; null means not set (0). A repeated call within a minute with the same book_hash and the same percentage gets the same answer; another percentage is a new request. A number written as text ("5") is read as the number, with a coerced_value warning.
Between 0 and 100. Default 0.
languagestringLanguage of the hotel name in the answer.
Default en.
hashstringdeprecatedLegacy name, still accepted. Use book_hash instead.
The rate token, in its older spelling. Use book_hash.
bookHashstringdeprecatedLegacy name, still accepted. Use book_hash instead.
The rate token, in its older spelling. Use book_hash.
Response
200The rate is locked. Keep partnerOrderId.
environmentstringThe environment of the key that made the call: sandbox for fxp_test_ keys, live for fxp_live_ keys. Present in every JSON body.
One of sandbox, live.
request_idstringThe id of the request, also in the X-Request-Id header.
warningsarray of objectPresent only when the request used a previous spelling of a field or something the endpoint ignored.
ShowHide 5 attributes
codestringWhat the warning is about.
One of deprecated_field, ignored_field, coerced_value, deprecated_value, deprecated_endpoint.
fieldstringThe request field the warning is about.
replacementstringThe name to use instead, for deprecated_* warnings.
messagestringA sentence that says what happened and what to do.
documentation_urlstringWhere the naming rules are explained.
successbooleantrue when the rate was locked.
statusstringAlways ok.
Always ok.
partnerOrderIdstringdeprecatedLegacy name, still accepted. Use booking_id instead.
The id of the booking you are about to make. Send it as booking_id to book_hotel, then to GET /, get_voucher and cancel_hotel.
booking_idstringThe id of the booking you are about to make. Send it as booking_id to book_hotel, then to GET /, get_voucher and cancel_hotel. The snake_case twin of partnerOrderId, with the same value: use this name.
partner_order_idstringThe id of the booking you are about to make. Send it as booking_id to book_hotel, then to GET /, get_voucher and cancel_hotel. The snake_case twin of partnerOrderId, with the same value: use this name.
priceChangedbooleandeprecatedLegacy name, still accepted. Use price_changed instead.
true when the supplier's price moved since get_rates (within price_increase_percent). data holds the new price.
price_changedbooleantrue when the supplier's price moved since get_rates (within price_increase_percent). data holds the new price. The snake_case twin of priceChanged, with the same value: use this name.
dataobjectThe locked rate.
ShowHide 4 attributes
hotelIdstringdeprecatedLegacy name, still accepted. Use hotel_id instead.
The id of the hotel.
hotel_idstringThe id of the hotel. The snake_case twin of hotelId, with the same value: use this name.
hidintegerThe supplier's numeric hotel id.
ratesarray of objectThe one rate you locked, with a new bookHash that starts with p-. Do not reuse it.
ShowHide 17 attributes
bookHashstringdeprecatedLegacy name, still accepted. Use book_hash instead.
The token of this rate. Send it as book_hash to pre_book. It stays valid for 30 minutes.
book_hashstringThe token of this rate. Send it as book_hash to pre_book. It stays valid for 30 minutes. The snake_case twin of bookHash, with the same value: use this name.
matchHashstringdeprecatedLegacy name, still accepted. Use match_hash instead.
A second identifier of the rate. You do not need it to book: send bookHash.
match_hashstringA second identifier of the rate. You do not need it to book: send bookHash. The snake_case twin of matchHash, with the same value: use this name.
roomobjectThe room of a rate. Match it with the hotel content by room name; the document of get_hotel_info lists every room type with photos and amenities.
ShowHide 16 attributes
namestringName of the room as the supplier sells it, in the language of the request.
beddingTypestringnullableThe bedding as a phrase: single bed, full double bed, twin beds ...
miscRoomTypestringnullableAnother description of the bed or room, when the supplier gives one (king size bed).
capacityintegernullableNumber of guests the room is made for; null when unknown.
bathroomstringnullableprivate-bathroom or shared-bathroom.
hasPrivateBathroombooleantrue when the bathroom is private.
balconybooleantrue when the room has a balcony.
roomClassstringnullableClass of the room: room, suite, junior-suite, apartment, studio, cottage, dorm ...
qualitystringnullableQuality of the room: economy, standard, classic, superior, deluxe, executive, business ...
viewstringnullableView of the room (city-view, garden-view ...); null when there is none.
floorstringnullableFloor of the room, when it is special (attic).
bedroomsintegernullableNumber of bedrooms; null when unknown.
isFamilybooleantrue for a family room.
isClubbooleantrue for a club room.
genderRestrictionstringnullableGender the room is reserved for (hostel dorms); null when there is no restriction.
nonSmokingbooleantrue when the room is non-smoking.
mealobjectThe meal plan of a rate.
ShowHide 3 attributes
typestringThe meal included in the rate. nomeal is room only.
One of nomeal, breakfast, lunch, dinner, half-board, half-board-lunch, half-board-dinner, full-board, all-inclusive, ultra-all-inclusive.
includedbooleantrue when a meal is included (type is not nomeal).
childMealIncludedbooleantrue when the meal of the children is included too.
dailyPricesarray of numberdeprecatedLegacy name, still accepted. Use daily_prices instead.
The supplier's price of each night of the stay (first room), in supplierCurrency.
daily_pricesarray of numberThe supplier's price of each night of the stay (first room), in supplierCurrency. The snake_case twin of dailyPrices, with the same value: use this name.
allotmentintegerHow many rooms of this type are left at this rate.
isPackagebooleantrue when the rate is part of a package.
anyResidencybooleanfalse when the rate is reserved for some residencies: it exists for the residency of your request only.
depositobjectnullableReserved for deposit terms of the rate. null today.
noShowobjectnullableReserved for no-show terms of the rate. null today.
cancellationPolicyobjectWhen and at what price a booking of the rate can be cancelled. The amounts are the supplier's (originalAmount, in originalCurrency); what is refunded to your agency is in the answer of cancel_hotel.
ShowHide 3 attributes
freeCancellationBeforestringnullabledeprecatedLegacy name, still accepted. Use free_cancellation_before instead.
UTC date and time (no zone suffix) until which the booking can be cancelled for free. null means the rate is non-refundable.
free_cancellation_beforestringnullableUTC date and time (no zone suffix) until which the booking can be cancelled for free. null means the rate is non-refundable. The snake_case twin of freeCancellationBefore, with the same value: use this name.
policiesarray of objectThe schedule of penalties from now to check-in: the free window first, then the windows with a penalty.
ShowHide 5 attributes
fromstringnullableStart of the window, a UTC date and time without zone suffix; null for the first window (from now).
untilstringnullableEnd of the window, a UTC date and time without zone suffix; null for the last window (to check-in and after).
originalAmountnumberThe penalty charged by the supplier in this window, in originalCurrency. 0 in the free window.
originalCurrencystringCurrency of originalAmount, the supplier's: EUR.
currencystringYour agency currency, as a label. originalAmount is NOT in this currency.
pricingobjectThe prices of a rate or of an offer. There is no b2bPrice in this block: the amount debited from your agency is totalPrice - agencyCommission (see Hotel pricing).
ShowHide 10 attributes
supplierCurrencystringdeprecatedLegacy name, still accepted. Use supplier_currency instead.
Currency the supplier prices in: EUR.
supplier_currencystringCurrency the supplier prices in: EUR. The snake_case twin of supplierCurrency, with the same value: use this name.
agencyCurrencystringdeprecatedLegacy name, still accepted. Use agency_currency instead.
The currency of your agency. Every amount below except originalPrice and taxes is in it.
agency_currencystringThe currency of your agency. Every amount below except originalPrice and taxes is in it. The snake_case twin of agencyCurrency, with the same value: use this name.
originalPricenumberThe supplier's price for the whole stay and all rooms, in supplierCurrency. For information: it is not the base of what you pay.
agencyCommissionnumberdeprecatedLegacy name, still accepted. Use agency_commission instead.
Your commission, in agencyCurrency. The agency price is totalPrice minus this amount.
agency_commissionnumberYour commission, in agencyCurrency. The agency price is totalPrice minus this amount. The snake_case twin of agencyCommission, with the same value: use this name.
totalPricenumberdeprecatedLegacy name, still accepted. Use total_price instead.
What the traveler pays: the whole stay, all rooms, in agencyCurrency. You are debited totalPrice - agencyCommission.
total_pricenumberWhat the traveler pays: the whole stay, all rooms, in agencyCurrency. You are debited totalPrice - agencyCommission. The snake_case twin of totalPrice, with the same value: use this name.
taxesobjectTaxes and fees by name (cityTax, cleaningFee, electricityFee, occupancyTax, resortFee, serviceFee, vat ...), only the ones the rate has.
exchangeRatenumberdeprecatedLegacy name, still accepted. Use exchange_rate instead.
The rate of exchange from supplierCurrency to agencyCurrency that was used (285.0 in the sandbox).
exchange_ratenumberThe rate of exchange from supplierCurrency to agencyCurrency that was used (285.0 in the sandbox). The snake_case twin of exchangeRate, with the same value: use this name.
formDataobjectThe supplier's booking form for the order that book_hotel finishes.
ShowHide 5 attributes
partnerOrderIdstringnullabledeprecatedLegacy name, still accepted. Use partner_order_id instead.
The order id again. null when the supplier opened no order.
partner_order_idstringnullableThe order id again. null when the supplier opened no order. The snake_case twin of partnerOrderId, with the same value: use this name.
orderIdintegernullableThe supplier's order number. null means the supplier opened no order and the hotel cannot be booked.
itemIdintegernullableThe supplier's item number inside the order.
paymentTypesarray of objectThe ways to pay the supplier offers: one deposit and one now option per card currency (about 40 entries).
ShowHide 5 attributes
amountstringThe amount as a decimal string, in currencyCode.
currencyCodestringISO 4217 currency of amount.
isNeedCreditCardbooleantrue when the option needs a card. FX-Port never does: it pays with the deposit option.
isNeedCvcbooleantrue when the option needs a card security code.
typestringdeposit pays from the supplier deposit of FX-Port (the one FX-Port uses); now is a card payment.
One of deposit, now.
Errors
| Status | Code | Example message |
|---|---|---|
| 400 | missing_field | Missing required field: hash |
| 400 | invalid_rate_hash | 'book_hash' must be a rate hash taken from get_rates (got 'abc'). |
| 400 | rate_not_found | Rate not found |
| 403 | read_write_permission_required | This endpoint requires an API key with read_write permission. |
| 409 | hotel_not_bookable_in_sandbox | Only the supplier test hotels can be booked in sandbox: test_hotel_do_not_book (any residency) or test_hotel (residency dz, ru or us). |
| 502 | supplier_invalid_response | The hotel supplier returned an answer that could not be used. Retry in a few minutes. |
| 503 | supplier_busy | The supplier is temporarily at capacity; retry after the time given in the Retry-After header. |
The shape of the error body depends on the endpoint. See Errors for every shape and code.