Confirm a booking. Executes the three-step confirmation lifecycle:
initialize— creates the provider PNR.initialize-payment— sets the chosen payment method.process— issues the booking and captures payment.
Supported verticals: flights, stays, cars, trains.
Pass paymentMethod in the request body to select a payment method. When omitted, postpay is used. Use GET /v1/payment-methods?vertical={vertical}&fareId={fareId} to retrieve the list of available methods for the fare.
The Idempotency-Key header is required.
Guest-checkout selection hold: when this booking is correlated to a guest checkout session, confirm rejects with 409 SELECTION_HOLD_EXPIRED and captures no charge if the session's fare hold had already lapsed before this call started — renew it with POST /v1/checkout/sessions/current/selection-hold and retry. A hold that lapses while this call is already running does not abort it: the three-step lifecycle above runs to its existing completion or failure outcome unaffected, since aborting partway would leave a provider PNR with no documented way to unwind it upstream.
Guest-checkout price acceptance: when the correlated checkout session still carries a renewed, higher total the traveler has not accepted (pendingRepriceAcceptance on GET /v1/checkout/sessions/current), confirm rejects with 409 PRICE_ACCEPTANCE_REQUIRED and captures no charge, whichever device calls it. Record the acceptance with POST /v1/checkout/sessions/current/selection-hold/accept-price and retry. An expired hold is reported first, since renewing it can change the price again.
Guest-checkout renewed fare (BOOKING_FARE_SUPERSEDED): confirm sends the provider no amount and no fare id, and a selection-hold renewal cannot reprice a booking that already exists: the provider charges what it holds on the booking. Mercury therefore does not rebind or reprice the existing booking. When a renewal moves the session's held fare, or its price, the booking already created for the session is superseded, and confirm rejects it with 409 BOOKING_FARE_SUPERSEDED. No provider call (initialize, initialize-payment, process) is made and nothing is charged. The superseded booking is abandoned, not voided: it stays pending with the provider and Mercury never confirms it. Recover by creating a new booking with POST /v1/flights/bookings using the renewal's renewedFareId as the fare id, then confirming that booking; it is charged the renewed newTotal. Creating it makes it the session's bookingId. Error order: SELECTION_HOLD_EXPIRED, then BOOKING_FARE_SUPERSEDED, then PRICE_ACCEPTANCE_REQUIRED (accepting a price cannot make a superseded booking chargeable, so it is reported before the acceptance check).
Stays booked from an offer (fall-through): when the rate an offer lock locked has expired, or its supplier no longer has it, before any payment step, travel.tech may move the booking onto another rate of the same offer that keeps what was sold, at the total shown, and confirm it; the answer is then the usual success, for the same bookingId. No new price is ever asked for at confirm. When no rate can be booked, confirm answers 410 OFFER_EXPIRED with recoveryAction REFRESH; details[0] carries fallthroughFailure (no_candidate or all_need_reprice) and research (checkIn, checkOut, rooms), the search to run again.
Retry contract (flights, verified; other verticals unverified — see MER-2705). For the flights vertical, the provider does not deduplicate on Idempotency-Key at all: retrying a failed confirm with the same key re-runs initialize/initialize-payment/process as brand-new provider calls, it does not replay a stored response — there is nothing stored to replay. A same-card retry (same key) and a different-card retry (a freshly minted key) are therefore identical from the provider's point of view. Safety of retrying instead rests on the failure's classification: after any card-decline reason on the commit error envelope (reason / recoveryAction — see the 422/409 error responses) — the traveler's card was never charged, so a follow-up confirm (same key or a new one) is safe. After a provider 5xx or a timeout with no decline code, whether a charge occurred is unverified; treat it as unknown rather than assuming either outcome. See internal ticket MER-2705 for the full evidence trail.
No per-session concurrency guard: Mercury applies no per-checkout-session single-flight lock on confirm. Two concurrent confirms for the same booking (for example two browser tabs) each reach the provider with their own Idempotency-Key; only the provider's own idempotency/state-machine behaviour — not yet verified from Mercury — can prevent a double process.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
401Missing or invalid authentication, or the token carries no account context to settle against
403User lacks booking capability for this vertical
404Booking not found
409Booking already in a terminal state, or (code: SELECTION_HOLD_EXPIRED) the correlated guest checkout session's fare hold lapsed before this call started, or (code: BOOKING_FARE_SUPERSEDED) a renewal moved the session's held fare or price after this booking was created, or (code: PRICE_ACCEPTANCE_REQUIRED) a renewed, higher total on that session is not yet accepted — no charge was captured and no provider call was made
410OFFER_EXPIRED: the selected rate expired. CAR_OFFER_REJECTED_BY_SUPPLIER: the rental supplier refused the car offer itself, which no retry changes. recoveryAction CHANGE_SELECTION asks for a replacement rate; REFRESH (a stay booked from an offer that no other rate could fill) asks for a new search, given in details[0].research
422Missing Idempotency-Key header, account requires mandatory custom attributes that were not supplied, the fare is out of corporate travel policy (POLICY_NOT_RESPECTED), or a guest-provisioned account requested a payment method other than card
502The provider rejected the confirm for a reason no retry changes and the traveler cannot correct, such as CAR_DRIVER_ADDRESS_INCOMPLETE. Read code: the status alone does not say whether retrying helps
503Service not configured or temporarily unavailable