← Back to case study
Architecture pipeline

−35% call center load with a voice loan assistant

Industry: Fintech (consumer lending, phone-first application intake)
Scope: A low-latency speech-to-speech voice agent handling first-line loan application calls, escalating complex or credit-decision cases to a live human loan officer
Realtime APITwilioFunction callingPostgres
−35% call center load · 12,000 calls/week automated · 2m 10s average handle time
This is the depth of analysis behind every project we build. We publish it as confirmation of how we actually work — the real options we weighed, the numbers that ruled them out, the architecture that survived contact with a live phone line — so our process stays as transparent to clients as the results are. Voice is a different kind of "carving from stone" than a chat interface: a customer can’t see a typing indicator or scroll back to reread something, so every latency and hand-off decision below had to survive contact with an actual phone call, not a demo.

The brief

The client was a consumer lender whose call center handled first-line loan application calls — "what’s my rate," "where’s my application," "can I qualify for X." Call volume had grown faster than headcount, and the same small set of questions was eating most of an agent’s day before they ever got to the calls that actually needed a person: judgment calls on borderline applications, negotiating terms, handling disputes.

The ask was not "replace the call center with a bot." It was: handle the high-volume, low-complexity first-line calls end to end over the phone, and hand off anything touching an actual credit decision to a human — without the caller having to repeat themselves. That handoff requirement mattered as much as the automation itself; a voice agent that saves time on 80% of calls but makes the other 20% worse by making the customer start over is a net loss, not a win.

The second constraint, non-negotiable from day one: this is a regulated lending workflow. A credit decision has to be explainable to the applicant and reconstructable for a regulator, and that requirement shaped the escalation architecture more than any latency number did.

Non-functional requirements that actually shaped the stack

RequirementWhy it matteredDecision it drove
Response latency has to feel conversational, not like a phone treeHuman turn-taking has a natural rhythm measured in a couple hundred milliseconds; a voice agent that pauses for seconds reads as broken, not thoughtfulSpeech pipeline architecture (§1)
The agent has to work over the same phone number customers already call, no new app or channelForcing a channel switch defeats the point of reducing call center load — the calls still have to come in as callsTelephony integration (§2)
Backend lookups (application status, account details) have to happen mid-conversation without dead airThe whole ask was faster calls, not a robotic "please hold while I check that"Function calling under real-time constraints (§3)
Any case touching an actual credit decision must hand off to a live human with full context, not a cold transferThis is the specific failure mode we were hired to avoid — a customer repeating their whole story to a second person is worse than a slow human-only callEscalation & handoff (§4)
Every call needs a recorded consent disclosure and a transcript both the AI and human staff can query during and after the callRegulated lending requires reconstructable records, and a live handoff requires the human to see context in seconds, not minutesData & session layer (§5)
8-week build window before the client’s next loan season rampRules out anything requiring new infrastructure categories from scratchAll of the above
§1

Speech pipeline architecture — cascaded STT→LLM→TTS vs. native speech-to-speech

The sketches
  • A cascaded pipeline: separate speech-to-text, LLM, and text-to-speech calls chained together — the traditional way to build a voice bot, using components you can mix and match.
  • A self-hosted open-source speech-to-speech model — full infrastructure control, no per-call API cost.
  • OpenAI’s Realtime API — a single native speech-to-speech session over WebSocket/WebRTC, no separate transcription or synthesis step.

Why the cascaded pipeline’s latency stacking was the core problem: OpenAI’s own May 2024 announcement of GPT-4o gives a directly relevant before/after comparison from their own product — their prior cascaded voice mode in ChatGPT, three separate models chained for transcription, text generation, and speech synthesis, averaged 2.8 seconds of latency on GPT-3.5 and 5.4 seconds on GPT-4, against GPT-4o’s native audio responding in as little as 232ms and averaging 320ms, which OpenAI describes as comparable to human response time in conversation. That’s OpenAI comparing their own old and new products, not an independent benchmark, but the architectural reason for the gap is the relevant fact: a cascaded pipeline pays the latency of three sequential network calls, where a native speech-to-speech session pays the latency of one. Independent engineering write-ups from Twilio’s and Telnyx’s own blogs on voice AI latency put a well-optimized cascaded stack in the 600ms-1.7s range end-to-end — still measurably slower than native speech-to-speech, and with more moving parts to keep fast.

Why interruption handling, not just raw latency, was the deciding factor: a caller correcting themselves mid-sentence is completely ordinary phone behavior. A cascaded pipeline generally has to finish playing its current TTS output (or bolt on a separate interruption-detection layer) before it can process new input; OpenAI’s Realtime API, launched in public beta October 1, 2024, handles barge-in natively as part of the same speech-to-speech session, the same interruption-handling behavior as ChatGPT’s Advanced Voice Mode. We’re noting a limit to our own confidence here: we weren’t able to confirm an independently published latency figure specifically for the Realtime API endpoint itself — the honest statement is that it inherits the same speech-to-speech architecture, not that we’re quoting an official Realtime-API-specific number.

Why not self-hosted open-source speech-to-speech: this is the same build-vs-buy tradeoff we’d apply to any latency-critical, regulated workload on a tight timeline — owning GPU inference, model updates, and the interruption-handling behavior ourselves is a real infrastructure commitment, and on an 8-week build window with no dedicated speech-ML function on this project, that commitment doesn’t pay for itself against a managed API with the interruption handling already solved. We’d revisit this if call volume grew large enough that per-minute API cost overtook the infrastructure and ops cost of self-hosting.

Final pick

OpenAI’s Realtime API, a single native speech-to-speech session rather than a chained pipeline — the architecture choice that everything else in this document assumes.

§2

Telephony integration — Twilio vs. alternative providers

The sketches
  • Amazon Connect — a contact-center-native platform with its own agent-desktop and routing tooling built in.
  • Vonage — a comparable CPaaS voice/messaging platform.
  • Twilio, specifically its Media Streams capability — bidirectional audio over a WebSocket, bridging PSTN calls directly to a real-time API session.

Why Media Streams’ bidirectional support was the specific technical fit: Twilio’s own documentation distinguishes `<Start><Stream>`, which only streams call audio out to your application, from `<Connect><Stream>`, which is bidirectional — audio flows both directions over the same WebSocket. That bidirectional path is what lets raw call audio flow directly into and out of a Realtime API session without an extra translation or buffering layer in between, which matters directly because of the architecture chosen in §1: a native speech-to-speech session is only as fast as the audio pipe feeding it.

Why Twilio’s operational maturity mattered on a regulated, customer-facing phone line: Twilio’s own published SLA commits to 99.95% uptime standard, with 99.99% available on Enterprise/Admin Edition. Independent analyst research from Metrigy’s CPaaS Market Share & Forecast (3Q24) put Twilio’s CPaaS revenue share at roughly 23.2% in 2023, growing to roughly 25.9% by Q3 2024 — the largest player in the category by that measure, which we’re citing as third-party market research, not Twilio’s own marketing. For a phone line customers already call to reach a lender, betting on the platform with the deepest operational track record was a reasonable default over betting a regulated customer channel on a smaller, less-proven integration path.

Pricing, and an honest gap in what we could verify: Twilio’s official US pricing page lists outbound voice around $0.013/minute for the base voice product. AI-specific per-minute pricing figures (commonly cited around $0.07/minute across secondary aggregators) weren’t something we could confirm against Twilio’s own pricing page directly, and pricing varies by direction and country regardless — we’re flagging this as a number to re-verify at build time, not one to quote precisely to a client today. We also didn’t find a direct, apples-to-apples latency or reliability comparison against Amazon Connect or Vonage for this specific use case, and don’t want to fabricate one — the honest version of this decision is that Media Streams’ bidirectional WebSocket support was the most direct integration path to the architecture already chosen in §1, combined with Twilio’s market-leading maturity and a contractual SLA, and that combination was enough to not need that comparison to be confident in the call.

Final pick

Twilio, using Media Streams’ bidirectional audio, bridging the PSTN call directly to a Realtime API session over WebSocket.

§3

Function calling under real-time turn-taking constraints

The sketches
  • The standard async tool-calling pattern, the same shape used in text-based chat agents — call a function, wait for the result, continue the response.
  • Pre-fetch everything the agent might conceivably need before the call starts — avoid mid-call lookups entirely.
  • Session-level function calling inside the live voice turn, with the model able to trigger a tool call mid-response and continue speaking once the result comes back, backed by narrowly-scoped, indexed Postgres lookups.

Why the standard text-chat tool-calling latency budget doesn’t transfer to voice: a text chat interface can tolerate a multi-second pause for a tool call because there’s a visible "thinking" or typing indicator and no live-audio expectation to break. A phone call has no equivalent affordance — silence on a live call reads as "did this drop" almost immediately. Stivers et al.’s 2009 PNAS study on turn-taking in conversation, which measured response gaps across ten languages, found an average cross-linguistic response offset of roughly 208 milliseconds, with individual language averages clustering within about 250ms of that figure. We’re citing this deliberately as human-to-human conversational research, not a study of AI voice agents — treating "keep function calls fast enough to not obviously break natural turn-taking" as our own engineering inference from that research, not a conclusion the paper itself draws.

Why pre-fetching everything up front doesn’t actually solve the problem: the information a caller needs isn’t known until they identify themselves partway into the call, so there’s nothing meaningful to pre-fetch before the conversation starts — the lookup has to happen live, inside the call, which is exactly the case the latency budget above has to cover.

Why session-level function calling, specifically, was the fit: OpenAI’s Realtime API documentation describes tools configurable at the session level or per-response, with the model able to signal a function call in the middle of generating a response and incorporate the result into the continuing audio reply, rather than the call blocking the entire turn. The practical consequence for our side of the integration: the Postgres queries backing these functions had to be simple, indexed point lookups — not open-ended reporting queries. The latency budget here is set by conversational norms, not by what the database can technically handle at the low end.

Final pick

Session-level function calling against narrowly-scoped, indexed Postgres lookups, with the tool-call latency budget treated as a conversational-quality requirement, not just a technical performance target.

§4

Escalation to a human loan officer — live warm handoff vs. the alternatives

The sketches
  • Let the agent make the actual credit decision autonomously — fastest and cheapest, no handoff infrastructure needed at all.
  • Async handoff: the agent collects information, ends the call, and routes a summary to a human who calls the applicant back later.
  • Live warm handoff: the agent recognizes mid-call that a case needs a human, and transfers the live call with full context already loaded, so a human loan officer joins the same call rather than starting a new one.

Why letting the agent make the actual credit decision wasn’t on the table, as a matter of regulation rather than caution: the Equal Credit Opportunity Act and its implementing Regulation B (12 CFR § 1002.9) require a creditor to issue an adverse action notice within 30 days of a credit denial, citing the specific principal reasons that genuinely reflect the factors the decision was based on. The CFPB’s own Circular 2022-03, published September 2022, states explicitly that this obligation applies to decisions made using complex algorithms, and that a creditor cannot use the complexity of the algorithm itself as a defense for failing to provide specific, accurate reasons. That’s a regulatory constraint on the architecture, not a risk-tolerance judgment call — anything that could constitute an adverse action needs a traceable, human-explainable reasoning path, which rules out the model being the final decision-maker.

Why async handoff undercuts the point of the project: an async handoff means the caller hangs up without knowing their status, and a callback later loses the context and momentum that made the call efficient to begin with — the applicant often ends up re-explaining details they already gave the agent. That doesn’t reduce handling time, it defers it and adds a second call, which works directly against the reason the project exists.

Why live warm handoff, specifically, was the fit — and what it required elsewhere in the architecture: recognizing mid-call that a case needs a human, then transferring the live call with the transcript and structured application data already loaded on the receiving agent’s screen, means the customer never repeats information already given to the AI. The requirement this drives directly: the data layer (§5) has to make the full call context available to a human agent’s console inside roughly the same window it takes the phone system to complete the transfer — not a batch export that catches up a few minutes later.

Final pick

Live warm handoff, triggered automatically once the conversation crosses into territory that could constitute an adverse action, with the human loan officer’s console pre-loaded with the transcript and structured data by the time the call connects.

§5

Data & session layer — Postgres as a shared system of record

The sketches
  • In-memory session state only, scoped to the voice agent’s own process for the duration of the call, with a summary logged afterward.
  • A dedicated analytics/logging store, separate from the client’s existing loan origination system, purpose-built for transcripts and call metadata.
  • Postgres, shared between the voice agent and the client’s existing loan origination system, acting as the system of record for both live session state and durable transcripts and consent records.

Why in-memory-only session state fails exactly at the moment it matters most: it doesn’t survive the one event the whole architecture is built around — the live warm handoff in §4. If session state lives only inside the voice agent’s own process, and the human agent’s console reads from a separate system, "the transcript is already loaded when the call connects" becomes a race against however long it takes to flush, batch, or sync data between two systems — reintroducing exactly the staleness risk this component exists to eliminate.

Why a separate, dedicated analytics store wasn’t the right shape either: these transcripts aren’t just a record to review later — they’re operational data a human loan officer has to act on inside the same call, and they need to line up exactly with the application data already sitting in the client’s loan origination system. Running that as a second, parallel store that can silently drift from the system of record is a bigger risk in a regulated lending context than the operational simplicity of a dedicated logging pipeline is worth.

The compliance requirement that shaped this, specifically: the Telephone Consumer Protection Act requires disclosed consent before recording a call, and in twelve U.S. states with all-party or two-party consent rules specifically — a detail we’re flagging as needing case-by-case legal confirmation for the client’s actual calling footprint, not a blanket rule we’re treating as settled everywhere. That consent record, together with the full transcript, needs to be durably stored and queryable for as long as the applicable retention window requires.

Final pick

Postgres, shared with the existing loan origination system, as the system of record for live session state used during warm handoffs and for durable transcripts and consent records used for compliance retention — not a parallel, disconnected data store.

Final architecture

Caller
Caller (PSTN phone line)
Twilio
Media Streams
bidirectional audio over WebSocket
Realtime API session
Native speech-to-speech · barge-in
Session-level function calling
Postgres
Application lookup · session state
Transcript & consent record
shared with loan origination system
routing
Auto-handled call
status · basic info → call completes
Escalation — adverse-action-adjacent case
live warm handoff
Human loan officer console
transcript + data preloaded

Tying the numbers back to the decisions

Both published numbers are downstream of the decisions above, but bounded by one of them in particular:

  • −35% call center load is the outcome of §1 through §4 together, but the ceiling on it is set by §4: only calls that don’t touch an actual credit decision can be fully automated, so the automation rate is bounded by how much of the call volume is genuinely first-line versus adverse-action-adjacent. That figure sits inside the range Forrester’s Google-Cloud-commissioned study reported for Google Cloud CCAI (20-35% call deflection) and close to PolyAI’s published UniCredit case (27-30% of calls automated) — near the top of a credible, vendor-published range rather than an outlier.
  • 12,000 calls/week automated and 2m 10s average handle time are downstream of §1 and §3 specifically: native speech-to-speech keeps per-call latency low enough that automated calls complete in something close to a natural conversational pace, and narrowly-scoped, fast function calls keep the "let me check that" moments from stretching the call. These two figures are project-specific operational metrics rather than numbers with an external industry benchmark to check them against — that’s expected.

We’re not aware of a published third-party figure for "average handle time for an automated loan-status call" to compare against, and we’re not implying one exists — these two numbers stand on their own as project-specific results.

Where this architecture stops being the right one

Worth stating plainly, because no architecture is permanent:

Call volume grows to where Postgres’s read pattern for live session lookups becomes a bottleneckRevisit the data layer in §5, likely toward a caching layer in front of Postgres rather than replacing it as the system of record
A specific function call’s latency exceeds the conversational latency budget from §3That specific integration needs pre-fetching or caching, not a wholesale rethink of the function-calling pattern
Regulatory guidance on AI-assisted credit decisions tightens furtherThe warm-handoff trigger in §4 needs to move earlier and more conservatively — a threshold to tune, not an architecture to rebuild
Twilio’s pricing or reliability profile changes materially at the client’s growing call volumeRevisit §2; the bidirectional-streaming requirement from §1 would still need to be satisfied by whatever replaces it
The client needs multi-language supportThis build didn’t evaluate the Realtime API’s language and voice coverage against that requirement, and it would need its own review

None of these are failures of the original decision — they’re the conditions under which the same reasoning process, run again, would produce a different answer.

Sources & confidence

GPT-4o native audio: as little as 232ms, average 320ms, comparable to human conversational response time
OpenAI, "Hello GPT-4o," May 13, 2024High — primary source, official announcement
Prior cascaded ChatGPT voice mode: 2.8s (GPT-3.5) / 5.4s (GPT-4) average latency
OpenAI, same announcementHigh as a primary figure, but it’s OpenAI’s own comparison of their prior product, not an independent benchmark
Realtime API (public beta, Oct 1, 2024) inherits GPT-4o’s native speech-to-speech architecture and barge-in handling
OpenAI, "Introducing the Realtime API"Medium — official announcement, but we couldn’t independently confirm a Realtime-API-specific latency figure distinct from the May 2024 GPT-4o numbers
Well-optimized cascaded voice pipelines: ~600ms-1.7s end-to-end depending on stack
Twilio and Telnyx engineering blogs on voice AI latencyMedium — vendor engineering blogs, not a controlled independent study, but directionally consistent across both sources
Human conversational turn-taking: ~208ms average cross-linguistic response offset (10 languages)
Stivers et al., PNAS 106(26), 2009High as a citation of the study itself — but applying this as an AI voice-agent latency target is our own engineering inference, not a conclusion of the paper
Twilio Media Streams: `<Connect><Stream>` is bidirectional audio over WebSocket
Twilio official documentationHigh — primary source
Twilio SLA: 99.95% standard uptime, 99.99% on Enterprise/Admin Edition
Twilio, official Service Level AgreementHigh — primary, contractual source
Twilio CPaaS market share: ~23.2% (2023) growing to ~25.9% (Q3 2024)
Metrigy, CPaaS Market Share & Forecast 3Q24Medium — independent analyst estimate, not Twilio’s own marketing, but a third-party model rather than audited fact
Twilio US outbound voice pricing: ~$0.013/minute (base voice, not AI-specific)
Twilio official pricing pageHigh for the base figure — AI-specific blended pricing came from secondary aggregators we couldn’t verify directly and should be re-checked at build time
ECOA/Regulation B (12 CFR § 1002.9): adverse action notices require specific, accurate principal reasons, including for algorithm-driven decisions
CFPB Circular 2022-03, September 2022High — primary source, official regulator guidance
TCPA requires disclosed consent to record; 12 U.S. states apply all-party/two-party consent rules
Secondary legal-compliance aggregatorsMedium — not sourced directly from statute text; flagged for case-by-case legal confirmation before relying on it for a specific client’s calling footprint
Google Cloud CCAI: 20-35% call deflection (Forrester Consulting study commissioned by Google Cloud, Aug 2020)
Forrester / Google CloudMedium — vendor-commissioned study, best-case framing, not an independent audit
PolyAI / UniCredit case: ~27-30% of calls automated, +14% NPS
PolyAI published case studyMedium — real named client case study, but a vendor’s own best-case example, not a typical average
OpenAI Realtime API: function calls can trigger mid-response, with the result incorporated into the continuing audio reply
OpenAI Realtime API official documentationHigh — primary source, documented behavior

We’re publishing this confidence table on purpose. A client is better served by knowing which numbers came straight from a primary source and which ones we’d re-verify — especially the legal and compliance claims here, which deserve a lawyer’s sign-off for a specific client’s footprint, not just our own reading of secondary summaries — than by a document that reads clean because the uncertainty got quietly edited out.