Master complete inbound DID routing configuration in ViciDial, from carrier integration through call distribution to agents, with production-ready examples and troubleshooting strategies.
Prerequisites
Before you begin this tutorial, ensure you have:
- ViciDial installation: Running 2.14+ (tested on 2.14.0.1 and later)
- Asterisk server: Version 13+ with chan_sip or PJSIP installed
- Database access: MySQL/MariaDB with vicidial user credentials
- Root or sudo access: Required for Asterisk configuration modifications
- SIP carrier account: Active account with inbound call capability
- ViciDial admin panel access: Web interface at
http://your-server/vicidial/admin.php - SSH/terminal access: To the ViciDial server for CLI operations
- Basic ViciDial knowledge: Familiarity with campaigns, user accounts, and call disposition codes
Understanding ViciDial DID Routing Architecture
DID (Direct Inward Dialing) routing in ViciDial connects incoming calls from your carrier through Asterisk dialplan logic, into ViciDial's inbound call queue system, and finally to available agents or IVR systems.
The routing chain flows like this:
SIP Carrier → Asterisk (sip-vicidial.conf) →
Extensions Dialplan (extensions-vicidial.conf) →
ViciDial Inbound Groups → Call Queue → Agent
Each DID number must be mapped to:
- An Asterisk context for initial routing
- A ViciDial inbound group
- Queue settings (agents, call recording, etc.)
- Agent assignment strategy (round-robin, longest-idle, etc.)
Prerequisites Continued: Database Tables
The following ViciDial database tables are critical for DID routing:
| Table | Purpose |
|---|---|
vicidial_inbound_groups |
Defines inbound call groups (queues) |
vicidial_users |
Agent accounts and inbound group assignments |
vicidial_phone_codes |
DID to inbound group mapping |
vicidial_carrier_log |
Inbound call detail records |
vicidial_log |
Agent interaction logs |
Step 1: Configure Your SIP Carrier Connection
1.1 Add Carrier to ViciDial Database
First, register your SIP carrier in the carrier configuration. Log into MySQL:
mysql -u root -p asterisk
Query existing carriers:
SELECT carrier_id, carrier_name, active FROM vicidial_carriers LIMIT 10;
Insert a new carrier (e.g., VoIP.ms):
INSERT INTO vicidial_carriers
(carrier_id, carrier_name, active, carrier_type, max_inbound, max_outbound,
inbound_cid, description)
VALUES
('VOIPMS', 'VoIP.ms', 'Y', 'SIP', 50, 100, 'Y', 'Primary SIP carrier');
Exit MySQL:
EXIT;
1.2 Configure SIP Trunk in sip-vicidial.conf
Edit the SIP configuration file:
sudo nano /etc/asterisk/sip-vicidial.conf
Add or modify your carrier SIP peer configuration. Example for VoIP.ms:
; VoIP.ms SIP Trunk Configuration
[voipms]
type=peer
host=toronto1.voip.ms
username=account_number
password=your_sip_password
secret=your_sip_password
fromuser=account_number
fromdomain=toronto1.voip.ms
context=from-voipms
insecure=port,invite
dtmfmode=rfc2833
canreinvite=no
nat=yes
qualify=yes
qualifyfreq=60
maxcallbitrate=128000
disallow=all
allow=ulaw,alaw
description=VoIP.ms Inbound Trunk
For a local SIP PBX or test environment:
[localsip]
type=peer
host=192.168.1.100
username=vicidial
password=your_password
secret=your_password
context=from-local-pbx
insecure=port,invite
nat=no
qualify=yes
1.3 Test Carrier Connection
Reload Asterisk SIP configuration:
sudo asterisk -rx "sip reload"
Verify the peer is registered:
sudo asterisk -rx "sip show peers"
You should see your carrier peer listed with status "OK (x ms)".
Check registration if using registration:
sudo asterisk -rx "sip show registry"
Step 2: Create Inbound Groups in ViciDial
Inbound groups are the core container for call distribution in ViciDial. Every inbound DID must belong to an inbound group.
2.1 Access the Admin Panel
Navigate to:
http://your-vicidial-server/vicidial/admin.php
Log in with your admin credentials.
2.2 Create Inbound Group via Admin
- Click Inbound → Inbound Groups
- Click ADD NEW INBOUND GROUP
- Fill in the following fields:
| Field | Example Value | Notes |
|---|---|---|
| Inbound Group Name | Sales_Queue_001 | Unique identifier |
| Inbound Group ID | sales_001 | System-wide unique ID |
| Active | Y | Enable the group |
| Calls in Queue Limit | 100 | Max queued calls before rejection |
| Greeting Recording | sales_greeting.wav | IVR greeting file |
| Hold Recording | hold_music.wav | Music on hold file |
| Voicemail Recording | voicemail.wav | Voicemail greeting |
| CID Group | EXTERNAL | Caller ID group for ANI capture |
| Call Recording | Y | Record all inbound calls |
| Web Form Recording | N | Skip web form integration |
| Default Group | Y | Primary routing group |
Click SUBMIT.
2.3 Create via Direct Database Insertion
Alternatively, insert directly into the database:
INSERT INTO vicidial_inbound_groups
(inbound_group_id, inbound_group_name, active, calls_in_queue_limit,
greeting_id, hold_id, voicemail_id, cid_group_id, call_recording,
web_form_address, web_form_recording, default_group, park_extension,
park_timeout)
VALUES
('sales_001', 'Sales_Queue_001', 'Y', 100, 1, 1, 1, 'EXTERNAL', 'Y',
'', 'N', 'Y', '6900', 1800);
Verify creation:
SELECT inbound_group_id, inbound_group_name, active FROM vicidial_inbound_groups
WHERE inbound_group_id = 'sales_001';
Step 3: Assign Agents to Inbound Groups
Agents must be explicitly assigned to inbound groups before they can receive inbound calls.
3.1 Assign via Admin Panel
- Navigate to Admin → Users
- Click on an agent's username
- Under Inbound Settings, select the inbound group from the dropdown
- Set Agent Type to "INBOUND" or "BOTH"
- Click SUBMIT
3.2 Assign via Database
UPDATE vicidial_users
SET inbound_group = 'sales_001',
user_level = 1,
active = 'Y'
WHERE user = 'agent001';
Verify assignment:
SELECT user, inbound_group, user_level, active
FROM vicidial_users
WHERE user = 'agent001';
3.3 Set Agent Availability
Agents must be logged in and in "READY" state to receive calls. This is handled via the agent interface at /agc/vicidial.php, but you can verify availability:
sudo asterisk -rx "queue show sales_001"
This command shows all agents in the queue and their status.
Step 4: Map DIDs to Inbound Groups
DIDs are mapped to inbound groups using the vicidial_phone_codes table. Each DID number can route to a specific inbound group.
4.1 Insert DID-to-Group Mapping
Map a DID number to your inbound group:
INSERT INTO vicidial_phone_codes
(phone_code, phone_code_name, inbound_group, call_type, active,
description)
VALUES
('2015550123', 'Sales DID Main', 'sales_001', 'INGROUP', 'Y',
'Primary sales inbound DID');
For multiple DIDs to the same group:
INSERT INTO vicidial_phone_codes
(phone_code, phone_code_name, inbound_group, call_type, active, description)
VALUES
('2015550123', 'Sales DID 1', 'sales_001', 'INGROUP', 'Y', 'Sales line'),
('2015550124', 'Sales DID 2', 'sales_001', 'INGROUP', 'Y', 'Sales line'),
('2015550125', 'Sales DID 3', 'sales_001', 'INGROUP', 'Y', 'Sales line');
Verify mapping:
SELECT phone_code, inbound_group, call_type, active
FROM vicidial_phone_codes
WHERE inbound_group = 'sales_001' ORDER BY phone_code;
4.2 Configure via Admin Panel (Optional)
- Navigate to Inbound → Phone Codes
- Click ADD NEW PHONE CODE
- Enter your DID number in Phone Code
- Select your inbound group from the dropdown
- Set Call Type to "INGROUP"
- Click SUBMIT
Step 5: Configure Asterisk Dialplan Extensions
The Asterisk dialplan bridges the gap between the SIP carrier context and ViciDial's inbound group system.
5.1 Create Carrier Context in extensions-vicidial.conf
Edit the main extensions file:
sudo nano /etc/asterisk/extensions-vicidial.conf
Add a context for your carrier. This example routes from VoIP.ms:
[from-voipms]
exten => _.,1,NoOp(Inbound DID: ${EXTEN} from ${CALLERID(num)})
exten => _.,n,Set(CALLERID(num)=${CALLERID(num)})
exten => _.,n,Set(CALLERID(name)=${CALLERID(name)})
exten => _.,n,Set(TO_VICIDIAL=${EXTEN})
exten => _.,n,Gosub(route-vicidial,${EXTEN},1)
exten => _.,n,Hangup()
include => parked-calls
5.2 Create ViciDial Routing Subroutine
Add the main ViciDial routing subroutine in the same file:
[route-vicidial]
exten => _.,1,NoOp(=== ViciDial Routing Subroutine ===)
exten => _.,n,NoOp(Received DID: ${ARG1})
exten => _.,n,Set(DID_NUMBER=${ARG1})
; Query database for inbound group
exten => _.,n,ODBC_Query(vicidial,SELECT inbound_group FROM vicidial_phone_codes WHERE phone_code='${DID_NUMBER}',result)
exten => _.,n,Set(INBOUND_GROUP=${result})
; Route based on inbound group
exten => _.,n,GotoIf($[${LEN(${INBOUND_GROUP})} > 0]?found:notfound)
exten => _.,n(found),NoOp(Found inbound group: ${INBOUND_GROUP})
exten => _.,n,Set(CHANNEL(language)=en)
exten => _.,n,Gosub(queue-call,${INBOUND_GROUP},1)
exten => _.,n,Return()
exten => _.,n(notfound),NoOp(No inbound group found for DID: ${DID_NUMBER})
exten => _.,n,Playback(sorry-youve-dialed-a-wrong-number)
exten => _.,n,Hangup()
[queue-call]
exten => _.,1,NoOp(=== Entering Queue: ${ARG1} ===)
exten => _.,n,Set(VICIDIAL_INBOUND_GROUP=${ARG1})
exten => _.,n,Set(CALL_RECORDING=Y)
exten => _.,n,Set(UNIQUEID=${UNIQUEID()})
exten => _.,n,Queue(${ARG1},t,,180)
exten => _.,n,Hangup()
5.3 Alternative: Direct Queue Entry (Simplified)
For simpler configurations without database lookups:
[from-voipms]
; Route specific DIDs directly to queues
exten => 2015550123,1,NoOp(Sales DID received)
exten => 2015550123,n,Set(VICIDIAL_INBOUND_GROUP=sales_001)
exten => 2015550123,n,Queue(sales_001,t,,180)
exten => 2015550123,n,Hangup()
exten => 2015550124,1,NoOp(Support DID received)
exten => 2015550124,n,Set(VICIDIAL_INBOUND_GROUP=support_001)
exten => 2015550124,n,Queue(support_001,t,,180)
exten => 2015550124,n,Hangup()
exten => 2015550125,1,NoOp(Billing DID received)
exten => 2015550125,n,Set(VICIDIAL_INBOUND_GROUP=billing_001)
exten => 2015550125,n,Queue(billing_001,t,,180)
exten => 2015550125,n,Hangup()
5.4 Reload Dialplan
Apply the changes:
sudo asterisk -rx "dialplan reload"
Verify the dialplan loaded correctly:
sudo asterisk -rx "dialplan show from-voipms"
Step 6: Configure Queue Parameters in ViciDial
Queue settings control call distribution behavior in your inbound groups.
6.1 Queue Strategy Settings
Edit /etc/asterisk/queues.conf or add to extensions context:
[sales_001]
music = default
strategy = rrmemory
timeout = 180
announce = queue-announce
announce-frequency = 0
announce-holdtime = no
joinempty = yes
leavewhenempty = no
ringall = no
maxlen = 50
Queue Strategy Explanations:
rrmemory: Round-robin with memory (preferred for balanced distribution)linear: Sequential agent orderfewest: Route to agent with fewest callsleastrecent: Route to agent idle longest
6.2 Create Queue via ViciDial Admin
- Navigate to Queues or queue settings in Inbound Groups
- Set:
- Ring Strategy: Round-robin with memory
- Ring All Agents: No (ring one at a time)
- Max Queue Size: 100
- Wait Timeout: 180 seconds
- Agent Ring Timeout: 20 seconds (time before moving to next agent)
6.3 Verify Queue Configuration
sudo asterisk -rx "queue show sales_001"
Output should show:
sales_001
Members:
Local/agent001@from-internal (dynamic)
Local/agent002@from-internal (dynamic)
Callers:
1. <Anonymous> (wait: 45:23, pos: 1)
Step 7: Configure ViciDial Logging and Recording
7.1 Enable Call Recording
Ensure recordings are enabled at both group and system level.
In vicidial_inbound_groups:
UPDATE vicidial_inbound_groups
SET call_recording = 'Y',
record_calls = 'Y'
WHERE inbound_group_id = 'sales_001';
7.2 Configure Recording Storage
Edit /etc/asterisk/modules.conf:
[modules]
autoload=yes
require=res_odbc.so
require=chan_sip.so
require=app_queue.so
require=app_mixmonitor.so
Recordings are typically stored in:
/var/spool/asterisk/monitor/
7.3 Verify Recording Directories
sudo ls -la /var/spool/asterisk/monitor/ | head -20
Ensure ViciDial daemon has write permissions:
sudo chown -R asterisk:asterisk /var/spool/asterisk/monitor/
sudo chmod 755 /var/spool/asterisk/monitor/
Step 8: Test the Complete DID Routing
8.1 Prepare Test Environment
Ensure:
- At least one agent is logged into
/agc/vicidial.phpin READY state - Agent is assigned to
sales_001inbound group - Your DID carrier is sending calls properly
8.2 Perform Inbound Test Call
Call your DID number from an external phone.
Monitor call in Asterisk:
sudo asterisk -rx "core show channels"
You should see:
Channel State App Data
SIP/voipms-xxxxx Up Queue (sales_001,t,,180)
Local/agent001@xxx Up AppQueue ()
8.3 Check ViciDial Agent Screen
The agent's screen at /agc/vicidial.php should show the incoming call.
8.4 Verify Call Logging
Check the call was logged to database:
SELECT call_date, caller_id_number, caller_id_name, called_number,
duration, agent, dispo FROM vicidial_carrier_log
WHERE called_number = '2015550123'
ORDER BY call_date DESC LIMIT 5;
Also check ViciDial logs:
SELECT call_date, caller_id_number, user, dispo, talk_time, hold_time
FROM vicidial_log
WHERE inbound_group = 'sales_001'
ORDER BY call_date DESC LIMIT 5;
Step 9: Implement Advanced Routing Scenarios
9.1 Time-Based Routing (Business Hours)
Route to different groups based on time of day:
[from-voipms]
exten => 2015550123,1,NoOp(DID received: ${EXTEN})
; Check if business hours (9 AM - 5 PM, Mon-Fri)
exten => 2015550123,n,GotoIf($[${IF($[${EPOCH} > ${STRFTIME(${EPOCH},2024-01-01 17:00:00,%s)}]?1:0)} = 1]?afterhours:business)
exten => 2015550123,n(business),NoOp(Routing to business hours group)
exten => 2015550123,n,Queue(sales_001,t,,180)
exten => 2015550123,n,Hangup()
exten => 2015550123,n(afterhours),NoOp(Routing to voicemail)
exten => 2015550123,n,VoiceMail(${EXTEN}@sales_voicemail)
exten => 2015550123,n,Hangup()
9.2 Skill-Based Routing
Route to different groups based on call type/IVR selection:
[from-voipms]
exten => 2015550123,1,NoOp(Sales main line)
exten => 2015550123,n,Set(SKILL_GROUP=sales_general)
exten => 2015550123,n,IVR(select-department)
exten => 2015550123,n,Queue(${SKILL_GROUP},t,,180)
exten => 2015550123,n,Hangup()
; IVR for department selection
[select-department]
exten => 1,1,NoOp(Selected: Billing)
exten => 1,n,Set(SKILL_GROUP=billing_001)
exten => 1,n,Return()
exten => 2,1,NoOp(Selected: Technical Support)
exten => 2,n,Set(SKILL_GROUP=support_001)
exten => 2,n,Return()
exten => 3,1,NoOp(Selected: Sales)
exten => 3,n,Set(SKILL_GROUP=sales_001)
exten => 3,n,Return()
9.3 Load Balancing Across Multiple Groups
Distribute calls across multiple similar groups:
[from-voipms]
exten => 2015550123,1,NoOp(Load balanced sales line)
exten => 2015550123,n,Set(LB_GROUP=${RAND(1,3)})
exten => 2015550123,n,Queue(sales_00${LB_GROUP},t,,180)
exten => 2015550123,n,Hangup()
This requires creating sales_001, sales_002, and sales_003 groups.
Step 10: Performance Tuning and Optimization
10.1 Optimize Queue Performance
Reduce database queries with caching:
sudo vi /etc/asterisk/res_odbc.conf
[asterisk]
dsn = asterisk
username = vicidial
password = your_password
pooling = yes
limit = 5
idleTimeout = 3600
10.2 Monitor Active Calls
Create a monitoring script:
#!/bin/bash
# check_vicidial_calls.sh
while true; do
ACTIVE_CALLS=$(asterisk -rx "core show channels" | grep -c "SIP/")
QUEUED_CALLS=$(asterisk -rx "queue show sales_001" | grep -c "wait:")
echo "$(date): Active Calls: $ACTIVE_CALLS | Queued: $QUEUED_CALLS"
sleep 5
done
Run it:
chmod +x check_vicidial_calls.sh
./check_vicidial_calls.sh
10.3 Database Query Optimization
Add indexes for faster lookups:
ALTER TABLE vicidial_phone_codes ADD INDEX idx_phone_code (phone_code);
ALTER TABLE vicidial_phone_codes ADD INDEX idx_inbound_group (inbound_group);
ALTER TABLE vicidial_users ADD INDEX idx_inbound_group (inbound_group);
Troubleshooting
Issue: Calls Not Reaching Agents
Symptoms: Incoming calls drop or go to voicemail instead of ringing agents.
Solutions:
- Verify agent is logged in and READY:
sudo asterisk -rx "queue show sales_001"
- Check agent assignment to inbound group:
SELECT user, inbound_group FROM vicidial_users WHERE user = 'agent001';
- Verify queue context includes the inbound group:
sudo asterisk -rx "dialplan show sales_001"
- Check Asterisk error logs:
sudo tail -f /var/log/asterisk/messages | grep -i queue
Issue: Calls Drop Immediately After Answer
Symptoms: Agent answers but call immediately disconnects.
Solutions:
- Check SIP peer context matches dialplan context:
grep -A 5 "context=" /etc/asterisk/sip-vicidial.conf
- Verify extensions exist in that context:
sudo asterisk -rx "dialplan show from-voipms" | head -20
- Check for codec mismatch:
sudo asterisk -rx "sip show peer voipms"
- Review logs for SIP errors:
sudo tail -f /var/log/asterisk/messages | grep -i sip
Issue: DID Not Found / Wrong Routing
Symptoms: Calls route to wrong queue or default handler.
Solutions:
- Verify DID is in phone_codes table:
SELECT * FROM vicidial_phone_codes WHERE phone_code = '2015550123';
- Check phone code format matches (leading zeros, country codes, etc.):
SELECT DISTINCT phone_code FROM vicidial_phone_codes LIMIT 10;
- Test database query directly:
sudo asterisk -rx "database show vicidial"
- Add debug logging to dialplan:
exten => _.,n,NoOp(DEBUG: DID=${DID_NUMBER} Group=${INBOUND_GROUP})
Then check:
sudo asterisk -rx "set verbose 4"
Issue: No Call Recordings
Symptoms: Calls complete but no recordings appear.
Solutions:
- Verify recording is enabled:
SELECT inbound_group_id, call_recording FROM vicidial_inbound_groups
WHERE inbound_group_id = 'sales_001';
- Check recording directory exists and has proper permissions:
ls -la /var/spool/asterisk/monitor/ | head
sudo chown -R asterisk:asterisk /var/spool/asterisk/monitor/
- Verify MixMonitor is loaded:
sudo asterisk -rx "module show like app_mixmonitor"
- Check for disk space issues:
df -h /var/spool/asterisk/monitor/
- Review ViciDial call logs for recording path:
SELECT call_date, recording_id, call_recording FROM vicidial_carrier_log
WHERE inbound_group = 'sales_001' LIMIT 5;
Issue: Queue Timeout - Calls Hanging or Looping
Symptoms: Calls stay in queue too long or loop back to queue.
Solutions:
- Verify queue timeout settings:
sudo asterisk -rx "queue show sales_001"
- Adjust timeout in extensions-vicidial.conf:
exten => _.,n,Queue(sales_001,t,,120) ; Reduced to 120 seconds
- Check Asterisk queue settings in queues.conf:
grep -A 10 "\[sales_001\]" /etc/asterisk/queues.conf
- Verify agent ring timeout:
[sales_001]
timeout = 20 ; Ring timeout per agent
Issue: High Call Abandonment Rate
Symptoms: Callers hanging up while waiting in queue.
Solutions:
- Increase number of agents in group:
SELECT COUNT(*) as agent_count FROM vicidial_users
WHERE inbound_group = 'sales_001' AND active = 'Y';
- Monitor queue depth:
sudo asterisk -rx "queue show sales_001" | grep "Callers:"
- Enable hold time announcements:
UPDATE vicidial_inbound_groups
SET announce_holdtime = 'Y'
WHERE inbound_group_id = 'sales_001';
- Implement callback system for high queue depths:
exten => _.,n,GotoIf($[${QUEUE_MEMBER_COUNT(sales_001)} > 5]?callback:queue)
exten => _.,n(callback),VoiceMail(${EXTEN}@sales_callback)
exten => _.,n,Hangup()
Issue: Caller ID Not Displaying Correctly
Symptoms: Agent sees "Unknown" or generic CID instead of actual caller.
Solutions:
- Enable CID preservation in dialplan:
exten => _.,1,Set(CALLERID(num)=${CALLERID(num)})
exten => _.,n,Set(CALLERID(name)=${CALLERID(name)})
- Verify carrier sends ANI information:
sudo asterisk -rx "sip show channels" | grep voipms
- Check CID Group settings:
SELECT * FROM vicidial_inbound_groups
WHERE inbound_group_id = 'sales_001'\G
- Review actual call logs:
SELECT caller_id_number, caller_id_name, agent
FROM vicidial_carrier_log LIMIT 5;
Summary
You now have a complete, production-ready DID routing configuration in ViciDial. Here's what you've implemented:
Key Components Configured:
- ✅ SIP carrier connection with proper peer configuration
- ✅ Inbound groups created and configured in ViciDial
- ✅ DID-to-group mapping via phone codes table
- ✅ Asterisk dialplan with routing logic (from-voipms context)
- ✅ Queue management and agent assignment
- ✅ Call recording and logging infrastructure
- ✅ Advanced routing scenarios (time-based, skill-based, load-balanced)
- ✅ Comprehensive troubleshooting procedures
Critical Configuration Files:
/etc/asterisk/sip-vicidial.conf— Carrier SIP peer definitions/etc/asterisk/extensions-vicidial.conf— Dialplan routing logic/etc/asterisk/queues.conf— Queue parameters (optional)
Critical Database Tables:
vicidial_carriers— Carrier registrationvicidial_inbound_groups— Queue definitionsvicidial_phone_codes— DID mappingvicidial_users— Agent assignmentsvicidial_carrier_log— Inbound call records
Next Steps:
- Test with real inbound calls from your carrier
- Monitor performance using
queue showand database queries - Implement failover routing for redundancy
- Set up alerts for queue depth and agent availability
- Document your specific DID/extension mappings
- Schedule regular backup of extensions and queue configs
Best Practices:
- Always test configuration changes with test calls before production traffic
- Monitor
/var/log/asterisk/messagesduring testing and troubleshooting - Keep detailed notes of all DID numbers, their purposes, and target inbound groups
- Regularly review call logs and agent metrics to optimize routing
- Implement proper access controls for admin panel and database
- Test failover scenarios and call handling during agent unavailability
Your ViciDial DID routing is now ready for production use with proper logging, recording, and agent distribution.