Your IVR is the first thing a customer hears when they call. If it's three seconds of silence followed by a robot voice saying "please hold," followed by four minutes of distorted hold music, you have a 40% abandonment rate and you earned it.
VICIdial's IVR system is built around a feature called Call Menus -- the admin-level configuration that controls what callers hear, which DTMF key presses route where, and how calls flow from a DID to an agent's headset. It's flexible, handles everything from 3-option auto-attendants to 6-level-deep menu trees with time-of-day routing and language selection. But it's unintuitive. The admin GUI gives you a blank form with 20+ fields, no tooltips, and documentation that assumes you already know how IVR routing works.
The Three-Link Chain
Every inbound call follows three links:
- DID receives the call. Your SIP carrier delivers the call to Asterisk. The dialed number gets matched against VICIdial's DID table.
- DID routes to a Call Menu (IVR). The caller hears your prompt and presses a key.
- Call Menu routes to an Inbound Group (queue). The caller enters the queue, hears hold music, and waits for an agent.
DIDs, Call Menus, and Inbound Groups are three separate objects in VICIdial. They connect like a chain. Get one link wrong and calls dead-end or route to the wrong place.
The DID: Where Everything Starts
Navigate to Admin > Inbound DID. Each DID entry tells VICIdial what to do when a call arrives for that number.
| Field | Purpose | Example |
|---|---|---|
| DID Extension | The phone number as delivered by carrier | 15551234567 |
| DID Route | Where to send the call | CALLMENU, IN_GROUP, or AGENT |
| Call Menu | Which call menu (if CALLMENU) | MAIN_IVR |
| In-Group | Which inbound group (if IN_GROUP) | SALES_QUEUE |
The DID Extension must match exactly what your carrier sends. This trips up more people than any other IVR issue. Some carriers send full E.164 with country code (15551234567). Others strip it (5551234567). Some prepend a "1". Run asterisk -rx "sip set debug on", place a test call, and look at the To header in the INVITE. That exact string goes in the DID Extension field.
Building a Basic Auto-Attendant
Navigate to Admin > Call Menus > Add A New Call Menu.
| Field | Value | Why |
|---|---|---|
| Menu ID | MAIN_IVR | Unique ID. Alphanumeric, no spaces. |
| Menu Name | Main Auto-Attendant | Descriptive name for admin. |
| Menu Prompt | main-greeting | Asterisk audio file name (no extension). |
| Menu Timeout | 10 | Seconds to wait for DTMF input. |
| Menu Timeout Prompt | sorry-try-again | What to play if no input. |
| Menu Invalid Prompt | invalid-option | What to play for unrecognized key. |
| Menu Repeat | 3 | How many replays before timeout action. |
Then add DTMF options:
| Key | Destination | Route Type |
|---|---|---|
| 1 | SALES_QUEUE | IN_GROUP |
| 2 | SUPPORT_QUEUE | IN_GROUP |
| 3 | BILLING_QUEUE | IN_GROUP |
| 0 | OPERATOR_QUEUE | IN_GROUP |
| t | SALES_QUEUE | IN_GROUP |
| i | MAIN_IVR | CALLMENU |
The t option catches timeouts -- if the caller doesn't press anything after all repeats, send them to the default queue. Never leave callers in silence. The i option catches invalid input -- replay the menu.
Link the DID: Admin > Inbound DID > set DID Route to CALLMENU > select MAIN_IVR.
Call Menu Option Types
Each DTMF option can route to:
- IN_GROUP -- Agent queue. Most common for leaf-level menu options.
- CALLMENU -- Another Call Menu. How you build multi-level IVRs.
- DID -- Another DID entry. Useful for forwarding to external numbers.
- EXTENSION -- Specific Asterisk extension. Direct-to-agent, voicemail, or custom dialplan.
- VOICEMAIL -- Directly to a voicemail box.
- AGI -- Execute an Asterisk AGI script for custom routing logic.
- HANGUP -- Disconnect. Use sparingly.
Menu Prompts: Single vs Multi-File
Single prompt: main-greeting -- plays main-greeting.wav from /var/lib/asterisk/sounds/.
Multi-file sequence: welcome-to-company|press-one-for-sales|press-two-for-support -- plays each file in order. Useful because you can swap individual segments without re-recording the whole greeting.
Insert silence between segments: main-greeting|silence/2|menu-options -- the silence/2 adds 2 seconds of breathing room. Small touch, sounds way more professional.
Audio Quality: The Thing That Makes or Breaks Your IVR
Bad audio is the number one thing that makes an IVR sound amateur. A tinny recording with background noise tells callers they've reached a company that doesn't care about details.
Format Requirements
- WAV (PCM), 8000 Hz, 16-bit, mono, linear PCM
- This is the native format for G.711 telephony
- If you upload 44.1kHz stereo, Asterisk transcodes on every playback, wasting CPU and adding artifacts
Convert with SOX
sox original-recording.wav -r 8000 -c 1 -b 16 main-greeting.wav
Batch conversion:
for f in /path/to/originals/*.wav; do
sox "$f" -r 8000 -c 1 -b 16 "/var/lib/asterisk/sounds/$(basename $f)"
done
Upload to VICIdial
Via the admin: Admin > Audio Store. Upload WAV files, VICIdial copies them to the correct directory. File name minus extension becomes the prompt name.
Via SCP:
scp main-greeting.wav root@vicidial-server:/var/lib/asterisk/sounds/
chown asterisk:asterisk /var/lib/asterisk/sounds/main-greeting.wav
chmod 644 /var/lib/asterisk/sounds/main-greeting.wav
Recording Tips
Record all prompts in one session with the same voice talent. Mismatched voices across menu levels sound terrible. Record each option as a separate file. Include 0.5 seconds of clean silence at the start and end. Normalize volume across all files.
Time-Based Routing: Business Hours, Holidays, After-Hours
A production IVR can't play the same menu 24/7. During business hours, "Press 1 for Sales" routes to a live queue. After hours, it should route to voicemail.
Setting Up Call Times
Navigate to Admin > Call Times:
| Field | Value |
|---|---|
| Call Time ID | BUSINESS_HOURS |
| Monday-Friday Start | 0800 |
| Monday-Friday End | 1800 |
| Saturday/Sunday | (blank = closed) |
Add holidays under Call Time Holidays. Each holiday date overrides the daily schedule.
Call Menu Time Check
Set Menu Time Check to BUSINESS_HOURS. Configure separate prompts and options for in-hours and after-hours:
Menu ID: MAIN_IVR
Menu Time Check: BUSINESS_HOURS
--- In-Hours ---
Prompt: business-hours-greeting
Option 1: IN_GROUP > SALES_QUEUE
Option 2: IN_GROUP > SUPPORT_QUEUE
Option 3: IN_GROUP > BILLING_QUEUE
Timeout: IN_GROUP > SALES_QUEUE
--- After-Hours ---
Prompt: after-hours-greeting
Option 1: VOICEMAIL > 1000
Option 2: EXTENSION > hours-info
Option 3: CALLMENU > CALLBACK_MENU
Timeout: VOICEMAIL > 1000
One Call Menu handles both scenarios. No manual switching required.
Time Zone Warning
VICIdial uses the server's system time for Call Time evaluations. If your server is in Eastern but your business operates in Pacific, your "9 AM open" triggers at noon Eastern.
timedatectl set-timezone America/Los_Angeles
Multi-Level IVRs: Don't Go Past Three
Chain Call Menus by setting DTMF options to route type CALLMENU pointing to another menu.
MAIN_IVR (Level 1)
+-- Press 1 > SALES_MENU (Level 2)
| +-- Press 1 > NEW_CUSTOMERS_QUEUE
| +-- Press 2 > EXISTING_ACCOUNTS_QUEUE
| +-- Press 0 > MAIN_IVR (back)
+-- Press 2 > SUPPORT_MENU (Level 2)
| +-- Press 1 > TECH_SUPPORT_QUEUE
| +-- Press 2 > BILLING_SUPPORT_QUEUE
| +-- Press 0 > MAIN_IVR (back)
+-- Press 3 > BILLING_QUEUE (direct, no sub-menu)
+-- Press 0 > OPERATOR_QUEUE
Always include a "return to previous menu" option. Press 0 or Press * to go back. Without it, callers in the wrong sub-menu hang up and call back.
Three levels is the practical maximum. At four levels, callers have been pressing buttons for over a minute before reaching a human. Satisfaction drops sharply after 45 seconds of IVR navigation. If your routing needs four levels, rethink the tree. Collect the critical decision at level 1 (language or department), then route to an inbound group with skill-based routing. Let the agent handle sub-categorization.
Language Selection Pattern
LANGUAGE_SELECT (Level 1)
+-- Press 1 (or timeout) > MAIN_IVR_EN (English menus)
+-- Press 2 > MAIN_IVR_ES (Spanish menus)
Each language gets its own Call Menus with localized prompts and potentially different inbound groups. Timeout defaults to primary language.
The DTMF Gotcha
If callers press keys but nothing happens, it's almost always a DTMF mode mismatch. VICIdial's IVR relies on Asterisk receiving DTMF events. If the carrier sends DTMF in-band (audio tones) but your trunk is set for RFC 2833, Asterisk never sees the key press.
asterisk -rx "sip show peer your-carrier" | grep -i dtmf
Should show dtmfmode=rfc2833 for most modern carriers. If DTMF isn't working, enable verbose logging:
asterisk -rx "core set verbose 5"
Place a test call and press keys. You should see lines like:
DTMF/SIP-xxx-1 '1' received on SIP/trunk-00000001
If you don't see DTMF events, the issue is between the carrier and Asterisk.
Inbound Groups: Where Calls Meet Agents
Each Call Menu option eventually routes to an inbound group -- VICIdial's queue system. Create groups at Admin > Inbound Groups:
| Field | Value | Notes |
|---|---|---|
| Group ID | SALES_QUEUE | Must match what you entered in Call Menu |
| Active | Y | Must be active to receive calls |
| Call Time ID | 24hours or BUSINESS_HOURS | Business hours for this queue |
| After Hours Action | CALLMENU | Route to after-hours menu |
Agents receive calls through closer campaigns. Set Campaign Allow Inbound to Y and list the inbound groups in Closer Campaigns.
Different hold music per queue reinforces branding:
[sales-hold]
mode=files
directory=/var/lib/asterisk/moh/sales
[support-hold]
mode=files
directory=/var/lib/asterisk/moh/support
Inbound Groups: Connecting Agents to Call Menus
Every Call Menu option eventually routes to an inbound group. Understanding how agents connect to these groups is essential.
Agents receive inbound calls through closer campaigns. The campaign needs:
- Campaign Type: Allow inbound (or dedicated CLOSER type)
- Allowed Inbound Groups: Which groups this campaign's agents can receive
- Dial Method: For blended operations, agents take inbound between outbound attempts
Priority determines which agents get calls first:
- Agent rank within the inbound group (set per-agent, 0-9)
- Longest wait time among equal-rank agents
- Queue priority if multiple groups compete for the same agents
Hold Music Per Queue
Different hold music per queue reinforces branding and sets expectations. Create MOH classes in musiconhold.conf and reference them in each inbound group:
[sales-hold]
mode=files
directory=/var/lib/asterisk/moh/sales
sort=alpha
[support-hold]
mode=files
directory=/var/lib/asterisk/moh/support
sort=random
Place 8kHz mono WAV files in the respective directories. A caller waiting in the support queue hearing different music than the sales queue subtly tells them they're in the right place.
Queue Position and Wait Time Announcements
Configure in each inbound group:
| Field | Purpose |
|---|---|
| On Hold Prompt | Audio file or MOH class while waiting |
| Hold Time Announce | "Your call will be answered in approximately 3 minutes" |
| Queue Position Announce | "You are caller number 5 in the queue" |
| Announce Frequency | How often to repeat (seconds) |
These announcements keep callers engaged and reduce abandonment. For high-volume queues, the callback option (CALLERID_CALLBACK) is even better -- callers press a key to leave the queue and get called back when an agent is free, maintaining their position.
Asterisk Dialplan Integration for Advanced IVR
VICIdial's Call Menu system handles 90% of IVR needs through the admin GUI. For advanced scenarios -- database lookups, API-driven routing, dynamic prompts -- you drop into Asterisk's dialplan and AGI.
Custom logic goes in extensions_custom.conf -- never in extensions.conf, which VICIdial overwrites.
After-Hours Voicemail with Custom Greeting
[custom-afterhours-vm]
exten => s,1,Answer()
same => n,Wait(1)
same => n,Playback(afterhours-message)
same => n,Playback(beep)
same => n,VoiceMail(1000@default,u)
same => n,Hangup()
Reference from a Call Menu option by setting route type to EXTENSION and destination to custom-afterhours-vm,s,1.
Dynamic VIP Routing
[custom-vip-check]
exten => s,1,Set(CALLER=${CALLERID(num)})
same => n,AGI(vip-lookup.agi,${CALLER})
same => n,GotoIf($["${VIP}" = "yes"]?vip:regular)
same => n(vip),Goto(default,8300,1)
same => n(regular),Goto(default,8301,1)
This checks the caller against a VIP list via AGI and routes high-value callers to a priority queue, skipping the IVR entirely.
AGI scripts (Perl, PHP, Python) live in /var/lib/asterisk/agi-bin/. Make them executable and owned by the asterisk user:
chmod 755 /var/lib/asterisk/agi-bin/vip-lookup.agi
chown asterisk:asterisk /var/lib/asterisk/agi-bin/vip-lookup.agi
Testing the Complete IVR Flow
Before going live, test everything:
- Call each DID and verify it enters the correct Call Menu
- Test every DTMF option and verify it routes to the correct inbound group
- Test timeout behavior -- let the menu play through all repeats without pressing anything
- Test invalid input -- press a key that isn't defined
- Test after-hours routing -- change the Call Time temporarily or test outside business hours
- Test overflow -- fill the queue and verify overflow destinations work
- Test from a mobile phone -- DTMF works differently on some mobile carriers
- Test voicemail -- leave a test message, verify it arrives via email notification
Document the entire flow with a diagram showing every path a caller can take. When the person who built the IVR leaves the company (and they will), the diagram is the only thing that keeps the next person from rebuilding it from scratch.
DTMF Multi-Digit Input
Most IVRs use single-digit options, but Call Menus also support multi-digit input for extension dialing, account number lookup, or PIN verification. Set Menu Timeout appropriately -- callers need enough time between digits. 3-5 seconds per expected digit. The # key typically serves as the submit delimiter.
For account-number-based routing, use an AGI script that receives the DTMF input, performs a database lookup, and sets a channel variable the dialplan uses for routing. This lets callers enter their account number and get routed to the right department automatically -- skipping the menu entirely for known accounts.
Full Testing Checklist
Before going live with any IVR change:
- Call each DID and verify correct Call Menu loads
- Press every defined key and verify routing
- Press an undefined key and verify invalid-input handling
- Let the menu timeout without pressing anything, verify timeout routing
- Test after-hours routing -- change the Call Time temporarily or call outside hours
- Fill the target queue and verify overflow behavior
- Test from a mobile phone on a different carrier -- DTMF works differently
- Test voicemail -- leave a message, verify email notification arrives
- Test sub-menus -- navigate into each, verify return-to-main works
- Test with a colleague who doesn't know the menu -- watch where they get confused
Document the entire flow with a diagram. When the person who built the IVR leaves (and they will), the diagram is the only thing keeping the next person from rebuilding from scratch.
Common IVR Mistakes
Dead-end menu options. An option that plays information ("Our hours are 9 to 5") and then dumps the caller to silence. Always route back to the menu or to a queue after informational options.
No timeout handler. Elderly callers and callers on landlines may not hear the menu clearly. Timeout should route to a default queue, not disconnect.
Menu too long. If your greeting plus options takes longer than 30 seconds, callers zone out. Keep the main menu under 20 seconds. Put detail in sub-menus.
Mismatch between recorded options and actual routing. The greeting says "Press 3 for Billing" but option 3 actually routes to Support because someone changed the routing but didn't re-record the prompt. Audit prompts against routing quarterly.
No return-to-main option in sub-menus. Callers in the wrong sub-menu will hang up and call back. Always offer Press 0 or Press * to go back.
Testing only from the office. Your desk phone uses the same PBX as the dialer. Test from an external line -- preferably a mobile phone on a different carrier -- to verify the full SIP chain, DTMF delivery, and audio quality.
Not updating prompts when routing changes. The greeting says "Press 3 for Billing" but option 3 routes to Support because someone changed the routing and nobody re-recorded. Audit prompts against actual routing quarterly.
Ignoring mobile callers. A significant percentage of your inbound calls come from mobile phones. Mobile carriers handle DTMF differently, audio quality varies, and callers are often in noisy environments. If your IVR prompt is a wall of speech with no pauses, mobile callers miss the options. Keep prompts short, insert silence between options, and speak slowly.
Server Timezone and IVR
VICIdial uses the server's system time for Call Time evaluations. If your server is in US Eastern but your business operates in US Pacific, your "9 AM open" will actually trigger at noon Eastern. Set the server timezone correctly:
timedatectl set-timezone America/Los_Angeles
For operations spanning multiple timezones, you have two options: set the server to your primary business timezone, or use custom AGI scripts that evaluate multiple timezones and route accordingly. Most operations set the server timezone to match their headquarters location and define Call Times relative to that.
IVR Performance Tracking
VICIdial tracks IVR traffic through optional tracking groups. Set the Tracking Group field in each Call Menu to a group ID. This lets you report on how many callers entered each menu, which options they selected, and where they dropped off.
Key metrics to watch:
- IVR completion rate: What percentage of callers who enter the IVR actually reach an agent queue? If it's below 85%, your menu is losing people.
- Option distribution: Which options get the most traffic? If 80% of callers press 1 (Sales), consider routing directly to Sales and making it the timeout default.
- Abandonment at each level: Where do callers hang up? If most abandonments happen during Level 2 menus, your IVR is too deep.
- Average time in IVR: How long before callers reach an agent? Under 30 seconds is ideal. Over 60 seconds and you're losing patience-sensitive callers.
For operations that track IVR engagement closely, set up a custom report that joins the call menu logs with the inbound group closer logs to show the complete caller journey from DID to agent connection. This reveals which paths through the IVR produce the highest conversion rates and which paths produce the most abandoned calls.
An IVR that actually works -- clean audio, sensible menus, proper time-based routing, working DTMF -- is the difference between a professional operation and a ringing phone nobody answers. ViciStack builds and tests the entire inbound chain before go-live.
Originally published at https://vicistack.com/blog/vicidial-ivr-setup/
Top comments (0)