← All case studies
Case study Holdline Proof of concept

One-to-one service, at disruption scale.

When a flight is cancelled, the hard problem isn’t one conversation. It’s resolving every passenger safely, concurrently and personally.

Holdline is a proof of concept for a fictional airline, Air Eleven. One voice agent built on ElevenLabs Agents calls every affected passenger at once, rebooks them in their own language, hands off to a specialist when needed, and never promises the same seat twice. I designed and built it end to end: the agents, the reservation service, the CRM integration and the 4:47 narrated demo of a real live run below.

5

concurrent calls from one disruption event

<3 min

to resolve every passenger

0

double bookings, with two callers racing for one seat

The narrated demo (4:47): a real live run of all five calls. Watch on Loom ↗

The challenge

01

At 10 a.m., Air Eleven cancels the 4:00 PM from Toronto to Vancouver. Five passengers now compete for two later departures: a 6:00 PM with very few seats left and a 7:40 PM with five.

Most voice-agent demos are one call with one person. Disruption recovery isn’t: every call happens at the same time, every passenger needs something different, and they all draw on the same seats. Each passenger here stands for a requirement a real airline would have.

Illustrated portrait of Sophie

Sophie

Multilingual

Her first language is French. The same live conversation has to adapt to her, without a separate queue.

Illustrated portrait of Priya

Priya

Handoff

She needs wheelchair assistance. Rebooking alone isn’t enough, and a specialist handoff shouldn’t make her repeat her story.

Illustrated portrait of Marcus

Marcus

CRM + policy

He wants cash compensation and a business upgrade. Personalisation has to stay inside policy.

Illustrated portrait of MayaIllustrated portrait of Daniel

Maya and Daniel

Concurrency

Both want the last seat on the 6:00 PM, on separate calls. Only one of them can get it.

The setup: five passengers, two flights, one shared source of truth
The setup. Five passengers, two flights, one shared source of truth.
Outreach: calling 5 of 5, five tiles moving from dialing to connected
The trigger. One disruption event, five concurrent outbound calls over Twilio.

The approach

02

Airy, the rebooking agent, calls all five passengers at once. Each conversation is independent; the seat inventory is not. Every call works against one authoritative reservation service.

That split is the core design rule: the model is never the source of truth for inventory. Airy can only promise what the reservation service grants through server tools. The agent decides how to talk; the service decides who gets the seat.

To test it without people on the line, each passenger is an ElevenLabs agent too, with its own persona, voice and phone number. Airy dials real numbers and talks to them over the public phone network, so the whole scenario can be rerun on demand.

Airy

The rebooking agent. Calls, verifies, offers flights, holds and confirms seats.

Outbound calls via Twilio, dynamic variables, webhook tools, guardrails, language detection

Accessibility Agent

Picks up Priya’s call with her context already attached.

Agent-to-agent transfer

Five passenger agents

Fictional personas that answer the phone and push on each capability.

Persona prompts, voices, French and English

Eight tools

get_alternatives, hold_seat, confirm_rebooking, release_hold, verify_passenger and three more.

Webhook tools, plus an MCP server for get_customer_context from Salesforce

Post-call webhook

Releases any seat a dropped call left behind, then updates the Salesforce case.

Signed post-call webhooks

24 agent tests

Simulated conversations that run before any change reaches the live agents.

Agent testing, evaluation criteria

Same call, her language

03 · Multilingual

Airy opens in English. When Sophie answers in French, the same live conversation adapts. ElevenLabs Scribe transcribes her French, and Airy carries on with the same voice, tools and session context, including the identity check where she spells her surname letter by letter. One workflow, now in Sophie’s language.

Sophie’s call live: language detected as French, Airy speaking French, with a translation underneath
Mid-call. Language detected, Airy carries on in French, with a live translation for the viewer.
Recap card: same call, her language. An English line on the left, Airy’s French reply on the right
Recap. No transfer, no separate queue.
Worked
Language switching mid-call was reliable once detection was on for the agent.
Friction
Spelled letters (“T comme Thérèse”) are where speech recognition struggles most. It took prompt changes for the spelling to survive, and masking so booking codes never reached the screen.

She said it once

04 · Specialist handoff

Priya mentions her wheelchair once. Airy verifies her and holds a seat on the 6:00 PM before transferring the live call, so the seat can’t be lost during the handoff. ElevenLabs carries the conversation context into a separate Accessibility Agent, which greets her by name and already knows her flight and her need. Priya said it once.

Priya’s call after the transfer: the Accessibility Agent speaking, with a handover panel showing context from Airy: verified, 6:00 PM held, mobility assistance
After the handoff. The specialist opens with her name, her flight and her need.
Recap card: Priya said it once. Airy hands context to the Accessibility Agent
Recap. Context moves with the call.
Worked
Agent-to-agent transfer kept the call and the context in one place.
Friction
The transfer replaces the first agent immediately. In the word timestamps, the specialist’s “Hi Priya” started 0.1 s after Airy’s last words, cutting off Airy’s goodbye. A longer transfer delay fixed it.

Policy held. He still said yes.

05 · CRM + policy

Marcus has a wedding tomorrow and wants cash compensation and a free business upgrade. Airy never promises the exception. It logs the request for customer care, pulls his profile from Salesforce and offers a window seat on the 6:00 PM, his saved preference. He still says yes.

Marcus’s call live: “Hold on. After this mess, I want cash compensation and a free upgrade”
The demand. Cash and a business upgrade.
Recap card: policy held, he still said yes. Compensation and upgrade logged for customer care; his profile’s window seat used for the 6:00 PM
Recap. One preference used, no policy broken.
Worked
Platform guardrails plus prompt rules (no upgrades, no rebates, prompt-injection resistance) held under pressure. The MCP lookup ran while Airy kept talking.
Lesson
Refusing is easy. Refusing and still closing the call needs real context, and that came from the CRM, not the prompt.

One seat, one promise

06 · Concurrency

The concurrency test. Maya and Daniel are on separate calls and both ask for the last seat on the 6:00 PM. The conversations are independent; the inventory isn’t. Both calls reach hold_seat, and one atomic hold in the reservation service (a single conditional SQL UPDATE) decides, so only one request can ever take the seat. Whichever hold lands first gets the seat; the other caller hears it has sold out and recovers to the 7:40 PM in the same call. In the run shown in these screenshots Maya won it. In the live take in the video, Daniel did.

Split screen: Maya’s call holds the last 6:00 PM seat, Daniel’s call is told it sold out and moved to the 7:40 PM, with a shared inventory panel between them reading 1 → 0, full
The last-seat race, from the recorded run. The inventory service granted the seat to Maya; Daniel’s agent heard sold_out mid-call and moved him to the 7:40 PM.
  1. maya hold_seat AE458 6:00 PM → held 1 → 0 seats
  2. daniel hold_seat AE458 6:00 PM → sold_out
  3. daniel hold_seat AE460 7:40 PM → held same call
  4. maya confirm_rebooking → confirmed
Intro card: two callers, one seat. Maya and Daniel both want the 6:00 PM
The setup. Two callers, one seat, and neither call knows about the other.
Recap card: one seat, one promise, 0 double bookings
Recap. Zero double bookings.
Worked
The agent handled sold_out gracefully and pivoted in the same breath. Sold out is shown as protection working, not as an error.
Proof
A test fires 50 threads at one seat, five rounds: exactly one hold every time. A deliberately naive read-then-write version runs through the same harness and must over-grant, which proves the race is real.

Architecture

07 · Under the hood

Each layer owns one job. Twilio handles telephony. ElevenLabs Agents runs the conversation layer. Salesforce provides customer context. The reservation service owns inventory and atomic holds.

Inside ElevenLabs, Airy uses verification, CRM, booking and transfer tools. Trusted context is bound to each call through dynamic variables. Only after the passenger is verified do server tools like hold_seat transact against the inventory service, and each conversation record shows those tool calls succeeding.

Every prompt, tool schema and agent setting lives in the repository and is pushed to ElevenLabs by a sync script, so a prompt change is a reviewed diff with tests, not a click in a dashboard.

Architecture: disruption event, telephony, ElevenLabs Agents with Airy and the Accessibility Agent, Salesforce, API middleware, and the reservations and inventory service
Architecture. Solid lines were built for the proof of concept; dashed ones show where a real airline’s systems would sit.
ElevenLabs dashboard: the tools Airy can call
The ElevenLabs workspace: verification, CRM, booking and transfer tools.
ElevenLabs conversation view showing tool calls that succeeded during a real call
A conversation record confirming the tool calls ran.

Five passengers resolved in under three minutes

08 · Results
5

passengers resolved

4

rebooked automatically

1

specialist handoff

0

double bookings

2

languages served

Disruption recovery results: 5 contacted, 4 automatically rebooked, 1 specialist handoff, 0 inventory conflicts, 2 languages
Results screen. Every number is computed from the run’s actual state, never hard-coded.

What I learned

09 · Findings

Put guarantees in code, conversation in the agent.

The agent is very good at the human part: tone, language, recovering from “sold out”. It should never be the thing that decides scarce resources. Tools that return a decision, idempotently, made the agent’s job simpler.

Agents testing agents is the unlock.

Persona agents on real phone numbers turned a hard-to-test system into one I could rerun on demand. Agent tests in simulation caught prompt regressions before any call minutes were spent.

Live calls never run on a fixed clock.

Calls connect seconds apart and every conversation takes a different path. The presentation had to follow events from the calls, not a script, and skip a beat that didn’t happen instead of faking it.

The details live in the audio.

Spelled names, a brand name read as “Air 11”, a goodbye cut off by a transfer. Most fixes came from reading word-level timestamps, not from changing the model.

Outcome

10 · Outcome and scope

The result is one-to-one service at disruption scale: personalised conversations, deterministic controls, and no tradeoff between customer experience and operational integrity.

Holdline is a proof of concept, not an airline product. The airline, passengers and flights are fictional, and the inventory service stands in for a real reservation system. Taking it to production would mean Postgres instead of SQLite, batch calling with a priority engine (connection risk, fare class, loyalty), staged agent versions and real operator authentication.

Closing screen: Air Eleven, disruption resolved, with each passenger’s outcome
Every passenger resolved: four rebooked by Airy, one by the Accessibility Agent.
About the screenshots
Screens are captured from the demo page replaying a recorded run of all five calls, which is how the demo is rehearsed without placing new calls. In the live take shown in the video, the race went the other way: Daniel won the seat and Maya moved to the 7:40 PM.
Stack
ElevenLabs Agents (GPT-4.1 mini for Airy and the specialist, Gemini 2.5 Flash Lite for the passengers, Eleven Flash and v3 Conversational voices), Twilio, FastAPI, SQLite, Salesforce over MCP. Built with Claude Code.

Building a voice agent?

Back to portfolio