Adding an AI Voice Bot Without Rewriting Your IVR — and Handing Off to Humans Cleanly
The requirement behind half the AI-telephony pitches: “let an LLM answer the simple calls, and hand complex ones to a human — without throwing away the phone system we already run.” Most stacks make you choose between a bolt-on bot platform (its own SIP island, its own CDRs) and a rewrite. RustPBX treats the bot as a first-class resident instead. Here’s the architecture, and specifically how the human handoff stays clean.
Three Integration Depths
1. Step IVR — your code decides each step. The PBX POSTs every call step (prompt played, digit collected, voice recognized) to your provider API; you answer with the next action. Full dynamic logic, and the platform still owns media, recording, and CDRs. A resume event continues a suspended flow after a transfer — no menu replay.
2. Realtime bridge — the LLM talks directly. The realtime AI-voice bridge attaches an LLM agent to the call as a peer: caller audio in, synthesized voice out, your prompt and tools in control. Co-existing with the CC addon means the same call can move from bot to queue without hanging up.
3. Transcription — the human’s assistant. Per-call transcription plans (provider, language, auto-start per call) feed agent-assist UIs and post-call summaries without touching the audio path.
The Handoff, Done Properly
The failure mode of every bot deployment is the handoff. RustPBX’s pattern:
- The bot decides to escalate — your logic (or the caller shouting “agent” three times) triggers a transfer.
- Application state survives — an inbound REFER carries the application handoff to the new leg; a suspended Step flow receives an explicit resume with
resume_from_step_id, so context isn’t lost and menus don’t replay. - The queue gets the call, the trace gets the history — the CDR’s leg timeline marks the bot phase and the human phase on one session id; the unified
call_errorcatalog covers bot-side failures too. - CSAT behaves — transferred-by-bot calls skip the survey by default, same as human transfers.
Why This Beats a Bolt-On Bot
One platform means: one set of recordings (bot and human segments linked), one CDR, one screen-pop flow (the queue dispatch carries Call-Info/UUI headers to the human agent regardless of what happened before), one scaling story (the media plane is the same Rust core), and one place to look when the 2 AM caller is angry.
The bolt-on pattern — bot platform dials the PBX as a “caller” — breaks all four: two CDRs, two recordings, a re-invitation handshake for the handoff, and a screen-pop that starts from zero.
Getting Started
- Enable the
transcriptaddon and set per-call transcription plans for the assist layer - Point a Step IVR at your AI service for intent collection (the protocol guide has a working Python provider)
- Wire the fallback:
[proxy.ivr_fallback]routes to a local IVR when your AI service is down — callers never hear the failure - Use the realtime bridge when the bot must hold a full conversation; hand off with REFER into the skill group
What the Handoff Looks Like to the Caller
One continuous phone call. The bot says “Let me connect you with a specialist,” the caller hears hold music with queue_hold_music, the skill group’s screen-pop fires on the agent’s desk with the full X-<key> context the bot collected (account number, issue summary — your Step flow wrote it into user data), and the agent’s wrap-up notes attach to the same call record. No “can you repeat your account number” moment — which is the entire point.

The platform records, rates, traces, and escalates — the bot is just another resident.