Agentic AI Travel Booking for TMCs and OTAs

The senior move for a booking agent isn't a smarter model. It's taking the LLM out of the control flow.

We built an autonomous travel booking agent whose control flow is deterministic Python. The model only parses the trip request and words the reply. Every booking step registers a compensating action, so when a hotel rate expires after the flight is ticketed, the agent voids the ticket inside the 24-hour window and hands the traveler honest alternatives instead of stranding them.

100%

Consistent terminal state, by construction

Fixed-seed 200-scenario synthetic batch, vs 65% for the LLM-in-control baseline

0 / 0

Stranded travelers, fabricated bookings surfaced

Same batch, vs 40 and 30 for the baseline

$3.25

Avg GDS search spend per booking

vs $7.57 baseline; GDS charges per search, Lufthansa raised fees Jan 1 2026

All scenarios are synthetic over a simulated GDS and CRS; airport codes and hotel names are real-shaped fixtures, not live inventory or real bookings.

Put an LLM in charge of a transaction and two things go wrong

Neither of them is a model-IQ failure. Both are already a 2026 bug class.

It strands travelers

The agent tickets a flight, the hotel step fails, and with no compensation logic it still says "You're all set." Someone is left holding a flight and no room. A perfect model still strands a traveler if nothing voids the ticket.

It surfaces inventory that doesn't exist

It invents a plausible hotel and books it. The property was never in the CRS. Nothing checked before it reached the traveler, because in a reasoning loop the model is both the proposer and the judge.

The reason a better model does not fix this is that the failures are infrastructure and usage events, independent of model quality. A rate expires between two API calls. A hold is rejected after a ticket is issued. A search storm burns margin. Stochastic reasoning compounds against these: ten steps at 90 percent reliability come out to roughly 34 percent end to end, and GPT-4 with ReAct completes real multi-day itineraries at 0.6 percent (TravelPlanner, OSU NLP, arXiv 2402.01622). You cannot prompt your way out of compounding stochastic failure.

And the deployer owns every statement the agent makes. In Moffatt v. Air Canada (BC Civil Resolution Tribunal, February 14 2024) the airline was ordered to pay $812.02 after its chatbot invented a bereavement-fare policy, and the argument that the AI was a separate entity was rejected.

How it works: agents advise, code decides

The control flow is a hand-built Python state machine of roughly ten nodes. The LLM is confined to two leaf jobs. Everything between them is deterministic.

input → extract (LLM leaf) → search → policy gate → verify gate → hold → ticket → hotel-book → commit

Extract, the LLM's only structured job

The model parses natural-language intent into a Pydantic-typed TripRequest (origin, destination, date, passengers, cabin, hotel). That typed object is the only structured artifact the LLM produces. It is provider-swappable through Pydantic AI and runs fully offline with a deterministic stub when no key is present.

Policy compiled to code

Corporate policy lives as pure Python predicates: economy only, a $600 per-segment fare ceiling, preferred carriers (United, American, Delta), a $350 nightly hotel ceiling. Out-of-policy options are physically un-presentable because they are filtered before they can be shown, not flagged after. Unknown fare families fail safe, treated as above policy rather than silently as economy. With no in-policy flight, the agent escalates to a human queue rather than bluffing.

The verification gate

Every hotel is confirmed against the CRS by property_id. A property the model invents simply is not in the CRS, so it is refused and never surfaced, and the booking reaches the terminal state abstained. The gate refuses unconfirmed inventory; it does not ask the model to grade its own output.

The Saga, the part most demos skip

Each forward step registers its reverse action at execution time. Ticketing, for instance, registers "void ticket, 24-hour window." On a failure at step N, compensations from N-1 down to 1 run in reverse order, and only then does the agent report. This is what separates a demo from a product, because it is what keeps a partial failure from becoming a stranded customer.

The GDS-cost meter and the audit trail

A live counter tracks GDS search spend at $3.25 per segment, because searches are charged, not just bookings. An L2B cache and deferred search keep it flat where a speculative agent re-searches and burns margin. Every booking writes an append-only JSON event log, exportable as audit-<pnr>.json, carrying the model and version, the typed trip request, every node verdict, every Saga compensation, the EU AI Act Article 50 disclosure flag, and the terminal state.

What the demo shows

Four buttons, side by side with a real LLM-in-control baseline on the same scenario. Every screenshot below is captured from the running app.

A normal booking, node by node

The demo running a normal ORD to SFO booking. On the right, the deterministic pipeline trace runs each node in turn, from intent extraction through policy gate, CRS verification, holds, ticket issue, and hotel commit, ending in a confirmed PNR with the GDS search spend meter reading $3.25. On the left, the panel labeled No Tools and No Verification simply replies that everything is booked.

"ORD to SFO next Tuesday, one night downtown, company policy." The state machine runs each node, confirms the Hyatt Regency SF against the CRS by property_id, and the search meter holds at $3.25 on one cached search. Terminal state: confirmed, with a PNR.

The verification gate refuses a hotel that doesn't exist

The CRS verification step marked as failed. The detail panel reads that Tabacon Springs Eco-Lodge is not in the CRS and has been refused, not surfaced. The property card is stamped REFUSED with the note that the verification gate refused it and it was not surfaced to the traveler.

The request named a fabricated property, "Tabacon Springs Eco-Lodge," a name that blends two real resorts and has no property_id by design. The gate finds no CRS match and refuses to surface it. The agent abstains honestly, "I could not confirm that property," instead of inventing one.

The Saga rollback, versus the LLM in control

The pipeline trace after a hotel rate expired following ticket issue. Three Saga compensation steps run in reverse, and a banner reads that the hold expired before commit and the Saga rollback is compensating in reverse. The result card reads ROLLED BACK, TRAVELER SAFE, with a note that the flight ticket was voided at no charge and alternative hotels are offered.

The hotel rate expires after the flight is already ticketed. On our side the Saga fires: void the ticket inside the 24-hour window, release the holds, and reply honestly that the ticket was voided at no charge with alternatives attached. Terminal state: rolled back, traveler safe. The baseline in the same scenario leaves the ticket issued, offers no compensation, and emits a false "You're all set," which is exactly the Air Canada precedent waiting to happen.

An exportable audit trail

The bottom of the console showing the Export Audit Trail (JSON) link, next to the rolled-back result explaining that the held rate expired before commit, the ticket was voided at no charge, and two alternative hotels are offered, with the GDS search spend at $3.25.

One click exports audit-<pnr>.json: the model and version, the typed trip request, every node and its deterministic verdict, every Saga compensation, the EU AI Act Article 50 disclosure flag (transparency obligations apply from August 2 2026), and the terminal state.

The 200-scenario benchmark

The benchmark scoreboard over 200 synthetic bookings at a fixed seed with identical injected failures. Four tiles compare the deterministic agent to a plain LLM baseline: 100 percent versus 65 percent consistent terminal state, 0 versus 40 stranded travelers, 0 versus 30 fabricated bookings, and $3.25 versus $7.57 average GDS spend. A results table lists per-scenario outcomes including confirmed, rolled back, abstained, escalated, integrity breach, and stranded.

The same 200 synthetic bookings, one fixed seed (42), and the same injected infrastructure failures run through both architectures. The scenario mix is 50 percent happy, 20 percent hotel-fail-after-ticket, 15 percent hallucinated-entity, and 15 percent search-storm. Our guarantees hold by construction; the baseline's failures emerge from the same data.

Deterministic control flow versus an LLM in the loop

The baseline is a real ReAct-style LLM-in-control agent run on the same scenarios, an honest anchor rather than a strawman. Numbers below are over the fixed-seed 200-scenario synthetic batch (benchmark.py, seed 42, n=200).

Metric Deterministic agent (ours) Baseline (LLM-in-control)
Consistent terminal state 100.0% 65.0%
Stranded travelers 0 40
Fabricated bookings surfaced 0 30
Avg GDS search spend per booking $3.25 $7.57

The 100 percent, 0, and 0 hold by construction over this fixed-seed synthetic batch, not as an open-world production guarantee. The claim is narrow and durable: a partial booking is never shown as confirmed, and a traveler is never stranded. The $3.25 versus $7.57 gap is a margin number that holds at any model quality.

What this demo does not do

  • It does not connect to a live GDS, CRS, or NDC. The GDS and CRS, IATA and ARC ticket issuance, and PCI payment are stubbed and simulated. The fixture adapter is the V1 integration; there is no live Amadeus, Sabre, or Duffel account.
  • It does not issue real tickets or move real money, and Veriprajna is not IATA or ARC accredited. Tickets and payment are stubs.
  • The scenarios, PNRs, hotels, and travelers are synthetic. "Tabacon Springs Eco-Lodge" is a deliberately fabricated property, a demonstration of the failure mode. Real-named hotels such as Hyatt Regency SF are fixture inventory, not real bookings.
  • It does not claim to book more, cheaper, or smarter than a GDS or OTA, and it does not claim zero hallucination from the model. The LLM still drafts intent; the guarantee is that the gate refuses unconfirmed inventory and the Saga cleans up partial failures.
  • The engine is a hand-built Python state machine, not LangGraph. LangGraph is named as a deferred production swap-in. LLM leaf calls use Pydantic AI.

Questions buyers ask

Can I trust an AI agent to book travel without stranding my travelers?

The guarantee does not come from trusting the model. In this demo the control flow is deterministic Python, and every forward step registers a compensating action the moment it runs. When a step fails after a ticket is issued, the engine runs those compensations in reverse (a Saga), voids the ticket inside the 24-hour window, and reports honestly. A traveler is never left with a flight and no room because nothing depends on the model deciding to clean up.

Does this connect to Amadeus, Sabre, or Duffel?

No. The GDS, CRS, ticket issuance, and payment are all stubbed and simulated in this demo. The fixture adapter is the V1 integration, and there is no live Amadeus, Sabre, or Duffel account behind it. The demo proves the control-flow architecture and the compensation logic, not a production booking pipeline.

What happens if the hotel fails after the flight is already ticketed?

That is the exact case the Saga is built for. Ticketing registers its own reverse action (void the ticket inside the 24-hour window) at the moment it runs. If the hotel rate expires before commit, the engine fires the compensations in reverse order, voids the ticket at no charge, releases the holds, and hands the traveler honest alternatives. The terminal state is rolled back, not confirmed and not stranded.

What stops the agent from inventing a hotel that doesn't exist?

A verification gate confirms every property against the CRS by its property_id before it can be shown. When the request named a fabricated property in our demo, the gate found no CRS match and refused to surface it, and the agent abstained honestly instead of booking it. The gate does not flag a made-up hotel after the fact; it makes it physically un-presentable.

How is this different from putting GPT-4 in an agent loop with tools?

A ReAct-style agent puts the LLM in charge of the transaction, so it decides when to search, book, and ticket, and it has no gate and no compensation logic. On the same fixed-seed 200-scenario synthetic batch, that baseline surfaced fabricated inventory and left tickets issued with no rollback, emitting a false You're all set. Here the LLM is a typed leaf node that only parses intent and words the reply; deterministic code owns the flow and each step carries its own undo.

Who is liable if the agent tells a traveler something wrong?

The deployer owns every statement its agent makes. In Moffatt v. Air Canada (BC Civil Resolution Tribunal, February 14 2024) the airline was ordered to pay $812.02 after its chatbot invented a bereavement-fare policy, and the it-was-the-AI defense was rejected. The demo exports a per-booking JSON audit trail with the model and version, every node verdict, every Saga compensation, and an EU AI Act Article 50 disclosure flag, so what the agent did is inspectable after the fact.

Technical Research

The research behind this demo — the architecture, the verification design, and the enterprise blueprint.

Evaluating an agentic booking layer you can't bet the company on?

The failures that strand travelers and invent hotels are infrastructure events, not model-IQ problems.

If your team is weighing where the LLM belongs in a high-stakes booking agent, and how to keep a partial failure from becoming an Air Canada-style liability, we would genuinely like to compare notes. The problem is industry-wide and the answers will be too.

Agent architecture review

  • ✓ Map where the LLM sits in your control flow today
  • ✓ Identify the steps that need a compensating action
  • ✓ Pressure-test the failure modes: rate expiry, post-ticket holds, search storms
  • ✓ Define the terminal states an operator can trust

Deterministic agent build

  • ✓ A state machine that owns search, policy, and ticketing
  • ✓ A verification gate and a Saga compensation engine
  • ✓ Policy compiled to code, with fail-safe defaults
  • ✓ An exportable audit trail with an Article 50 disclosure flag