The travel API is not the architecture
Search is the part every supplier makes easy. What decides whether you can run a booking business is the layer that remembers, constrains and repairs a trip long after search returned.
Nobody has ever lost money on a search call. Every travel integration that failed in production failed after the money moved, and yet most build-versus-buy decisions in this industry get made by reading a search endpoint.
Search is the easiest thing a travel supplier will ever hand you. Origin, destination, date, a clean array of offers. It looks like commerce with unusual inventory, and for about six weeks it behaves that way.
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 boundary was never “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. Everything else is a client of that layer, including your own checkout.
Four planes, one traveller promise
A travel stack you can reason about 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. Comparable, not identical. The industry’s favourite normalisation mistake is to sand every offer down to a price and a time, which produces a search experience that lies: refundability, baggage, breakfast and room type are what travellers actually choose on. Keep those, contain the rest. 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. If it is first written by the booking response, you have thrown away the evidence for every question support will ask. 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 lives only in a client is not a rule. It holds until someone reaches the same endpoint from 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 names are the least interesting part. The guarantee is what matters: every material change traces back to one order.
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
Most teams make both normalisation mistakes, in order, about four months apart.
The first is too little. Every supplier field is passed straight through and the product becomes a thicket of conditions: if NDC read this, if GDS infer that, if LCC call another endpoint. Supplier complexity just moved upstream into the code that is hardest to change.
The second is the overcorrection. Distinct products get flattened until the conditions that decide a purchase disappear. A refundable branded fare is not just a price. A prepaid net rate is not a room with breakfast and a pay-at-property rule, and a traveller who learns that at the front desk is a support ticket your schema created.
The way out is three layers, not one:
- 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. That is the entire reason the boundary exists, and you will be grateful for it the day a source renames a field without telling anyone.
Treat confirmation as a distributed transaction
No transaction manager spans a card network and an airline’s ticketing system. Everyone in travel knows it and few systems are built as though it were true. Payment authorises while a supplier times out. A hotel confirms while a flight fails. A retry issued in good faith creates a second booking, because the first response was lost, not refused.
The answer is discipline, not cleverness:
- 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 never means every supplier committed in one database transaction. It means the platform owns the partial state and has a deterministic next action for each one. Otherwise the traveller becomes the reconciliation system, because they are the only party holding all four confirmation emails.
The multi-supplier version, where a flight, a hotel and a transfer commit as one journalled saga with 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 exactly once. Deduplicate by supplier reference, order per order, append to a stream that can be replayed from zero.
Consumers stop guessing. The interface renders order state instead of computing it. Support reconstructs day 21 from the timeline the traveller lived through, without a supplier terminal. Finance joins payment.authorized to settlement to refund.settled and can explain any day the numbers disagree.
Build for day two before launch day
Search and checkout get built first because they demo. Servicing gets built last because it does not. That ordering is where almost all travel architecture debt comes from.
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 to any of them is “an operations person checks three systems,” you do not have a control plane. You have an adapter and a rota.
One order should serve human interfaces, embedded components and agentic tools alike. Channels differ; authority and state do not. That is how you add an AI agent without inventing a second booking truth.
The build-versus-platform question
Direct integration is right when one supplier is the product advantage and you will own its certification and servicing surface for years. It stops being right the moment your differentiation is the customer experience but your engineers spend their quarters on schema versions, queue semantics and reconciliation. That trade is easy to describe and very hard to notice while it is happening.
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 is usually hybrid, and hybrid is fine. OnArrival describes a model in which existing GDS PCCs, direct deals and platform supply all enter the same offer schema. The principle outranks the sourcing choice: supply edges may multiply, the order core may not. The NDC, GDS and LCC comparison applies it to airline distribution specifically.
A control-plane review checklist
Before approving a travel architecture, ask the eight questions the search demo never answers:
- 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?
A good travel architecture is boring in production and invisible in the interface. The screen holds one decision. The layer underneath remembers what was promised, on whose money, and what to do when a supplier changes its mind at 2am. If you are specifying that layer now, the travel order lifecycle turns this control-plane view into concrete states and evidence.