Turns a live hold into a sale. bookings:write, not a scope of its
own: confirming is the second half of the booking this key already
created, not a new capability.
Idempotent by state, not by Idempotency-Key. Confirming an
already-confirmed booking answers 200 with the same body rather
than an error — a retry after a lost response is the normal case, not
a mistake to refuse.
A hold whose clock already ran out is 409 BOOKING_HOLD_EXPIRED,
not 422. The request was well-formed and was true when it was made
— the clock ran out underneath it, and the sweep may already have
handed the room to somebody else. Same reasoning POST /bookings
uses for UNIT_NOT_AVAILABLE.
Only a pending booking can be confirmed here — a positive
allow-list, the mirror of POST /bookings/{booking}/cancel's. A
status added later is refused by default rather than silently
permitted, and it stops an integration reversing a terminal state:
un-cancelling a booking, or "confirming" a stay that already checked
out, are merchant decisions with folio consequences.
And only a hold this API placed. A booking held by anything else
— a merchant's quote, a website checkout in progress — answers 422 BOOKING_NOT_CONFIRMABLE as well: confirming it would sell the room
while its producer still believed the hold intact, with no
accommodation charge ever posted against it. A booking you created
WITHOUT hold_minutes is not a hold at all and confirms normally;
this endpoint is its only route to confirmed.
Confirming clears is_hold, hold_origin and hold_expires_at —
the one place on this surface that does. POST /bookings/{booking}/cancel deliberately leaves them set, because a
released hold is not a guest cancelling anything.
A booking outside this property answers 404, identically to one
that does not exist.
Scope: bookings:write · Property-bound
See Holds.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||