Call Flow Builder & AI Actions System Documentation
The Call Flow Builder and AI Actions (in the agent editor's "Tools & Transfer" section) configure how calls are handled before and during AI conversation. The Call Flow Builder shows a visual graph for IVR flows; AI Actions shows a categorized list of agent capabilities.
How They Relate
| UI View | Where | What it shows |
|---|---|---|
| IVR Flow Editor | /agents/new/ivr-flow or /agents/:id/ivr-flow | Visual canvas for IVR flow agents (agentType: 'ivr_flow') |
| AI Actions | Agent Editor → Tools & Transfer tab | Categorized list: On Call Connected, Flow Actions, AI Transfers |
Note: The standalone Call Flow Builder page at /call-flow-builder and the embedded "Call Flow Builder" step in the voice agent editor have been removed. IVR flows are now configured exclusively through the IVR Flow Editor page for dedicated ivr_flow agent types.
Architecture (Current)
Entry Point: Interactive Start
The only entry point. Always present, cannot be removed. Has two routing modes:
| Routing Mode | How it works |
|---|---|
| DTMF | Plays TTS prompt → waits for keypad press → routes by digit binding |
| Voice Intent | Plays TTS prompt → listens to speech → AI classifies intent → routes |
Pre-Action: Auto DTMF (On Call Connected)
Optional. Fires BEFORE the entry point. Sends a DTMF sequence automatically when the call connects. Used to navigate external IVR systems.
Configured via the Auto DTMF node on the canvas (drag from Node Palette → "On Call Connected" section).
AI Transfer Actions (During Conversation)
LLM-triggered transfers that happen DURING the AI conversation (not during IVR):
| Type | Trigger | What happens |
|---|---|---|
| Transfer to Agent (Cold AI) | LLM decides based on description | SIP REFER to target agent/number |
| Transfer to Number (Cold AI) | LLM decides based on description | SIP REFER to phone number |
| End Conversation | LLM decides conversation is complete | Hangs up the call |
Node Types
Canvas Nodes (Call Flow Builder)
| Node | Category | Terminal? | Description |
|---|---|---|---|
interactiveStart | Entry Point | No (source) | The IVR entry point TTS prompt + routing |
autoDtmfStart | On Call Connected | No (source) | Pre-action DTMF sequence |
menuPrompt | Flow Actions | No (source+target) | Sub-menu for nested IVR trees |
connectAgent | Flow Actions | Yes (target only) | Handoff to another AI agent |
transfer (direct) | Flow Actions | Yes (target only) | SIP transfer to phone number |
transfer (cold_ai) | AI Transfers | Standalone | LLM-triggered cold transfer |
transfer (warm_ai) | AI Transfers | Standalone | LLM-triggered warm transfer with DTMF nav |
collectDigits | Flow Actions | No (source+target) | Collect N-digit input from caller |
repeat | Flow Actions | No (source+target) | Loop back to previous menu |
Terminal Nodes
connectAgent and transfer (direct) are terminal they cannot have outgoing connections. Once a call reaches these nodes, control is handed off:
connectAgent→ new agent takes over (its prompt, LLM, tools, first message)transfer→ call leaves the system entirely (SIP transfer)
The UI enforces this: no source handle on these nodes, isValidConnection blocks outgoing edges.
Data Model (MongoDB Agent Document)
{
agent_id: "r7_vZ96yewMc",
// IVR Configuration (per direction)
inboundIvrConfig: {
enabled: true,
mode: "interactive", // Always "interactive" for new configs
routingMode: "dtmf", // "dtmf" | "voice_intent"
routingGoal: null, // For voice_intent mode
greetingSource: "menu_prompt",
onCallConnected: { // Auto DTMF pre-action
enabled: true,
dtmfSequence: [
{ type: "wait", value: "2000" },
{ type: "digit", value: "1" }
]
},
rootNode: { // Interactive Start node
nodeId: "start",
prompt: "Press 1 for Sales, Press 2 for Support",
bindings: {
"1": { type: "transfer", transferTarget: "+919876543210" },
"2": { type: "connect_agent", targetAgentId: "support-agent-id" }
},
timeoutSeconds: 10,
timeoutAction: "connect_agent"
},
nodes: [] // Sub-menu nodes (flat list)
},
outboundIvrConfig: { /* same shape */ },
// AI Transfer Actions (LLM-triggered during conversation)
ivrActions: [
{
actionId: "act_123",
name: "Transfer to Claims",
description: "Transfer when caller asks about insurance claims",
transferTarget: "+14155550100",
transferType: "cold", // "cold" | "warm"
dtmfSequence: [] // Only for warm transfers
}
]
}API Endpoints
IVR Config (per direction)
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/agents/:agentId/inbound-ivr-config | Get inbound IVR config |
| PUT | /api/agents/:agentId/inbound-ivr-config | Save inbound IVR config |
| DELETE | /api/agents/:agentId/inbound-ivr-config | Delete inbound IVR config |
| GET | /api/agents/:agentId/outbound-ivr-config | Get outbound IVR config |
| PUT | /api/agents/:agentId/outbound-ivr-config | Save outbound IVR config |
| DELETE | /api/agents/:agentId/outbound-ivr-config | Delete outbound IVR config |
IVR Actions (AI Transfers)
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/agents/:agentId/ivr-actions | Get AI transfer actions |
| PUT | /api/agents/:agentId/ivr-actions | Save AI transfer actions |
Internal (Agent Server)
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /internal/agent-inbound-ivr-config/:agentId | Fallback config fetch for inbound calls |
Validation Rules
Backend Validation (inbound-ivr.controller.ts)
| Rule | Description |
|---|---|
| Root node required | rootNode must exist when enabled |
| Prompt optional for voice_intent | TTS prompt not required when routingMode === "voice_intent" |
| Bindings required for DTMF | At least one digit binding when routingMode === "dtmf" |
| Transfer target format | Must be E.164 phone number or SIP URI |
| Max tree depth | 3 levels of sub-menus |
Frontend Validation (validator.ts)
| Rule | Description |
|---|---|
| Menu nodes need 1-12 bindings | Outgoing edges count |
| Prompt ≤ 500 chars | For menu nodes |
| No disconnected nodes | Every non-entry node needs incoming edge |
| Terminal nodes no outgoing | connectAgent and direct transfer can't have children |
| Tree depth ≤ 3 | From root menu node |
TTS Prompt vs First Message
| Scenario | What plays |
|---|---|
| TTS prompt configured + IVR enabled | TTS prompt plays as IVR greeting |
| No TTS prompt + voice_intent mode | Agent's first message plays as greeting |
| IVR disabled | Agent's first message plays normally |
| After IVR with agent handoff | Connected agent's first message plays |
| After IVR without handoff | Nothing extra (IVR prompt was the greeting) |
Priority: TTS Prompt > First Message. If TTS prompt is set, it always takes priority.
Frontend File Reference
| File | Role |
|---|---|
src/pages/customer/IvrFlowEditorPage.tsx | IVR Flow Editor page canvas, state, save/load |
src/components/ivr-builder/FlowCanvas.tsx | React Flow canvas, drag-drop, terminal node enforcement |
src/components/ivr-builder/ConfigPanel.tsx | Right-side node config forms |
src/components/ivr-builder/FlowTestSimulator.tsx | Text-based flow walkthrough simulator |
src/components/ivr-builder/NodePalette.tsx | Left-side draggable nodes |
src/components/ivr-builder/nodes/*.tsx | Custom node components |
src/lib/ivr-builder/serializer.ts | React Flow ↔ Backend config conversion |
src/lib/ivr-builder/validator.ts | Client-side flow validation |
src/lib/ivr-builder/templates.ts | Pre-built flow templates |
src/lib/ivr-builder/types.ts | TypeScript type definitions |
src/components/features/call-flow-actions/actionMeta.ts | AI Actions categories and metadata |
src/stores/callFlowStore.ts | Zustand store for call flow state |
Removed files:
IvrBuilderPage.tsxStandalone IVR builder (replaced by IvrFlowEditorPage)CallFlowBuilderPage.tsxWrapper for standalone page (removed)CallFlowStep.tsxEmbedded builder in agent wizard (removed)
Agent Server File Reference
| File | Role |
|---|---|
Agent/src/core/ivr_menu_engine.py | IVR tree execution TTS, DTMF, voice intent, actions |
Agent/src/core/auto_dtmf_runner.py | Automated DTMF sequence sender |
Agent/src/core/first_message_resolver.py | First message priority resolution |
Agent/src/core/ivr_tools.py | Cold/warm transfer + DTMF utilities |
Agent/src/handlers/call_handler.py | Outbound call lifecycle |
Agent/src/handlers/inbound_call_handler.py | Inbound call lifecycle |
Agent/src/schemas/metadata_schema.py | Pydantic models for IVR config |
Agent/agent.py | Session setup, background audio |
See Agent/docs/CALL-FLOW-BUILDER.md for detailed Agent Server implementation guide.
Changelog
| Date | Change |
|---|---|
| 2026-05-11 | Consolidated to 1 entry point (Interactive Start) + 1 pre-action (Auto DTMF) |
| 2026-05-11 | Terminal node enforcement (connect_agent/transfer can't have children) |
| 2026-05-11 | Removed On Call Connected toggle panel (use canvas node instead) |
| 2026-05-11 | Removed "IVR menu prompt" messaging (TTS prompt > first message) |
| 2026-05-11 | Agent audio muting during DTMF IVR execution |
| 2026-05-11 | Background audio pausing during IVR |
| 2026-05-11 | Confirmation messages on valid DTMF press |
| 2026-05-11 | Connected agent's first message plays after handoff |
| 2026-05-06 | Voice intent routing mode |
| 2026-05-06 | AI Transfer nodes on canvas |
| 2026-05-06 | Template system |
| 2026-05-05 | Agent handoff via targetAgentId |
| 2026-05-05 | Outbound IVR support |
| 2026-04-28 | Initial IVR menu engine |
| 2026-04-20 | IVR call transfer tools |