1. Open the IVR Builder
In the dashboard, go to Voice → IVR Builder. The page loads an empty canvas with a toolbar across the top and a node palette on the left. From the toolbar, the main actions you will use across this tutorial:- Save writes the current canvas to the draft. Nothing a caller can reach.
- Validate runs the structural checks described in section 4. Fix issues until it comes back clean.
- Publish promotes the draft to the live snapshot that your DIDs actually answer.
- Simulate opens the in-product simulator so you can walk a scripted caller through the flow.
- History lists every published version, with a one-click revert.
2. Set up: create and name your first flow
Click New flow on the toolbar, then Save. Choose a name —Billing lookup — and save. The canvas is blank and the flow is a draft: you have a { nodes: [], edges: [] } skeleton the validator will reject until you add a start node.
You can also hydrate a flow from one of the JSON-LD templates in the template catalog — open the Load menu and pick a template, and the canvas populates with a pre-built graph you can edit. For this tutorial we will build from scratch so you know every node and edge intimately.
Every save you make from now until you publish only changes the draft. The toolbar shows an Unpublished changes chip whenever the draft differs from the live snapshot.
3. Compose the billing-lookup flow
We will build a flow that greets the caller, gathers their account number by DTMF, checks it against a data dip, routes valid accounts to a billing agent queue, offers an SMS callback on timeout or no-match, and hangs up. Here is the composite view of what you will end up with — twelve nodes, each one dragged from the palette and wired on the canvas.3.1 Drop the entry point
Drag Start from the palette onto the canvas. Every flow needs exactly one entry — the validator will flag a flow with zero or two start nodes. The Start node has one outgoing handle; every other node you drop will connect from it or from a downstream node.3.2 Greeting — Play Audio
Drag Play Audio from the palette and drop it below Start. Drag a wire from Start’s outgoing handle onto the Play Audio node. Click the node to open the right-hand config panel and set the TTS text:audioUrl — a hosted WAV or MP3 — instead of text, or use both together (TTS as a fallback when the URL is unreachable).
3.3 Gather the account number — Start Gather
Drag Start Gather from the palette and wire it from the Play Audio node. Click the node and configure:- Prompt:
Enter your 6-digit account number, followed by the hash key. - Max digits:
6 - Finish on key:
# - Timeout (seconds):
5 - Number of attempts:
3
Wire the
gathered handle to the next node (the Data Dip, once you add it). The timeout and nomatch handles will go to the Offer SMS Callback node later.
3.4 Verify the account — Data Dip
A Data Dip node queries your CDP or CRM in real time during the call. Drag one from the palette, wire it from the gather node’sgathered handle. In the config panel, set:
- Dip type:
Lookup - Lookup key:
account_number - Value:
{{steps.gather_account.digits}}— a template variable that resolves to the digits the caller just entered. - Segment: the CDP segment that contains your account records.
matched and not_matched. Wire matched to the Billing Queue node (next). Wire not_matched to a Play Audio node that says "We could not find that account number. Please try again or wait for an agent." and then connect it back to the gather node for a retry, or to the Offer SMS Callback.
For a production billing flow you would also use the Data Dip to fetch the caller’s current balance, open cases, and account tier, then branch the routing — VIP accounts to a priority queue, standard accounts to the main queue. The dip runs before the queue node so the data is available for screen-pop when an agent answers.
3.5 Route to the billing agent queue
Drag a Queue node from the palette and wire it to the Data Dip’smatched handle. In the config panel, pick the billing ACD queue — the dropdown lists every queue you have configured on the workspace. The caller hears hold music (your configured queue audio) until an agent picks up.
The Queue node is a terminal for the caller — the call stays in the queue until an agent answers, the caller hangs up, or the queue overflows (at which point the overflow handle activates — wire it to voicemail or Offer SMS Callback).
3.6 SMS deflection — Offer SMS Callback
When the caller times out at the gather node or the account lookup fails three times, offer an SMS callback instead of keeping them on hold. Drag an Offer SMS Callback node from the palette and wire thetimeout and nomatch handles from the Start Gather node to it.
In the config panel, set:
- Message template:
"We could not verify your account over the phone. Tap here to continue over SMS: {{callback_url}}"— the{{callback_url}}placeholder is resolved at send time to a deep link that opens a two-way SMS conversation in the customer’s messaging app. - Sender number: pick a registered SMS sender.
accepted and declined. Wire accepted to a Hangup node (the SMS session takes over). Wire declined to the billing queue as a last-resort human handoff, or to a voicemail node.
The SMS callback session is a two-way thread — the customer can text back with their account details, and the agent responds from the dashboard inbox. See Queue appointment booking and Callbacks scheduling for the full callback lifecycle.
3.7 Dial node with geo-routing
If your billing team is split across regions, a Dial node can route by geography. Drag a Dial node, wire it from the Data Dip’smatched handle (as an alternative to the Queue). In the config panel, enable Geo-routing and select a routing policy:
- Round-robin across all billing agents.
- Closest-to-caller based on the caller’s ANI area code.
- Language-match based on the caller’s detected locale from the
languageheader on the inbound SIP invite.
no_answer handle, which you can wire to the queue or voicemail.
3.8 Hangup and return-to-IVR
Every terminal path in a flow must end at a Hangup node, a Queue, a Transfer, a Voicemail, a Ring Group, or a Dial by Name node. Add a Hangup node for the SMS-accepted path and for any dead-end fallback. Wire theaccepted handle of Offer SMS Callback to Hangup.
A flow can also loop back into itself — after a Data Dip fails and the caller hears the retry prompt, wire back to the Start Gather node. The validator checks that every re-entry has a route to a terminal within the cycle so the caller is never trapped in an infinite loop. The canvas highlights cycle edges in red when the backend returns a cycle_without_exit issue.
3.9 Full composite
At this point your canvas should have these nodes wired:4. Tag nodes with intents, DTMF, and speech recognition
A Start Gather node is DTMF-only: it listens for keypad digits through the Jambonz gather verb. DTMF is the right tool when the answer space is small and constrained — account numbers, PINs, a “press 1 or 2” menu. It works on every phone, from a landline to a VoIP softphone, with no model round-trip and no confidence threshold to manage. When you want the caller to speak freely — “I need to check my bill” or “someone overcharged me” — replace the Start Gather with a Speech Input node. The speech node transcribes the caller’s utterance (via Deepgram by default, configurable per node to Google, Microsoft, AWS, or the cluster default), runs it against per-node speech intents or per-tenant intent buckets, and branches by matched intent name.
The Speech Input node’s right-hand panel becomes a speech-grammar editor: add intents by name, list phrases (one per line), and optionally assign a DTMF digit per intent for keypad fallback. The
dtmfFallbackEnabled toggle turns keypad fallback on or off — with fallback off, the gather accepts no digits and the menu is speech-only. For the full NLU routing pipeline — modelling intent buckets, testing utterances for confidence, and wiring the fallback edge — see Build an IVR flow with NLU intent routing.
If your flow uses both DTMF and speech, a common pattern is a Menu node for the first split (“press 1 for billing, 2 for support”) and a Speech Input node on each branch for the sub-route (“tell us what you need”).
5. Validate and simulate before you publish
5.1 Validate the graph
Click Validate on the toolbar. The validator checks:- Exactly one entry node.
- Every node reachable from the entry.
- Every branch reaches a terminal (Hangup, Queue, Transfer, Voicemail, Ring Group, Dial by Name, or Offer Callback accepted).
- No unreachable islands.
- No cycles without a route to a terminal.
- No duplicate or malformed DTMF keys.
- No raw-SIP-URI transfer targets.
5.2 Simulate a scripted caller
Click Simulate on the toolbar. Add turns — each turn with digits, speech, or an intent — and the walker steps through the graph, lighting up the path a real call would take and reporting the reached nodes, the handle taken at each branch, the emitted verb categories, and the termination reason. For the billing flow, exercise three paths: Happy path — a known account number:
Timeout then SMS accept:
Timeout then SMS decline → queue fallback:
Run all three before you publish. The same three walks can be scripted through the API —
POST /api/v1/voice/ivr-flows/:id/simulate with a turns array of { digits, speech, intent } objects — which lets you wire the simulation into a CI check so no regression reaches your DID. The API walker uses the same edge-precedence rules as the in-product dialog.
5.3 Simulation limits
- Max turns: 50 per simulation run. A walk that exceeds this returns
turn_limit_exceededand stops. - Max steps: 200 graph transitions per run. Deeply nested flows with many retry loops may hit this; simplify the retry count or flatten the tree.
- Source: by default the simulator walks the draft. Toggle the Source dropdown to walk the published snapshot instead, or paste a raw definition to simulate a candidate without saving it first.
422 IVR_FLOW_GRAPH_INVALID, the graph failed validation — solve the validator issues first, then simulate again.
6. Publish and attach to a DID
6.1 Publish
When the validator is clean and all three simulation paths pass, click Publish. The draft is promoted to the live snapshot. The toolbar Unpublished changes chip disappears, and the published snapshot is what the runtime now uses for inbound calls. Every publish appends a row to the version history. Open History to see each published snapshot with its publish time and node count. To revert, pick an earlier version and confirm — the revert restores that snapshot and publishes it as the new head, so the DID keeps answering on the reverted definition without a manual re-edit.6.2 Attach to a number
Open the number you want to route (Numbers → your number → Routing), chooseivr as the inbound type, and select the billing-lookup flow from the dropdown. Save. Inbound calls to that number will now walk the flow’s published snapshot.
Alternatively, from the builder toolbar, click Assign to Number and pick an owned number — the same routing row is written.
The attachment is immediate. To verify end-to-end without phoning in, re-run the simulator against the published source.
7. After the call — ACW, dispositions, and callback hand-off
When the billing call ends (the agent hangs up), the agent enters After-Call Work (ACW) — the post-call state where they complete wrap-up tasks before the next call rings. ACW is configured on the queue, not the flow, but here are the touchpoints you control from the builder:Disposition tags
Every call can be tagged with a disposition — a label that records the call outcome. Common billing dispositions includeaccount_verified, balance_inquiry, dispute_filed, payment_taken, callback_scheduled. Tagging is done by the agent in the dashboard; the analytics surface (queue analytics, agent scorecards, call logs) then breaks down volume and duration by disposition. See Call disposition tags and Wrap-up codes for the configuration surface.
Callback queue hand-off
If the Offer SMS Callback node fires and the caller accepts, the SMS session is a separate interaction thread — but it can still lead back to a voice callback. The Callbacks scheduling page covers the full lifecycle: a caller who accepted SMS can request a voice callback when an agent is free, and the callback is enqueued at the position the original call would have held.Queue appointment booking
If the caller needs a scheduled callback rather than an immediate one (for example, the billing queue is closed outside business hours), direct thedeclined path from Offer SMS Callback to a flow that prompts for a preferred time slot. The Queue appointment booking guide covers the scheduling endpoint and the calendar integration.
8. Editor behaviors to know
A few canvas-specific behaviors that change how you work day-to-day:- Snap-to-tree. Dropped nodes land at the current insert point. Auto-layout re-sorts the tree into a readable vertical flow; Fit view re-frames a long canvas.
- Hover hints. Palette entries carry a one-line description; toolbar buttons explain Validate, Auto-layout, Fit view, History, and the unsaved-changes indicator.
- Selection safety. Delete/Backspace removes the selected node, but the keys are suspended while autosave is in an error state so a failed save never costs you a node.
- Preview. The Preview button opens a read-only rendering of the flow for sharing with a teammate.
9. Limits
See also
- Pick your IVR surface: canvas or DSL — side-by-side comparison and full round-trip.
- Design IVR flows on the visual builder — the canvas reference page (full palette, config panel, and toolbar).
- Build and ship your first IVR flow — the full DSL/API contract the builder writes to.
- Build an IVR flow with NLU intent routing — intent modelling, utterance testing, and multilingual routing.
- IVR unattended PCI capture — DTMF card capture in PCI mode.
- Callbacks scheduling — the callback lifecycle after SMS deflection.
- Queue appointment booking — scheduled callbacks and calendar integration.
- Call disposition tags — post-call outcome tagging and analytics.
- Wrap-up codes — ACW codes and queue configuration.
- Flow versions and publishing — draft → publish lifecycle and rollback.