Skip to content
FX-Port/Docs
GuidesAPI referenceChangelog
Get API keys
  • Introduction
  • Quickstart
  • Security
  • Authentication
  • Environments and Postman
  • Sandbox
  • Conventions and naming
  • Legacy names and twins
  • Errors
  • Rate limits and fair use
  • Streaming search results

Concepts

  • Overview
  • Sandbox testing
  • Mini and full data
  • Guests and rooms
  • Pricing

Search

  • Geo search
  • Locations and coordinates
  • Name search
  • Hotel details

Rates and booking

  • Rates
  • Pre-book
  • Book

After booking

  • Get a booking
  • Voucher
  • Cancel
  • Use these docs with AI

Hotels · After booking

Cancel a hotel booking

Cancel a confirmed hotel booking owned by your agency. Requires Read+Write permission.

POST/api/v1/hotels/cancel_hotel

Cancel a confirmed hotel booking of your agency. FX-Port cancels it at the supplier and gives back what the cancellation schedule of the rate allows: everything before the free cancellation deadline, less (or nothing) after it.

Permission: Read+Write (a read key answers 403 read_write_permission_required)

Only a confirmed booking can be cancelled

Right after book_hotel the booking is pending: wait until the booking is confirmed. A booking that is pending, failed, or already cancelled or refunded answers 400 invalid_status.

In the sandbox, cancel every booking you make

Every sandbox booking is a real order on the supplier's test contract. The test hotels cancel for free until 21:00 UTC on the check-in day (test_hotel until 00:00 UTC on that day). See Sandbox testing.

Request body

POST /api/v1/hotels/cancel_hotel

FieldTypeRequiredDescription
booking_idstringYesThe bookingId of book_hotel.
curl https://api.fx-port.com/api/v1/hotels/cancel_hotel \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "booking_id": "HMVXX4ZQ3"
}'

Legacy names

These older spellings still work. The answer carries a deprecated_field warning that names the spelling to use (see Legacy names).

Older nameUse instead
partner_order_id, partnerOrderId, bookingIdbooking_id

Response

A real answer of the sandbox, in 3.5 to 4 seconds (1 to 4 seconds is usual):

{
  "environment": "sandbox",
  "success": true,
  "status": "ok",
  "bookingId": "HMVXX4ZQ3",
  "bookingStatus": "refunded",
  "adminCancel": false,
  "refund": {
    "cancellationType": "free",
    "type": "Free cancellation",
    "agencyAmount": 1482,
    "travelerAmount": 1484.97,
    "currency": "DZD",
    "providerAmount": 3,
    "providerCurrency": "EUR"
  },
  "refundProcessed": true,
  "webhookSent": true,
  "processingTime": 3.488
}
FieldDescription
bookingStatusThe new status of the booking: refunded (free cancellation), partial_refund (a penalty applied) or cancelled (nothing refunded). Also as booking_status.
refund.cancellationTypefree (the supplier refunded everything), partial (a penalty applied) or none (no refund). Also as cancellation_type.
refund.typeThe same as a label: Free cancellation, Partial cancellation or No refund.
refund.agencyAmountWhat is credited back to your agency, in your currency. For a free cancellation it is everything you were debited (totalPrice - agencyCommission). Also as agency_refund_amount.
refund.travelerAmountWhat the traveler gets back, in your currency. For a free cancellation it is the whole totalPrice. Also as traveler_refund_amount.
refund.currencyYour agency currency. Also as agency_currency.
refund.providerAmount, providerCurrencyWhat the supplier refunded, in its own currency (EUR). For information.
refundProcessedtrue when the refund was handled. In the sandbox nothing moves and it is still true. Also as refund_processed.
adminCanceltrue when FX-Port staff cancelled the booking. Always false for your call.
webhookSentAn internal step of FX-Port, not a message to you: FX-Port sends no webhooks to customers.
processingTimeSeconds the call took.

The keys are camelCase. Twins in snake_case sit next to them: booking_id, booking_status, refund_processed and processing_time at the top, and cancellation_type, agency_refund_amount, traveler_refund_amount, supplier_refund_amount, agency_currency and supplier_currency inside refund. See Legacy names.

What you get back

When you cancelbookingStatuscancellationTypeYour refund
Before the free cancellation deadlinerefundedfreeEverything you were debited.
After it, with a penaltypartial_refundpartialWhat was debited minus the penalty of the rate.
After it, on a non-refundable ratecancellednoneNothing.

When the deadline is, and what each window costs, is in the cancellationPolicy of the rate (freeCancellationBefore and policies[]): see Get hotel rates. Cancelling after the free window still succeeds; it is the refund that is smaller. We could not exercise the penalty windows in the sandbox, because the test hotels refund in full until the check-in day: the lines above for partial and none come from the refund rules of the code, not from a sandbox run.

If the call times out

The supplier sometimes takes about 30 seconds to answer a cancellation. The call then fails with 503 supplier_timeout, and the cancellation may or may not have happened. So:

  1. Read the booking: GET /bookings/{booking_id}?product=hotels.
  2. refunded, partial_refund or cancelled: it went through.
  3. Still confirmed: call cancel_hotel again. In our tests the second call answered in under a second.
for attempt in range(3):
    try:
        response = requests.post(f"{API}/hotels/cancel_hotel", headers=HEADERS, json={"booking_id": booking_id}, timeout=60)
    except requests.Timeout:
        continue  # read the booking if you can; it stays confirmed until the supplier answers
    if response.ok:
        break
    time.sleep(3)  # a 5xx once in a while: the booking is still confirmed, ask again

Errors

HTTPCodeCause
400invalid_statusThe booking is not confirmed.
400missing_field, invalid_booking_idbooking_id is missing (errors[0].field is booking_id), or is not 1 to 256 characters of letters, digits and _ . : -.
400the supplier's codeThe supplier refused the cancellation (errors[0].type is supplier_error). The booking is unchanged.
403read_write_permission_required, forbiddenA read key, or a booking of another agency.
404booking_not_foundNo booking of your agency has this id in this environment.
502supplier_invalid_responseThe supplier answered in a form that could not be used. The cancellation may or may not have happened: read the booking, and call again only if it is still confirmed.
503supplier_timeoutThe supplier did not answer in time. Read the booking, then retry.

Related

Get a hotel bookingThe status after a cancellation, and what was refunded.Get hotel ratesThe cancellation schedule of a rate.Hotel pricingWhat you were debited, and what the traveler paid.API reference: Cancel a confirmed hotel bookingEvery field and error.
PreviousGet the hotel voucherNext eSIMs API overview

On this page

  • Request body
  • Legacy names
  • Response
  • What you get back
  • If the call times out
  • Errors
  • Related

© 2026 FX-Port

TermsPrivacy[email protected]