The travel API is not the architecture
A durable travel stack needs a control plane that can remember, constrain and repair a trip long after search has returned.
A search response can make a travel integration look deceptively small. Send an origin, a destination and a date; receive a neat array of offers. It feels like commerce with unusual inventory.
Then the itinerary changes.
A supplier reprices between search and confirmation. A traveller adds a bag. One segment is cancelled. The refund arrives in two parts. Finance needs to explain why the amount captured, the amount paid to suppliers and the amount returned do not line up on the same day.
That is when an adapter stops being an architecture.
The useful architectural boundary is not “our API versus their API.” It is a travel control plane: the layer that turns several supplier dialects into one durable order, records every transition and applies policy before side effects happen.
Four planes, one traveller promise
An understandable travel stack separates four concerns without separating their history.
| Plane | Responsibility | The failure it contains |
|---|---|---|
| Supply | Connect, normalise and rank offers | Supplier-specific schemas leaking into product code |
| Order | Preserve the trip’s canonical state | Competing PNR, reservation and payment truths |
| Policy | Decide what is allowed | Invalid cabin, budget or servicing actions |
| Event | Publish what changed, in order | Silent divergence between product, support and finance |
The supply plane is where NDC, GDS, low-cost carrier, hotel and other sources become comparable objects. It should not pretend those sources are identical. It should expose customer-relevant distinctions such as refundability, baggage, breakfast and room type while containing implementation-specific ones. The width of that boundary is the point: OnArrival normalises 517+ carriers, including 140+ low-cost carriers that never appear in a GDS, alongside a hotel graph of 2M+ properties deduplicated to a duplicate rate under 0.1%, with around 97% of mappings resolving automatically. Each source sits at a different capability tier, and NDC alone spans schema generations and servicing depths that nothing above this plane should ever have to see.
The order plane begins before money moves. It links the selected offer, its recheck, payment authorisation and supplier confirmation. After booking, it becomes the stable home for exchanges, cancellations, disruptions and settlement.
The policy plane is a gate, not a suggestion rendered in the interface. A rule that exists only in a client can be bypassed by a second channel, a retry worker or an agent. Budget, traveller, approval and tool scopes belong beside the transaction.
The event plane makes the lifecycle legible. OnArrival’s platform page describes a sequence that includes search.completed, offer.rechecked, payment.authorized, booking.confirmed, schedule.changed and refund.settled. The value is not the event names themselves. It is the promise that every material change can be traced back to one order.
Stacked into a running system, the same separation reads as layers rather than a list. Surfaces multiply at the top. Suppliers and money rails multiply at the bottom. The shared objects and the orchestration between them are the part that must not multiply.
Normalise at the boundary, preserve at the core
There are two common mistakes in normalisation.
The first is too little: every supplier field is passed through, and the customer-facing application becomes a thicket of conditions. “If NDC, read this; if GDS, infer that; if LCC, call another endpoint.” Supplier complexity has merely moved upstream.
The second is too much: distinct products are flattened until meaningful conditions disappear. A refundable branded fare is not just a price. A room with breakfast and a pay-at-property rule is not interchangeable with a prepaid net rate.
A stronger model has three layers:
- Canonical fields for concepts that the product must understand consistently: total price, currency, cancellation terms, travellers and fulfilment state.
- Typed capabilities for product-specific richness: seat maps, bags, room occupancy, time slots or insurance cover.
- Source evidence for diagnosis and servicing, retained behind the boundary rather than exposed as application logic.
In payload terms, one hotel offer carries all three layers at once:
{
"offer_id": "OFF-9D4T",
"product": "hotel",
"canonical": {
"total": { "amount": 412550, "currency": "INR" },
"cancellation": { "refundable": true, "free_until": "2026-09-01T18:00:00Z" },
"travellers": 2,
"fulfilment": "instant_confirm"
},
"capabilities": {
"occupancy": { "adults": 2, "children": 0 },
"board": "breakfast_included",
"payment_model": "prepaid_net"
},
"evidence": {
"source_dialect": "bedbank",
"supplier_offer_ref": "HB-88213-4",
"raw_payload": "retained behind the boundary, never read by product code"
}
}
Product code reads canonical and capabilities and never branches on evidence. Support tooling and reconciliation read evidence when a booking needs explaining. When a supplier revises its schema, the blast radius ends at the adapter that produced this object, which is the entire reason the boundary exists.
This lets a team learn one offer shape without losing the facts required to explain or service it.
Treat confirmation as a distributed transaction
Travel confirmation crosses systems that do not share a transaction manager. Payment may authorise while a supplier times out. One hotel confirms while a flight fails. A retry can create a second booking if the first response was lost.
The response should be disciplined rather than magical:
- Give every intent an idempotency key.
- Recheck the offer immediately before commitment.
- Persist each attempted side effect and its external reference.
- Distinguish “failed” from “unknown”; reconcile before retry.
- Make void, cancellation and refund explicit workflows.
- Return a stable order identifier even while fulfilment is resolving.
“Atomic” in travel rarely means every supplier committed in one database transaction. It means the platform owns the partial state and has deterministic next actions. The customer should not become the reconciliation system.
A single-product confirmation is the easy case. The multi-supplier version, where a flight, a hotel and a transfer must commit as one journalled saga with ordered bookings and compensating actions, gets its own treatment in the travel cart essay.
The design goal is not a world without partial failure. It is a system in which partial failure has an owner, a state and a safe next step.
One stream of record, many consumers
Supplier signals arrive in the worst possible shape: webhooks delivered twice, queue messages out of order, a schedule change that lands before the booking confirmation it modifies. Consume them raw and every downstream team re-derives the truth, and each team derives a slightly different one. That is how product, support and finance end up holding three histories of the same trip.
The event plane absorbs that mess once. Deduplicate by supplier reference, order per order, append to a stream that can be replayed from zero. The names matter less than the guarantee: every event carries the same order identifier and lands exactly once in the record.
The payoff is that consumers stop guessing. The interface renders order state instead of computing it. Support reconstructs day 21 from the same timeline the traveller lived through. Finance joins payment.authorized to supplier settlement to refund.settled and can explain any day where the numbers disagree. One stream, three consumers, zero divergence.
Build for day two before launch day
Teams naturally begin with search and checkout because those screens create visible progress. But most architectural debt enters through servicing.
Before launch, walk one itinerary through these cases:
- Supplier price changes after selection.
- Confirmation response is lost after the supplier books.
- One segment changes schedule.
- Traveller requests a voluntary change.
- Refund has a supplier penalty and a separate platform fee.
- A webhook is delivered twice or out of order.
- Support needs to explain the complete history without opening a supplier terminal.
If the answer is “an operations person checks three systems,” the control plane is incomplete.
The same order should support human interfaces, embedded components and agentic tools. Channels can differ; authority and state should not. That is how a business adds an AI agent or a new checkout without inventing a second booking truth.
The build-versus-platform question
Building direct integrations can make sense when one supplier is strategically unique and the organisation can own its certification and servicing surface. It becomes less attractive when the differentiation is the customer experience but engineering time is consumed by schema versions, queues and reconciliation.
Use this decision test:
| Question | Build directly when… | Use a control-plane platform when… |
|---|---|---|
| Supply | One source is the product advantage | Breadth and comparison matter |
| Servicing | Your team wants to own source-native flows | One consistent changes/refunds flow matters |
| Operations | Specialist queues are acceptable | Events and policy must be programmable |
| Expansion | New sources are rare | New products and markets are expected |
| Data | Supplier records are enough | One lifecycle must feed product and finance |
The honest answer can be hybrid. OnArrival describes a model in which existing GDS PCCs, direct deals and platform supply can enter the same offer schema. The architectural principle is more important than the sourcing choice: supply edges may multiply; the order core should not. The NDC, GDS and LCC comparison applies that principle to airline distribution choices.
A control-plane review checklist
Before approving a travel architecture, ask:
- Is there one canonical order ID across search, payment, booking and service?
- Can every mutation be retried idempotently?
- Are rules enforced server-side for every channel?
- Does an unknown supplier outcome enter reconciliation rather than blind retry?
- Can support reconstruct the order from events?
- Can finance connect capture, supplier settlement and refund?
- Can a new supplier be added without branching the product interface?
- Does reduced supplier availability degrade gracefully?
The strongest travel architecture is quiet. It lets the interface focus on a traveller’s decision while the machinery below remembers what was promised. If you are specifying the order itself, the travel order lifecycle turns this control-plane view into concrete states and evidence.