The difficult part of an agentic travel product begins when the user says, “Book it.”
An offer may have expired. A held booking may no longer have a guaranteed price. Payment may succeed while a booking response disappears. A flight may be confirmed while the hotel fails. None of those situations can be repaired by improving the itinerary description.
The right unit of architecture is a governed commerce workflow with explicit commercial state, bounded authority, and recovery ownership.
This is a proposed reference architecture grounded in primary supplier and distributed-systems documentation checked on September 6, 2026. It is not a claim that ContextOS runs a travel platform or that the design has been deployed. The worked scenario is synthetic. Duffel supplies concrete flight examples; its behavior must not be generalized to every airline, hotel supplier, or payment provider.
Start with supplier semantics
Duffel offers contain an expires_at field. An offer represents a purchasable itinerary at a price for a limited time; the deadline is a property of that offer. The agent’s cached search result cannot extend it.
Duffel order creation distinguishes instant orders from held orders. A hold is available only when the offer supports deferred payment. Its holding-orders guide distinguishes the payment deadline from the price-guarantee deadline. A seat hold and a guaranteed price are therefore separate facts.
Order cancellations have a separate quote-and-confirm flow. A cancellation request and its refund information are not the same as a completed refund settlement. These distinctions should remain visible in the domain model instead of being flattened into booking_status: success.
These are integration facts. The architecture below is our synthesis of their consequences.
Divide the system by responsibility
| Component | Responsibility | Must not decide alone |
|---|---|---|
| Conversation and intent service | Resolve travelers, constraints, and requested work | Whether vague language authorizes a purchase |
| Offer normalization service | Preserve supplier IDs, amounts, conditions, and expiry | That different suppliers have identical guarantees |
| Proposal service | Present one concrete itinerary and commercial terms | That an old approval covers changed terms |
| Policy and approval service | Check identity, spend bounds, risk, and decision basis | That a successful tool response proves fulfillment |
| Durable commerce coordinator | Own workflow state, operation IDs, retries, and recovery | That compensating a flight is always possible |
| Supplier adapters | Enforce provider-specific contracts and obtain receipts | Broader user intent or new purchase authority |
| Reconciliation service | Resolve unknown outcomes against authoritative state | That a temporary absence proves no booking exists |
| Operations console | Handle unresolved cases with evidence and ownership | Silent substitutions outside the approved scope |
The planner can suggest a next step. The coordinator validates whether that step is legal in the current workflow state. Credentials and mutating supplier calls stay behind adapters.
In the ContextOS five-plane model, traveler identity and preferences belong to Intelligence; offer and policy assembly to Context; the bounded workflow to Decision; supplier effects to Action; and approvals, observations, and audit to Trust. These are mappings to existing concepts, not additions to the portable schema.
Model a trip as several state machines
“Trip confirmed” is a projection over independent facts:
Trip intent
├─ approved proposal version
├─ flight order and fulfillment state
├─ lodging reservation state
├─ payment authorization / capture / refund state
└─ unresolved operations and recovery obligationsA flight reference may identify a reservation without proving all ticketing or fulfillment obligations are complete for the integration in use. Define what counts as confirmed for each adapter, using documented supplier states and independently observable receipts.
For each component, preserve supplier identity, supplier object ID, traveler references, itinerary version, currency, price components, cancellation conditions, observation timestamp, expiry deadlines, and workflow operation IDs. Keep payment instruments in their dedicated payment boundary, with opaque references in the orchestration layer.
Do not normalize away unknown fields. If an adapter cannot determine refundability, represent that uncertainty and require the product’s chosen handling path. “Unknown” is not interchangeable with “free cancellation.”
Approve a commercial proposal
A useful approval binds the traveler set, itinerary, dates, cabin or room type, included services, supplier, total price and currency, cancellation terms, proposal version, and validity window. Approval also needs a defined policy for tolerable changes.
A price decrease does not necessarily preserve equivalence: a cheaper offer can remove a bag, change an airport, or introduce a self-transfer. A price increase requires fresh approval unless the user has already authorized a specific bounded repricing rule. Such a rule should name its ceiling and protected attributes.
For a multi-currency trip, either present separate currency obligations or bind an explicit exchange-rate quote and its expiry. Comparing bare amounts across currencies is an invalid policy check. Use exact monetary representations rather than binary floating-point totals.
Immediately before dispatch, validate the current offer and approval basis. Duffel’s response-handling documentation recommends checking offer expiry before creating an order. That local check reduces avoidable failures; only the supplier can determine whether the offer is still fulfillable at request processing time.
Keep the happy path explicit
For a supplier-supported instant purchase, the conceptual flow is:
resolve intent -> search -> normalize -> refresh selected offer
-> present terms -> bind approval -> check current eligibility
-> reserve local spend -> durably record purchase intent
-> dispatch through adapter -> reconcile supplier result
-> verify fulfillment -> present confirmationFor a supported hold, insert the hold state and track both payment and price-guarantee deadlines. Revalidate price and authority before paying. Never implement a hold by merely saving an offer in the agent’s memory.
This architecture does not prescribe a universal “charge, then book” sequence. Some suppliers couple payment with order creation, others permit deferred payment, and an independent payment processor adds another state machine. Each adapter declares its supported sequence and failure semantics before the coordinator enables that path.
Treat ambiguous outcomes as durable work
The most important error class is not failed; it is unknown.
The coordinator sends a purchase and receives no response. Three worlds remain possible: the supplier rejected it, the supplier committed it, or it is still processing. Retrying with a new operation ID can create another purchase in the latter two worlds.
Persist a logical operation ID before dispatch and reuse it across transport retries where the provider contract permits. Keep attempt IDs separate. Record provider request IDs, timestamps, sanitized payload digests, and any booking references already observed.
Stripe’s idempotency documentation illustrates why the provider contract matters: it describes key reuse, parameter matching, retention, and replay of stored responses, including certain failures. Those details are Stripe-specific. Do not assume that a flight provider implements the same behavior or that your local idempotency key reaches it.
The proposed recovery rule is:
- Mark the outcome unknown and keep its reservation active.
- Query through a documented supplier reconciliation path.
- Retry only if the operation and provider semantics make that safe.
- Escalate when the evidence cannot distinguish committed from uncommitted.
- Tell the user what is confirmed, what remains unresolved, and who owns the next step.
Do not treat an eventually consistent lookup returning nothing as definitive absence. Declare a reconciliation deadline, polling policy, and operator handoff path per adapter. A background worker can continue observation, but must not gain authority to buy a replacement trip merely because time elapsed.
A synthetic failure trace
Assume the user approved a flight for USD 420, and the adapter’s documented reconciliation mechanism can locate the logical purchase. This assumption is part of the example, not a promise about every provider.
| Event | Durable observation | Required response |
|---|---|---|
| 1 | Proposal v7 approved with named traveler and fare conditions | Bind approval to v7 |
| 2 | Offer refreshed; terms still match | Reserve USD 420 against the trip allowance |
| 3 | Purchase op_flight_1 dispatched | Persist attempt reference |
| 4 | Connection closes without a result | Mark unknown; keep reservation |
| 5 | Worker restarts | Reload op_flight_1; do not mint another purchase |
| 6 | Supplier lookup resolves one matching order | Attach supplier order and payment evidence |
| 7 | Required fulfillment state is observed | Mark confirmed and notify the user |
The control that would prevent duplication is stable operation identity plus reconciliation under the adapter contract. This is a design trace, not an observed production incident or a measured control success.
If event 6 cannot resolve the outcome, the correct terminal state for the current automated attempt is unresolved handoff. A reassuring confirmation message would be false.
Multi-supplier recovery is a commercial decision
Suppose the flight confirms and the hotel fails. The trip is partially fulfilled. Canceling the flight may impose a fee or destroy scarce inventory; buying another hotel may exceed the user’s authority.
The saga pattern coordinates local transactions and compensating actions across services, while acknowledging concurrency and compensation failure. It provides a useful structure, but does not make compensation a rollback of commercial reality.
Before execution, define a recovery policy for each partial state:
| Partial state | Proposed default |
|---|---|
| Flight held; hotel unavailable | Preserve or release the hold according to its terms and deadline |
| Flight confirmed; hotel unavailable | Offer approved alternatives or request a bounded new decision |
| Payment captured; fulfillment unknown | Reconcile both ledgers before another charge or refund |
| Cancellation accepted; refund pending | Track refund settlement separately |
| Recovery deadline exceeded | Assign an operator with the full evidence packet |
Recovery actions have their own costs, conditions, and approvals. A “cancel everything” instruction is not implied by approval to buy a trip. Recovery can also fail, so it needs durable retries, observations, and ownership just like purchase.
Keep memory out of purchase authority
Remembered aisle preferences can help rank offers. A remembered spending limit, passport detail, loyalty number, or traveler identity may require fresh verification under policy. A previous successful purchase does not establish standing authorization for a new one.
Store preferences with provenance, scope, consent, and correction history. Fetch transactional truth from the relevant system of record. The feature-store-to-memory design explains why the time a fact became available matters for replay and why a retrieved preference cannot grant permission.
Evaluate outcomes that matter
Use supplier sandbox tests where available, deterministic fault fixtures for boundary cases, and explicitly authorized operational validation for live behavior. Never claim that passing a simulated airline task proves real ticketing readiness. τ-bench’s airline environment is useful for conversational policy-following, but is not a live settlement or supplier certification test.
The release scorecard should include price-and-terms agreement at purchase, unauthorized effects, duplicate orders or charges, confirmed fulfillment, unknown-outcome duration, partial-trip recovery, refund reconciliation, and operator workload. Track denominators by supplier and workflow path; an instant-order success rate can conceal a failing hold-and-pay path.
Adversarial cases should include expired offers, changed baggage terms, approval after repricing, response loss after commit, duplicate callbacks, out-of-order updates, worker restart, and a conflicting human change to the itinerary. A stale webhook must not regress a newer confirmed state; use supplier version semantics where available and reconcile ambiguous ordering.
No implementation or live booking test is claimed in this article. The reference architecture is valuable when it becomes an adapter contract and a falsifiable acceptance suite: every promise to the traveler should correspond to an observable commercial state.
What to read next
- Proof-carrying agent runtime describes binding authority to exact effects.
- Agent Harness Benchmark protocol provides the fault and outcome measurement design.
- Canonical execution contracts explain ContextOS’s existing integration boundary.