Booking Flow
Book
Redeem a rate key, name the guests, state the price you expect — and hold the reservation.
/api/v1/bookings x-api-keyBooking spends a rateKey from search. The rate key already carries the property, the
dates, the occupancy, the board and the price, so the request body is mostly about people and money.
Request Body
Required
leadGuestobject required- The person the booking is in the name of. Requires
name.first,name.last,contact.emailandcontact.phone. Optionallytitle,birthDateand the address fieldscontact.address,city,country,zip. roomsobject[] required- One entry per room, each with its own
rateKeyand theguestsstaying in it. A guest is{ title?, name: { first, last }, age? }.specialRequestsis an optional list of strings passed to the hotel. Every rate key in one request must be for the same property. expectedPrice{ amount, currency } required- What you expect the total to be — the
price.sellfigure from search. A live total above this is rejected with409 price_changedrather than booked.
Optional
reference.clientstring (≤100) optional- Your own reference for this booking. Returned on every read as
clientReference, which makes reconciliation against your orders straightforward. paymentobject optional{ mode: "gws_collected", transactionRef? }— who collected the guest’s money. Recorded, not charged: the API never takes card details.isTestboolean default false- Asks the supplier for a test booking. Forced on in sandbox, so it only has an effect with a live key.
Response
Success returns a booking view — the same shape GET /bookings/:id serves — and it says what was bought: the hotel, each room with the guests in it, and the rate it was sold at. Keep it; showing the guest their reservation never needs another search.
bookingIduuid optional- Store this. It is how you read or cancel the booking.
statusBookingStatus optionalCONFIRMED,ON_REQUEST,UNKNOWNorFAILEDat this point in the lifecycle.mode"sandbox" | "live" optional- Which environment the booking lives in.
clientReferencestring | null optional- The
reference.clientyou sent, ornull. price{ amount, currency } optional- The total actually booked.
cancelPolicyobject[] optional- The cancellation ladder as it stood when the booking was made. Keep it — it is what a cancellation preview is computed from.
hotelConfirmationNumberstring | null optional- The hotel’s own reference, when the supplier gave one.
checkIn / checkOutstring (YYYY-MM-DD) optional- The stay dates.
nightsinteger optional- The length of the stay.
propertyobject | null optional- The hotel booked, in the same shape as on GET /properties.
nullonly if the hotel has since been removed. roomsobject[] optional- One entry per room booked. See below.
createdAtstring (ISO 8601) optional- When the booking was created.
Each room
roomCodestring | null optional- The room’s code, the same one search gave it.
roomNamestring | null optional- The room’s name.
guestsobject[] optional- Who is in this room, as you sent them:
{ title?, name: { first, last }, age? }. price{ amount, currency } optional- What this room cost. The rooms add up to the booking’s
price. rateobject | null optional- The rate it was sold at, as search showed it:
rateCode,rateName,board,nightly,taxesAndFees,refundable,cancelPolicy,paymentTypeand anyremarks. TherateKeyis not repeated — it was spent by booking.nullon a booking made before rates were kept with it.
Status Codes
- 201
- Booked. A new reservation exists.
- 200
- A replay. This
Idempotency-Keywas already used, and the booking it created is returned unchanged. Nothing new happened. - 202
status: UNKNOWN - The supplier gave no definite answer. The reservation may or may not exist — see below.
- 400
validation_error - A malformed body, or a missing / too-short
Idempotency-Keyheader. - 401
unauthorized - Missing or invalid
x-api-key. - 409
conflict · price_changed - The total is higher than
expectedPrice(detailscarriesexpected,actualandcurrency), or the rate itself was re-priced since it was quoted (detailsthen carries just thecurrency). Nothing was booked: search again for the new price. - 409
rate_unavailable - The rate is gone — sold out, stop-sold or withdrawn since it was quoted. Search again.
- 410
rate_key_expired - The rate key is unknown, has expired, or has already been used. Search again for a fresh one.
- 424
distributor_unavailable - The rate can no longer be booked: its source was switched off between search and book. Search again.
- 502
supplier_error - The booking was refused outright. Nothing was reserved.
details.bookingIdis the record kept of the attempt; quote therequestIdif you contact support.
The 202 UNKNOWN Case
Sometimes a supplier accepts a booking request and then fails to say whether it worked — a
timeout at their end, a dropped connection, an ambiguous status. Gateway Stream does not guess.
The booking is recorded with status: "UNKNOWN" and returned with 202.
ON_REQUEST Bookings
ON_REQUEST means the supplier has taken the request but not confirmed it. The room
is not guaranteed. Show it to the guest as pending, poll GET /bookings/:id rather than rebooking, and be
ready for it to end as either CONFIRMED or FAILED.
Retrying Safely
- Network error, no response at all — resend the identical body with the
identical
Idempotency-Key. Safe, and the only correct move. 409or410— nothing was booked. Search again and present the new price to the guest before trying once more.202or502— stop. Do not book again with a new key; reconcile first.