Skip to main content
The Orbit IVR Builder is a visual canvas where you drag, connect, and configure every turn of an inbound call — no JSON graph to hand-author. This tutorial takes you through one end-to-end loop: blank canvas, compose a working billing-lookup flow, validate it in the simulator, publish it, and point a DID at it. By the end you will have a flow live on a number, and you will know the lifecycle well enough to adapt it to your own routing trees. If you are comparing the canvas to writing the IVR DSL in code, read Pick your IVR surface: canvas or DSL first — it puts the two surfaces side-by-side and walks the export/import round-trip. This tutorial stays on the canvas.

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.
The palette holds every building block. Hover any entry to see its one-line description. For the full node catalog and the corresponding DSL payloads, see Build and ship your first IVR flow. We will use only the nodes the billing-lookup example needs.

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:
The TTS text is what the caller hears. You can also supply an 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
The gather node is a DTMF collector — it plays the prompt, waits for digits, and branches on the outcome. It exposes three handles: 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’s gathered 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.
The Data Dip returns a match or no-match. It exposes two handles: 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.
A Data Dip queries the live CDP at call time. A dip that times out or returns an error falls through the not_matched handle — design the timeout path so a transient CDP blip does not strand the caller. For the billing flow, wire not_matched to the billing queue as a safe fallback rather than hanging up.

3.5 Route to the billing agent queue

Drag a Queue node from the palette and wire it to the Data Dip’s matched 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 the timeout 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.
The node exposes two handles: 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’s matched 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 language header on the inbound SIP invite.
The Dial node dials the agent’s extension or external number. An answered call bridges caller and agent; an unanswered call falls through the no_answer handle, which you can wire to the queue or voicemail.
A Dial node whose destination is a raw SIP URI is rejected by the validator with transfer_external_sip. Outbound calls terminate on the Devotel network — route by E.164 number or on-net extension.

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 the accepted 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:
Auto-layout (toolbar button) will redraw this as a readable tree. Save, and then validate.

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.
Fix every issue the panel reports — Publish is blocked while issues persist. Click any issue line to focus the canvas on the offending node or edge.

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_exceeded and 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.
If the simulation rejects with 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), choose ivr 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 include account_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 the declined 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