@voyant-travel/bookings) owns the durable, customer-safe operational record of a sale. When a pursuit hardens past the proposal, the result is a Booking: the travelers, the priced line items, the inventory holds, the provenance of how it was created, and the lifecycle state it sits in. Everything operational about a confirmed sale routes through here.
Bookings is the third and fourth rung of the commitment chain: Quote -> accepted Quote Version -> reserve workflow -> Booking -> Fulfillment. A booking does not require a generic first-party order to exist; it is the first-party commitment, and its origin record carries the provenance of wherever it came from. Identity is referenced by snapshot (personId plus organizationId), never owned here.
Key concepts
The durable first-party commitment and customer-safe operational record: travelers, booking items, allocations, fulfillments, redemptions, origin and provenance, and state. Avoid “reservation” and “order”; the booking is the persistent record. See Booking.
A line item on a Booking (unit, service, extra, fee, tax, discount, accommodation, transport). Each allocation belongs to exactly one booking item, and each item produces zero or more fulfillments. See Booking Item.
A capacity hold against a Slot, Pickup, or Resource, moving
held to confirmed to fulfilled. An allocation is the inventory-line entity and belongs to exactly one booking item. See Allocation.The temporal status of a booking before confirmation, carrying a
hold_expires_at. A hold is a temporary, time-limited claim on inventory that expires if the booking is not confirmed in time. Distinct from an Allocation, which is the inventory line itself. See Hold.Bookings-owned provenance describing how a Booking was created. The durable
booking_origins row is keyed by booking_id and records one of accepted_quote_version, catalog_price_availability, catalog_snapshot, provider_source_order, or legacy_transaction, plus the matching reference ids (quote version, trip snapshot, reservation plan, catalog response, provider order ref, or migrated transaction id). New flows write this owner path. See Booking Origin.Issuance of a deliverable artifact (voucher, ticket, PDF, QR, barcode) for a booking item. A fulfillment is delivered over exactly one channel, and consumed at the point of service by a redemption. See Fulfillment.
A Traveler on the booking, carrying category and PII. Travel details (identity document, date of birth, dietary and accessibility needs) are KMS-encrypted and can be pre-filled from a Relationships Person document snapshot.
The commitment ladder
A booking is the operational endpoint of the chain. Each rung hardens what came before.Quote
A tracked pursuit in the quotes module, with a Person or Organization.
Accepted quote version
The client accepts a sent quote version. That marks the version accepted, closes the quote won, and seeds the reserve workflow. Acceptance is a sales decision, not supplier confirmation.
Reserve workflow
Reserve takes the accepted version’s frozen snapshot, rechecks sellability and cost on live lines, moves manual lines into supplier confirmation, and secures the component commitments. A reservation plan captures what was reserved (and what failed) before any booking is created.
Booking
Once reserve secures the commitments, the Booking is created. It holds allocations against slots (or pickups, or resources) for each item, snapshots the buyer, and records its origin pointing back at the accepted quote version.
Status state machine
Booking status moves along an explicit transition graph; illegal transitions raise aBookingTransitionError. The transitions are:
| From | Allowed next states |
|---|---|
draft | on_hold, awaiting_payment, confirmed, cancelled |
on_hold | awaiting_payment, confirmed, expired, cancelled |
awaiting_payment | confirmed, expired, cancelled |
confirmed | in_progress, cancelled |
in_progress | completed, cancelled |
completed, expired, cancelled | terminal |
confirmedAt on confirm, paidAt additionally when confirming out of awaiting_payment (so reporting can split free confirmations from paid ones), expiredAt, cancelledAt, and completedAt. Cancel is the operational reversal of a booking, distinct from voiding an invoice (a financial reversal) and from closing a quote.
An admin can override status outside the transition graph, but only as an explicit, audit-logged action with a required reason. The transition graph is the default; the override is the escape hatch.
PII handling and audit logging
Booking travelers carry sensitive personal data: passport numbers, dates of birth, dietary and accessibility needs. Bookings treats this PII as first-class.- Encryption at rest. Identity, dietary, and accessibility fields are encrypted through a KMS provider into JSON envelopes. The PII service (
getTravelerTravelDetails,upsertTravelerTravelDetails,deleteTravelerTravelDetails) is the only path that decrypts them, and decryption is on demand per traveler. - Audit logging. Every encrypt, decrypt, and delete of traveler travel details emits a
BookingPiiAuditEventcarrying the action, the traveler id, and the acting principal, through the service’sonAudithook. Reads of PII are logged, not just writes. - Snapshot, do not re-key. A booking traveler can pre-fill its travel snapshot from a Relationships Person document. Explicit input always wins; the snapshot only fills the gaps. Provenance of the snapshot (the source person-document id) is recorded.
- Route-boundary redaction. Callers without the PII-reveal scope receive redacted contact and traveler fields at the API boundary (email, phone, names, tax id, and address lines are masked). Internal callers such as cron jobs and workflows bypass redaction; redaction is an in-flight protection at the route layer.
Action-ledger approvals
Agent and workflow status mutations that require approval do not mutate the booking directly. They return202 with the requested action and approval ids, and must send an Idempotency-Key. The key is fingerprinted with the command input and the approval-policy inputs, so replaying the same key with different input returns a conflict rather than a second action.
After approval, the same status mutation is executed again with the approval-id header from @voyant-travel/action-ledger. The route revalidates that the approval is approved, unexpired, linked to the same requested action and current principal, and command-equivalent to the original request before it touches the booking. Approved execution ledger fields are stamped so execution entries link back to the requested action and its approval.
What it owns
- Bookings (
book) and their status state machine. - Booking travelers (
bkps) with KMS-backed PII and snapshot pre-fill. - Booking origins (
booking_origins): the provenance owner path. - Booking items, allocations, and the hold lifecycle.
- Booking supplier statuses (
bkss), the booking activity log (bkal), and booking notes (bnot). - Booking session states (
bkst): first-class persisted wizard state for the public storefront flow, with preview andapplyToSessionrepricing. - Booking requirements (
./requirements) and the supplier extension (./extensions). - The reservation-plan and refund workflows, and the dependency-injected checkout capability.
Working with it
Mount the module and drive a booking through its lifecycle.Links to other modules
- quotes feeds the accepted quote version that a booking’s origin points at. The
booking_crm_detailsextension links the booking back to its quote. - relationships owns the buyer snapshotted onto the booking and the person documents that seed traveler PII.
- trips groups independent component bookings into one Trip Envelope without erasing their per-component lifecycle boundaries.
- catalog is where the booking engine lands rows; bookings is the shared parent so an itinerary can mix owned and sourced lines.
- The finance module attaches payment schedules and invoices to a booking; legal attaches contracts and policy acceptances.
React package
@voyant-travel/bookings-react is the client tier. Headless consumers import from the root, ./hooks, ./client, or ./query-keys with no styling peers, including public storefront helpers (usePublicBookingSession, usePublicBookingSessionState, usePublicBookingSessionFlowMutation) that target the wizard-state and repricing contract. Styled surfaces live under ./ui, ./components/*, ./journey, and ./admin, with optional heavier peers. The customer-facing booking journey under @voyant-travel/bookings-react/journey pairs with the catalog booking-engine surfaces.
Next steps
Quotes
The accepted quote version that seeds the reserve workflow.
Trips
Trip envelopes that compose multiple component bookings into one itinerary.
Catalog
The sellable plane the booking engine lands owned and sourced rows into.
Glossary
The shared vocabulary: Booking, Booking Item, Allocation, Hold, Origin, Fulfillment.