Booking lifecycle
Two objects
Section titled “Two objects”A booking intent is a hold: what the customer wants, bound to a departure, before any money. A booking is the confirmed thing — departure-bound and obligation-bound — and reuses the intent’s id once the purchase settles. GET /booking-intents/{id} reads the hold; GET /bookings/{booking_id} reads the booking.
Every booking has an agreement: a versioned chain of the facts agreed with the customer (start time, meeting point, party, add-ons, price). Changes append to the chain; nothing is overwritten.
States
Section titled “States”Bookings move through a small, fixed set of states. Everything else about a booking is a flag or a fact on its agreement, not a state.
booked ──▶ checked_in ──▶ done │ ├──▶ cancelled └──▶ no_show| Transition | Endpoint | Notes |
|---|---|---|
| Check in | POST /bookings/{id}/check-in |
Idempotent; a repeat is 200 {already: true}. Can also be done per participant (POST /participants/{id}/check-in, with /undo) for tenants that check in riders one at a time. |
| No-show | POST /bookings/{id}/no-show |
Applies the policy’s no-show outcome (often retain the payment). |
| Reopen | POST /bookings/{id}/reopen |
done or no_show back to booked, within the tenant’s reopen window (default 24 hours) of entering that state. A cancelled booking does not reopen. |
| Cancel | POST /cancellations or a cancel amendment |
See below. Terminal. |
Checking in a cancelled or no-show booking is 409 bad_transition; reopening outside the window is 409 reopen_window_closed.
Tenants that assign equipment to riders record it with POST /bookings/{booking_id}/assignments (a unit is held by one open booking per departure window; 409 unit_busy names the holder) and release it with the matching DELETE.
Finding bookings
Section titled “Finding bookings”GET /bookings takes exactly one selector: a departure window (window_start + window_end), the guest’s confirmation number (ref, the QS-XXXXXXXX form of the obligation ref), the buyer’s email, or free text q over buyer and rider names. Cancelled bookings ride along with status: cancelled for the caller to filter. For a day’s operations use GET /days/{date}, and for a departure’s roster GET /contexts/{context_id}/manifest.
Amendments
Section titled “Amendments”POST /bookings/{booking_id}/amendments appends one typed amendment to the agreement. Send expected_version equal to the agreement’s current version; a stale version is refused 409 with stored_version and changes nothing.
kind |
Carries | Money |
|---|---|---|
start_time |
a new start | none |
meeting_point |
a name and address, or null to clear | none |
span |
a new rental span | priced server-side |
party_delta |
names joining or leaving | server-priced; growth reserves capacity first (409 capacity_exhausted if it cannot) |
add_on_delta |
an add-on id and a quantity delta | server-priced; removal reverses at the line’s own paid rate |
add_on_rider |
which rider an add-on line is bound to | none |
party_rename |
a rider’s new name | none |
price_override |
a net delta and a reason | requires commerce.price_override; a staff action with an audit trail |
cancel |
an effective date | the refund fraction comes from the booking’s policy snapshot |
The customer update notification is enqueued in the same transaction as the amendment, so the email always describes what was actually recorded. An amendment can only ever lower what is owed down to what has been collected; refunding money that was already collected is the refund lane’s job.
Cancellations and refunds
Section titled “Cancellations and refunds”A cancellation is addressed by obligation_ref and carries a reason code because the reason governs the money:
operator_trip_cancelled— always a full refund.customer_initiated— refunded according to the booking’s policy snapshot: the tenant’s T-minus schedule (hours of notice → refund basis points) as it stood when the booking was sold, or a per-offering / per-channel policy document that overrides it.
The refund is not settled inline: the booking flips to cancelled and its capacity is released in one transaction, and a cancellation.requested event is enqueued for the ledger to settle against the original capture. Read the obligation’s balance or the ledger to see it land. x-idempotency-key is required.
Cancelling a whole departure (POST /departure-cancellations) cancels and fully refunds every booking on it.
Rebooking
Section titled “Rebooking”POST /rebookings moves a booking to a new departure (variant + start time) and posts the price revision against its obligation: upcharge (a priced charge is enqueued), downgrade (a partial refund), or even_exchange (no money event). Capacity is swapped in the same transaction — the old seat released, the new one held — and a full target chain refuses 409 capacity_full with nothing moved. The operator can quote first with GET /bookings/{booking_id}/quotes.
Tenants can require rebooks to be approved rather than automatic, and can attach a change fee; both come from the policy.
Policies
Section titled “Policies”A policy governs cancellation, reschedule, no-show and weather outcomes, fees, and the text the guest sees. Policies are scoped — tenant default, then per offering, per variant, or per sales channel — and anything a scope leaves unset inherits. PUT /policies/{scope_kind}/{scope_ref} publishes a new version; bookings made from then on snapshot it, existing bookings keep theirs. GET /bookings/{booking_id}/policy shows the terms a given booking is actually under, and POST /bookings/{booking_id}/policy/re-resolve re-snapshots it deliberately. The simple tenant-wide T-minus schedule is set with POST /merchants/cancellation-schedule.
The operator-cancel rule is fixed at full refund and cannot be configured otherwise.
Guest links
Section titled “Guest links”Customers act for themselves through capability links: single-purpose URLs whose token is the authorisation.
| Link | Minted by | Lets the guest |
|---|---|---|
/b/{token} |
POST /obligations/{ref}/balance-links |
Pay exactly the obligation’s live balance, read at render and again at pay |
/g/{token} |
group pay | Pay their share of a group booking |
/p/{token} |
party link | Add or name riders on the booking |
/w/{token} |
POST /readiness-links |
Complete readiness requirements — sign the waiver, attest eligibility |
Minting a new balance link for the same obligation cancels the previous one, so a guest never holds two live links. Tokens are stored as digests and shown once; an unknown, consumed or expired token is a 404.
Signed-in customers get the same abilities without a link through /account/*: their bookings, a cancellation quote, a self-serve cancellation or rebook within policy, and their readiness status.
Confirmation emails
Section titled “Confirmation emails”Every booking, amendment, cancellation and rebook enqueues its customer notification in the same transaction as the change, and it leaves through Quikstik with the operator in copy. POST /bookings/{booking_id}/resend-confirmation re-sends the current confirmation. GET /obligations/{ref}/deliveries lists what was delivered.