A booking groups one or more reservations (one reservation = one unit) at the same property, with one customer and, if needed, one payment card.
Booking BK-EUWIGPSG
├── Reservation RS-AAAAAAAA (unit 1, offer X, guests …)
└── Reservation RS-BBBBBBBB (unit 2, offer Y, guests …)
Creating a booking
POST /v2/bookings
{
"customer": {
"first_name": "John", "last_name": "Doe",
"email": "[email protected]", "phone_number": "+31612345678",
"address_line_1": "Damrak 1", "postcode": "1012LG", "city": "Amsterdam", "country": "NL",
"company_name": "Acme B.V."
},
"reservations": [
{ "offer_id": "9131b7c8-…", "guests": [ { "first_name": "John", "last_name": "Doe" }, { "first_name": "Jane", "last_name": "Doe" } ] },
{ "offer_id": "5be0e7f1-…", "guests": [ { "first_name": "Max", "last_name": "Doe" } ] }
],
"comments": ["Late arrival, around 22:00"],
"payment": { "type": "visa", "card_number": "4111111111111111", "cvv": "123",
"card_holder": "John Doe", "expiry_month": "11", "expiry_year": "2028" }
}Rules:
- One reservation per unit. For a multi-unit search, send the reservations in the same order as the units in your
occupancy. Respectoccupancy_has_offer(Availability). - One property per booking. All offers must belong to the same property. The maximum number of reservations per booking is nine, but it's PMS/CRS dependent.
- Multi-unit bookings are all-or-nothing. Either every reservation is confirmed or none is. If one unit fails at the hotel, we cancel the units that were already created.
- Guests per reservation: at least 1, and at most the adults + children of the unit you searched for. Sending more gives
the number of guests is more than allowed. - Offers are single-use. Once you've made a booking request with an offer, don't use it again, even if that booking failed or your request timed out (Offers).
Customer and guest data
The customer is the booker. The hotel receives the customer's email and phone as the contact for the booking, and the customer's address as the billing address for the card. Guests are the people staying.
| Field | Required | Guidance |
|---|---|---|
customer.first_name, last_name, email, phone_number, address_line_1, postcode, city, country | Yes | — |
guests[].first_name, last_name | Yes | — |
country | — | Send either an ISO 3166-1 alpha-2 code (NL, GB, US), or the country's name (France) |
phone_number | — | International format, max 14 characters (+31612345678). Some hotel systems reject longer numbers. |
| Names | — | Up to 256 characters. Where a hotel system accepts fewer. Prefer plain Latin letters: some hotel systems reject characters like Ü. |
title | No | e.g. Mr, Ms |
birth_date | No | YYYY-MM-DD |
company_name | No | The customer's company. Use it for invoicing details, since there is no VAT-number field. Add the VAT number in comments if the hotel needs it. |
comments | No | On the booking (sent with every reservation) or per reservation. Max 500 characters per comment. They are passed to the hotel, but special requests are not guaranteed. |
loyalty_id | No | The loyalty number with the hotel chain, where supported. One per booking: most chains accept only one, so it is not per guest. |
iata_code | No | Your IATA number, max 12 characters, one per booking. Send it on every booking that should carry it; hotels use it to pay travel-agent commission. |
tax_remittance | No | Only if you are registered for tax remittance in the US: SINGLE_REMITTANCE or DUAL_REMITTANCE. |
Confirmation is asynchronous
A 201 from POST /bookings means we accepted the booking and are sending it to the hotel. It does not mean the hotel confirmed it. Every reservation starts as TO_BE_DELIVERED.
To find out the result, poll GET /v2/bookings/{booking_id} until every reservation has a final status.
Recommended polling:
- Poll every 1–2 seconds.
- Most bookings settle within 10 seconds.
- Keep polling for up to 120 seconds. Some hotel systems need two calls of up to 30 seconds each, and multi-unit bookings take longer. Bookings taking 40–60 seconds are normal for those systems.
- If a reservation is still
TO_BE_DELIVEREDafter that, the reservation should be marked as failed. - A failed
GETis not a failed booking. IfGET /bookings/{id}itself returns an error (e.g.500or a timeout), retry it. Only the reservationstatustells you the outcome. Treating a lookup error as a failure leads to the customer booking twice. - If
POST /bookingstimes out on your side and you have no booking ID, don't retry with the same offer: the first request may have gone through, and the offer is now used up. Contact support with the time and guest name before rebooking.
Reservation statuses
| Status | Final? | Meaning |
|---|---|---|
TO_BE_DELIVERED | no | We received the reservation and are sending it to the hotel. |
CONFIRMED | yes | The hotel confirmed it. supplier_confirmation_id holds the hotel's confirmation number. |
FAILED | yes | The hotel did not create the reservation. See errors. |
TO_BE_CANCELLED | no | We are sending a cancellation to the hotel (Cancellation). |
CANCELLED | yes | The hotel confirmed the cancellation. |
CANCELLATION_FAILED | yes | The hotel did not accept the cancellation. The reservation is still active. |
Multi-unit bookings: each reservation has its own status, but they succeed or fail together. If one fails, the others end up FAILED or CANCELLED too.
Reservation errors
When a reservation fails, errors[] usually explains why. It can occasionally be empty.
"errors": [ { "code": "PRICE_CHANGED", "message": "…" } ]| Code | Meaning | What to do |
|---|---|---|
NO_AVAILABILITY | The unit sold out between search and booking. | Search again. |
PRICE_CHANGED | The hotel returned a different price at booking time; nothing was booked. | Search again and show the new price. |
PAYMENT_ERROR | The hotel couldn't charge or validate the card (declined, insufficient funds, unsupported type, or the card expires before check-in). | Use another card. Card issues are between you, the card issuer and the hotel; Katanox only passes the card on. |
SUPPLIER_ERROR | The hotel rejected the booking for another reason. The message may contain the hotel's own text (e.g. invalid name or phone). | Check the message; and create a new reservation. |
EXTERNAL_TIMEOUT | The hotel system didn't answer in time. We don't know whether the hotel created the booking. | See below. |
INTERNAL_SERVER_ERROR | Something failed on Katanox's side; no hotel error implied. | Retry with a fresh offer. |
UPDATE_REJECTED | A modification was rejected by the hotel. | — |
CANCELLATION_REJECTED | A cancellation was rejected by the hotel. | See Cancellation. |
When the hotel times out (EXTERNAL_TIMEOUT)
EXTERNAL_TIMEOUT)The reservation is marked FAILED, but the hotel may still have processed it ("ghost booking"). The hotel then holds a reservation we don't have a confirmation number for.
- Don't immediately rebook the same guest at the same property for the same dates. You may end up with two bookings.
Retrieving a booking
GET /v2/bookings/{booking_id} returns the booking with every reservation, its status, price, policies (a snapshot from booking time) and errors.
To fetch one reservation, use GET /v2/bookings/{booking_id}/reservations/{reservation_id}.
| ID | Where you see it |
|---|---|
booking.id | BK-…, returned by POST /bookings |
reservation.id | RS-…, one per unit |
reservation.booking_id | The BK-… the reservation belongs to |
reservation.supplier_confirmation_id | The hotel's confirmation number, set once CONFIRMED |
Invoices and folios
GET /v2/bookings/{booking_id}/invoicesreturns the folio data the hotel has issued for the booking (charges, payments).GET /v2/bookings/{booking_id}/invoices/{invoice_id}/filereturns the invoice document.
Both are only available for hotel systems that provide them, and only after the hotel has issued them (usually after check-out).