← All Tutorials

How to Set Up ViciDial DID Routing — Inbound Number Configuration

ViciDial Administration Intermediate 15 min read #92

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:

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:

  1. An Asterisk context for initial routing
  2. A ViciDial inbound group
  3. Queue settings (agents, call recording, etc.)
  4. 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

  1. Click InboundInbound Groups
  2. Click ADD NEW INBOUND GROUP
  3. 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

  1. Navigate to AdminUsers
  2. Click on an agent's username
  3. Under Inbound Settings, select the inbound group from the dropdown
  4. Set Agent Type to "INBOUND" or "BOTH"
  5. 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)

  1. Navigate to InboundPhone Codes
  2. Click ADD NEW PHONE CODE
  3. Enter your DID number in Phone Code
  4. Select your inbound group from the dropdown
  5. Set Call Type to "INGROUP"
  6. 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:

6.2 Create Queue via ViciDial Admin

  1. Navigate to Queues or queue settings in Inbound Groups
  2. 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:

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:

  1. Verify agent is logged in and READY:
sudo asterisk -rx "queue show sales_001"
  1. Check agent assignment to inbound group:
SELECT user, inbound_group FROM vicidial_users WHERE user = 'agent001';
  1. Verify queue context includes the inbound group:
sudo asterisk -rx "dialplan show sales_001"
  1. 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:

  1. Check SIP peer context matches dialplan context:
grep -A 5 "context=" /etc/asterisk/sip-vicidial.conf
  1. Verify extensions exist in that context:
sudo asterisk -rx "dialplan show from-voipms" | head -20
  1. Check for codec mismatch:
sudo asterisk -rx "sip show peer voipms"
  1. 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:

  1. Verify DID is in phone_codes table:
SELECT * FROM vicidial_phone_codes WHERE phone_code = '2015550123';
  1. Check phone code format matches (leading zeros, country codes, etc.):
SELECT DISTINCT phone_code FROM vicidial_phone_codes LIMIT 10;
  1. Test database query directly:
sudo asterisk -rx "database show vicidial"
  1. 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:

  1. Verify recording is enabled:
SELECT inbound_group_id, call_recording FROM vicidial_inbound_groups 
WHERE inbound_group_id = 'sales_001';
  1. Check recording directory exists and has proper permissions:
ls -la /var/spool/asterisk/monitor/ | head
sudo chown -R asterisk:asterisk /var/spool/asterisk/monitor/
  1. Verify MixMonitor is loaded:
sudo asterisk -rx "module show like app_mixmonitor"
  1. Check for disk space issues:
df -h /var/spool/asterisk/monitor/
  1. 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:

  1. Verify queue timeout settings:
sudo asterisk -rx "queue show sales_001"
  1. Adjust timeout in extensions-vicidial.conf:
exten => _.,n,Queue(sales_001,t,,120)  ; Reduced to 120 seconds
  1. Check Asterisk queue settings in queues.conf:
grep -A 10 "\[sales_001\]" /etc/asterisk/queues.conf
  1. 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:

  1. Increase number of agents in group:
SELECT COUNT(*) as agent_count FROM vicidial_users 
WHERE inbound_group = 'sales_001' AND active = 'Y';
  1. Monitor queue depth:
sudo asterisk -rx "queue show sales_001" | grep "Callers:"
  1. Enable hold time announcements:
UPDATE vicidial_inbound_groups 
SET announce_holdtime = 'Y' 
WHERE inbound_group_id = 'sales_001';
  1. 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:

  1. Enable CID preservation in dialplan:
exten => _.,1,Set(CALLERID(num)=${CALLERID(num)})
exten => _.,n,Set(CALLERID(name)=${CALLERID(name)})
  1. Verify carrier sends ANI information:
sudo asterisk -rx "sip show channels" | grep voipms
  1. Check CID Group settings:
SELECT * FROM vicidial_inbound_groups 
WHERE inbound_group_id = 'sales_001'\G
  1. 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:

  1. ✅ SIP carrier connection with proper peer configuration
  2. ✅ Inbound groups created and configured in ViciDial
  3. ✅ DID-to-group mapping via phone codes table
  4. ✅ Asterisk dialplan with routing logic (from-voipms context)
  5. ✅ Queue management and agent assignment
  6. ✅ Call recording and logging infrastructure
  7. ✅ Advanced routing scenarios (time-based, skill-based, load-balanced)
  8. ✅ Comprehensive troubleshooting procedures

Critical Configuration Files:

Critical Database Tables:

Next Steps:

  1. Test with real inbound calls from your carrier
  2. Monitor performance using queue show and database queries
  3. Implement failover routing for redundancy
  4. Set up alerts for queue depth and agent availability
  5. Document your specific DID/extension mappings
  6. Schedule regular backup of extensions and queue configs

Best Practices:

Your ViciDial DID routing is now ready for production use with proper logging, recording, and agent distribution.

Stuck on something specific?

Book a free 30-minute call. I run ViciDial centers across 3 countries and can usually unblock your setup in one session — or build it for you.

Book a Free Consultation