How to Configure ViciDial Closer Campaigns & In-Groups
Master the complete process of setting up closer campaigns and in-groups in ViciDial, from database configuration through agent assignment and real-time monitoring.
Prerequisites
Before proceeding with closer campaign and in-group configuration, ensure you have:
- ViciDial installation (3.14+ recommended) with working Asterisk (16+)
- Database access: Direct MySQL/MariaDB access to the
asteriskdatabase - Admin credentials: Access to
/vicidial/admin.phpwith administrator role - Asterisk CLI access: Ability to run
asterisk -rxcommands - Existing dialplans: At least one active dialer campaign configured
- User accounts: Multiple agents already created in ViciDial system
- In-group defined: A primary in-group created before closer assignment
- SSH/Shell access to ViciDial server with sudo privileges
Understanding Closer Campaigns vs In-Groups
What Are Closer Campaigns?
Closer campaigns are ViciDial's mechanism for transferring live inbound calls or callbacks to specific agents who specialize in closing sales. Unlike dialer campaigns that push calls outbound, closer campaigns pull inbound calls into a queue for assignment to available agents.
Key characteristics:
- Receive transferred calls from dialer campaigns or direct phone numbers
- Queue calls until agent availability
- Track transfer times and call dispositions separately
- Support multiple in-groups within one closer campaign
- Integrate with DNC (Do Not Call) lists and custom call rules
What Are In-Groups?
In-groups are the actual agent queues where calls wait for assignment. A closer campaign may have multiple in-groups (e.g., "Sales", "Support", "Escalations"), each with distinct:
- Member agents with individual priorities
- Call handling rules and routing logic
- Recording preferences
- Custom CID masking
- Hold music and announcements
The relationship: Closer Campaign contains In-Groups containing Agents
Part 1: Create the Closer Campaign via Web UI
Step 1.1: Access Campaign Configuration
- Log into
/vicidial/admin.phpwith administrator account - Navigate: Admin → Campaigns → Add New Campaign
- Select Campaign Type: "Closer"
- Fill in campaign details:
| Field | Example Value | Notes |
|---|---|---|
| Campaign ID | CLOSER_SALES | Uppercase, no spaces, 2-20 chars |
| Campaign Name | Sales Closer Campaign | Human-readable name |
| Active | YES | Enable immediately |
| Outbound CID | 18005551234 | Caller ID for transfers |
| Outbound CID Name | Sales Dept | Display name (optional) |
| Phone Number | 18005551000 | DID for external transfers |
| Call Time Zone | America/Chicago | Agent's timezone |
| Use Asterisk | YES | Required for call routing |
Step 1.2: Configure Callback and Transfer Settings
In the same campaign creation form:
Callback Auto Hold Minutes: 5 Callback Auto Dial: YES Campaigns to receive from: DIALER_SALES,DIALER_LEADS Transfer Timeout Seconds: 45 Voicemail Extension: 9999 Call Recording: ALWAYS Recording Call Display Name: CLOSER_SALES Lead ID Prefix: CS- Critical setting: Leave "Closer Dial Campaign" BLANK (this is for callbacks originated from closer agents, not for receiving transfers)
Step 1.3: Save and Verify
Click SUBMIT. ViciDial will:
- Create campaign record in
vicidial_campaignstable - Create campaign contexts in
/etc/asterisk/extensions-vicidial.conf - Generate default dialplan extensions
Verify creation:
mysql -u root -p asterisk -e "SELECT campaign_id, campaign_name, campaign_type, active FROM vicidial_campaigns WHERE campaign_id='CLOSER_SALES';" Expected output:
| CLOSER_SALES | Sales Closer Campaign | CLOSER | Y | Part 2: Create In-Groups Within the Closer Campaign
Step 2.1: In-Group Fundamentals
In-groups are the actual call queues. You must create at least one in-group per closer campaign. Most production environments have 2-4 in-groups:
- Primary in-group: Handles all standard transfers
- Overflow in-group: Secondary queue when primary is full
- VIP in-group: Dedicated high-value customer queue
- Escalation in-group: Complex issues requiring senior agents
Step 2.2: Create In-Group via Database
While ViciDial admin UI has in-group creation, direct database insertion is faster for bulk setup:
INSERT INTO vicidial_ingroups ( ingroup_id, campaign_id, ingroup_name, description, active, callback_grp, phone_number, voicemail_ext, hold_music, recording_call_display_name, call_timeout, call_priority, full_set_queue_exten, customer_greeting, agent_greeting, in_group_digits ) VALUES ( 'CLOSER_SALES_MAIN', 'CLOSER_SALES', 'Sales Main Queue', 'Primary in-group for sales closer campaign', 'Y', 'Y', '18005551000', '9999', 'default', 'CLOSER_SALES_MAIN', '120', '1', '1', 'Thanks for calling. A representative will be with you shortly.', 'Thank you for calling. Please give me one moment while I retrieve your information.', '' ); Key fields explanation:
-
ingroup_id: Unique identifier across system (24 chars max) -
callback_grp: Allow agents to callback? Set 'Y' for closer queues -
call_timeout: Seconds before call abandons from queue (120-300 typical) -
call_priority: Lower number = higher priority (1-5 range) -
full_set_queue_exten: Extension when queue full (usually extension 1) -
recording_call_display_name: Identifies recording in playback system
Step 2.3: Create Secondary In-Group (Overflow)
INSERT INTO vicidial_ingroups ( ingroup_id, campaign_id, ingroup_name, description, active, callback_grp, phone_number, voicemail_ext, hold_music, call_timeout, call_priority, full_set_queue_exten ) VALUES ( 'CLOSER_SALES_OVERFLOW', 'CLOSER_SALES', 'Sales Overflow Queue', 'Overflow for sales closer campaign', 'Y', 'Y', '18005551001', '9999', 'default', '150', '2', '1' ); Step 2.4: Verify In-Group Creation
mysql -u root -p asterisk -e "SELECT ingroup_id, campaign_id, ingroup_name, active, call_timeout FROM vicidial_ingroups WHERE campaign_id='CLOSER_SALES';" Expected:
| CLOSER_SALES_MAIN | CLOSER_SALES | Sales Main Queue | Y | 120 | | CLOSER_SALES_OVERFLOW | CLOSER_SALES | Sales Overflow Queue| Y | 150 | Part 3: Assign Agents to In-Groups
Step 3.1: Add Agent Memberships
Agents must be explicitly assigned to in-groups via the vicidial_ingroup_agents table. Each agent can belong to multiple in-groups with different priorities and statuses:
INSERT INTO vicidial_ingroup_agents ( ingroup_id, user_id, agent_priority, lead_id, agent_status, closer_flag ) VALUES ('CLOSER_SALES_MAIN', 'agent001', 1, '0', 'ACTIVE', 'Y'), ('CLOSER_SALES_MAIN', 'agent002', 2, '0', 'ACTIVE', 'Y'), ('CLOSER_SALES_MAIN', 'agent003', 3, '0', 'ACTIVE', 'Y'), ('CLOSER_SALES_OVERFLOW', 'agent004', 1, '0', 'ACTIVE', 'Y'), ('CLOSER_SALES_OVERFLOW', 'agent002', 2, '0', 'ACTIVE', 'Y'); Field explanations:
-
agent_priority: 1=highest priority, 9=lowest. Lower number gets called first -
closer_flag: 'Y' = this agent handles closer calls (must be 'Y') -
agent_status: 'ACTIVE'='available', 'INACTIVE'='unavailable', 'DELETED'='removed' -
lead_id: Usually '0' for in-group assignments; can specify a particular lead
Step 3.2: Configure Individual Agent Closer Permissions
Each agent needs ViciDial user account flags enabled. Update the vicidial_users table:
UPDATE vicidial_users SET closer_enabled = '1', closer_campaigns = 'CLOSER_SALES', call_transfer_flag = '1', no_outbound_calls = '0', can_transfer_to_other_users = '1' WHERE user_id IN ('agent001', 'agent002', 'agent003', 'agent004'); Critical fields:
-
closer_enabled: Set to '1' to allow agent to receive closer calls -
closer_campaigns: Comma-separated list of campaigns agent can handle -
call_transfer_flag: Enables blind/attended transfers -
can_transfer_to_other_users: Allows warm transfer to other agents
Verify settings:
mysql -u root -p asterisk -e "SELECT user_id, closer_enabled, closer_campaigns FROM vicidial_users WHERE user_id IN ('agent001','agent002','agent003','agent004');" Step 3.3: Set Agent Pause Codes for In-Group Work
Create pause codes specific to closer work:
INSERT INTO vicidial_pause_codes ( pause_code, pause_name, inbound_seconds, outbound_seconds, active ) VALUES ('CLOSER', 'Closer Call In Progress', 0, 0, 'Y'), ('CLOSER_BREAK', 'Closer Break', 300, 300, 'Y'), ('CLOSER_LUNCH', 'Closer Lunch', 3600, 3600, 'Y'); These codes appear in agent's pause dropdown in /agc/vicidial.php interface.
Part 4: Configure Asterisk Dialplan Integration
Step 4.1: Verify Auto-Generated Extensions
ViciDial automatically creates extensions when you add a closer campaign. Verify in /etc/asterisk/extensions-vicidial.conf:
grep -A 30 "exten => 8900,1" /etc/asterisk/extensions-vicidial.conf | head -40 You should see auto-generated closer campaign context. If missing, manually trigger regeneration:
/usr/share/astguiclient/astguiclient_closer_extension_sync.pl Step 4.2: Create Custom Transfer Extension (Optional)
To allow dialer agents to transfer calls to closer campaign, add to /etc/asterisk/extensions-vicidial.conf:
[ViciDial-Closer-Transfer] exten => 1,1,NoOp(Closer Transfer Request to ${closer_queue}) same => n,ViciDialCloserQueue(${closer_queue}|${closer_priority}|${CALLERID(num)}) same => n,Hangup() exten => i,1,NoOp(Invalid extension) same => n,Playback(invalid) same => n,Hangup() exten => t,1,NoOp(Transfer timeout) same => n,Hangup() Step 4.3: Reload Asterisk Configuration
asterisk -rx "dialplan reload" Verify dialplan loaded:
asterisk -rx "dialplan show ViciDial-Closer-Transfer" Expected output:
[ ViciDial-Closer-Transfer ] '1' => 1. NoOp(Closer Transfer Request to ${closer_queue}) 2. ViciDialCloserQueue(${closer_queue}|${closer_priority}|${CALLERID(num)}) 3. Hangup() Part 5: Test the Closer Campaign
Step 5.1: Pre-Test Verification Checklist
Before testing, verify all components in database:
#!/bin/bash # Test script for closer campaign setup CAMPAIGN="CLOSER_SALES" DB="asterisk" echo "=== Campaign Configuration ===" mysql -u root -p "$DB" -e "SELECT campaign_id, campaign_type, active FROM vicidial_campaigns WHERE campaign_id='$CAMPAIGN';" echo -e "\n=== In-Groups ===" mysql -u root -p "$DB" -e "SELECT ingroup_id, ingroup_name, active FROM vicidial_ingroups WHERE campaign_id='$CAMPAIGN';" echo -e "\n=== Agent Assignments ===" mysql -u root -p "$DB" -e "SELECT ingroup_id, user_id, agent_priority, closer_flag FROM vicidial_ingroup_agents WHERE ingroup_id LIKE '$CAMPAIGN%';" echo -e "\n=== Agent Closer Status ===" mysql -u root -p "$DB" -e "SELECT user_id, closer_enabled, closer_campaigns FROM vicidial_users WHERE closer_enabled='1' LIMIT 5;" Run this script:
chmod +x test_closer_config.sh ./test_closer_config.sh Step 5.2: Manual Agent Availability Test
Log in agents to the system and verify they appear available for closer calls:
# Check current agent statuses mysql -u root -p asterisk -e "SELECT user_id, status, campaign_id, on_call FROM vicidial_users WHERE user_id IN ('agent001','agent002','agent003');" Step 5.3: Simulate Incoming Call to In-Group
Use Asterisk CLI to originate test call:
asterisk -rx "channel originate SIP/2000 extension 8900@CLOSER_SALES_MAIN-inbound" Monitor Asterisk logs in real-time:
tail -f /var/log/asterisk/messages | grep -i "closer\|ingroup\|queue" Step 5.4: Monitor Call Routing
Check Asterisk queue status:
asterisk -rx "queue show CLOSER_SALES_MAIN" Expected output:
CLOSER_SALES_MAIN has 0 calls (max unlimited) in 'rrordered' strategy agent001 (SIP/2000) (paused) (Not in call, paused 0m 45s) agent002 (SIP/2001) (ready) (Penalty: 2) agent003 (SIP/2002) (ready) (Penalty: 3) Step 5.5: Verify Call Logging
After test call completes, verify logs:
mysql -u root -p asterisk -e " SELECT call_id, caller_id, customer_id, campaign_id, start_time, call_duration, status FROM vicidial_closer_log WHERE campaign_id='CLOSER_SALES' ORDER BY start_time DESC LIMIT 5; " Part 6: Advanced Configuration
Step 6.1: Implement Call Distribution Strategy
Closer in-groups support multiple distribution strategies. Configure in vicidial_ingroups:
UPDATE vicidial_ingroups SET call_distribution_method = 'rrordered', max_concurrent_calls = '5', force_return_to_hold = 'N' WHERE ingroup_id = 'CLOSER_SALES_MAIN'; Distribution methods:
-
rrordered: Round-robin by agent priority (default, recommended) -
leastrecent: Agent with oldest last call -
fewestcalls: Agent with fewest total calls today -
random: Random agent selection
Step 6.2: Configure Callback Rules
Allow agents in closer campaign to schedule callbacks:
UPDATE vicidial_campaigns SET closer_dial_campaign = 'CLOSER_SALES_CB', callback_dial_method = 'MANUAL', callback_hours_start = '08:00:00', callback_hours_end = '20:00:00' WHERE campaign_id = 'CLOSER_SALES'; This requires a separate dialer campaign (CLOSER_SALES_CB) created for callback origination.
Step 6.3: Implement DNC (Do Not Call) Filtering
Link in-group to DNC lists:
INSERT INTO vicidial_ingroup_dnc_filters ( ingroup_id, dnc_filter_id, active ) VALUES ('CLOSER_SALES_MAIN', 'FEDERAL_DNC', 'Y'), ('CLOSER_SALES_MAIN', 'COMPANY_DNC', 'Y'), ('CLOSER_SALES_MAIN', 'INTERNAL_DNC', 'Y'); Step 6.4: Set Up Custom CID Masking
Force specific outbound CID for transfers from this in-group:
UPDATE vicidial_ingroups SET outbound_cid = '18005551234', outbound_cid_name = 'Sales Department', cid_mask_method = 'FORCED' WHERE ingroup_id = 'CLOSER_SALES_MAIN'; Step 6.5: Configure Call Recording Exceptions
Set custom recording preferences:
UPDATE vicidial_ingroups SET recording_call_display_name = 'CLOSER_SALES_MAIN_REC', call_recording_compression = '32', call_recording_quality = 'gsm', recording_enabled = 'Y' WHERE ingroup_id = 'CLOSER_SALES_MAIN'; Part 7: Performance Tuning
Step 7.1: Optimize Queue Timeout Settings
Based on team size and call volume, adjust timeouts:
UPDATE vicidial_ingroups SET call_timeout = '120', -- 2 min normal calls callback_timeout = '180' -- 3 min callbacks WHERE ingroup_id = 'CLOSER_SALES_MAIN'; Monitor actual wait times:
mysql -u root -p asterisk -e " SELECT DATE(start_time) as Date, COUNT(*) as Total_Calls, AVG(wait_time) as Avg_Wait_Sec, MAX(wait_time) as Max_Wait_Sec FROM vicidial_closer_log WHERE campaign_id='CLOSER_SALES' AND start_time > DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY DATE(start_time) ORDER BY Date DESC; " Step 7.2: Monitor Agent Adherence to In-Group
Track agent time allocation across in-groups:
SELECT user_id, ingroup_id, COUNT(*) as calls_handled, AVG(call_duration) as avg_duration, SUM(call_duration) as total_talk_time FROM vicidial_closer_log WHERE DATE(start_time) = CURDATE() GROUP BY user_id, ingroup_id ORDER BY total_talk_time DESC; Step 7.3: Set Agent Concurrent Call Limits
Prevent single agent from receiving multiple simultaneous calls:
UPDATE vicidial_users SET max_concurrent_calls = '1', force_logout_after_seconds = '0' WHERE user_id IN ('agent001', 'agent002', 'agent003', 'agent004'); Part 8: Troubleshooting
Issue: Calls Not Routing to In-Group
Symptom: Incoming calls to DID don't reach agent queues, instead hangup or disconnect.
Root causes & solutions:
- Campaign not active:
mysql -u root -p asterisk -e "SELECT campaign_id, active FROM vicidial_campaigns WHERE campaign_id='CLOSER_SALES';" If active is 'N', update:
UPDATE vicidial_campaigns SET active='Y' WHERE campaign_id='CLOSER_SALES'; - In-group not linked to campaign:
mysql -u root -p asterisk -e "SELECT ingroup_id, campaign_id FROM vicidial_ingroups WHERE campaign_id='CLOSER_SALES';" - Dialplan context missing:
asterisk -rx "dialplan show CLOSER_SALES | head -20" If no output, regenerate:
/usr/share/astguiclient/astguiclient_extension_update.pl asterisk -rx "dialplan reload" sleep 2 asterisk -rx "module reload pbx_config" - SIP routing issue - verify SIP profile includes ViciDial context:
grep -A 5 "context=" /etc/asterisk/sip-vicidial.conf | head -20 Issue: Agents Not Receiving Calls Despite Being Available
Symptom: Agents log in, show as READY, but calls don't route to them.
Diagnosis:
# Check agent in-group membership mysql -u root -p asterisk -e " SELECT u.user_id, u.status, ia.ingroup_id, ia.closer_flag FROM vicidial_users u LEFT JOIN vicidial_ingroup_agents ia ON u.user_id = ia.user_id WHERE u.user_id='agent001'; " Solutions:
If
ingroup_idis NULL, agent not assigned to in-group. Re-run Step 3.1.Verify closer_enabled flag:
mysql -u root -p asterisk -e "SELECT user_id, closer_enabled FROM vicidial_users WHERE user_id='agent001';" - Check if agent paused:
asterisk -rx "show channels | grep agent001" If showing paused status, use admin panel to unpause or force status:
UPDATE vicidial_users SET status='READY' WHERE user_id='agent001'; - Check Asterisk queue membership:
asterisk -rx "queue show CLOSER_SALES_MAIN | grep agent001" Issue: High Call Abandonment Rate
Symptom: Calls dropping before reaching agents (>15% abandonment).
Analysis:
mysql -u root -p asterisk -e " SELECT ingroup_id, COUNT(*) as total, SUM(CASE WHEN call_status='ABANDON' THEN 1 ELSE 0 END) as abandoned, ROUND(100*SUM(CASE WHEN call_status='ABANDON' THEN 1 ELSE 0 END)/COUNT(*), 2) as abandon_pct, AVG(wait_time) as avg_wait_sec FROM vicidial_closer_log WHERE DATE(start_time) = CURDATE() AND campaign_id='CLOSER_SALES' GROUP BY ingroup_id; " Solutions:
Increase agents assigned: Add more agents to in-group or create overflow in-group.
Reduce timeout: Lower
call_timeoutvalue (but not below 60 seconds):
UPDATE vicidial_ingroups SET call_timeout='90' WHERE ingroup_id='CLOSER_SALES_MAIN'; - Add voicemail option: Ensure
voicemail_extis configured:
UPDATE vicidial_ingroups SET voicemail_ext='9999' WHERE ingroup_id='CLOSER_SALES_MAIN'; - Implement callbacks: Enable callback option when queue full.
Issue: Calls Queuing But Not Distributed
Symptom: Queue shows calls waiting but agents not getting delivered calls.
Debug:
# Monitor queue in real-time watch -n 1 'asterisk -rx "queue show CLOSER_SALES_MAIN"' Check agent member penalties:
mysql -u root -p asterisk -e "SELECT ingroup_id, user_id, agent_priority FROM vicidial_ingroup_agents WHERE ingroup_id='CLOSER_SALES_MAIN';" Solution: Agent priority (penalty in queue) must be ascending (1, 2, 3, etc.). If reversed, queue won't distribute:
UPDATE vicidial_ingroup_agents SET agent_priority = 1 WHERE ingroup_id='CLOSER_SALES_MAIN' AND user_id='agent001'; UPDATE vicidial_ingroup_agents SET agent_priority = 2 WHERE ingroup_id='CLOSER_SALES_MAIN' AND user_id='agent002'; Reload queue:
asterisk -rx "queue reload" Issue: Calls Recording Not Working
Symptom: Closer calls complete but no recordings saved.
Check recording path:
ls -lah /var/spool/asterisk/monitor/ | tail -20 Verify recording_enabled flag:
mysql -u root -p asterisk -e "SELECT ingroup_id, recording_enabled, recording_call_display_name FROM vicidial_ingroups WHERE ingroup_id='CLOSER_SALES_MAIN';" Enable recordings:
UPDATE vicidial_ingroups SET recording_enabled='Y' WHERE ingroup_id='CLOSER_SALES_MAIN'; Check Asterisk logging:
grep -i "monitor\|record" /var/log/asterisk/messages | tail -30 Disk space issue - common cause:
df -h /var/spool/asterisk/ If full, archive old recordings:
find /var/spool/asterisk/monitor/ -mtime +30 -exec gzip {} \; Issue: Agent Can't Transfer Calls
Symptom: Closer agent attempts blind/attended transfer but fails.
Verify transfer permissions:
mysql -u root -p asterisk -e "SELECT user_id, call_transfer_flag, can_transfer_to_other_users FROM vicidial_users WHERE user_id='agent001';" Enable transfers:
UPDATE vicidial_users SET call_transfer_flag='1', can_transfer_to_other_users='1' WHERE user_id='agent001'; Check Asterisk DTMF configuration - verify feature codes in /etc/asterisk/features.conf:
[featuremap] blindxfer => #1 ; Blind transfer atxfer => *2 ; Attended transfer Test with CLI:
asterisk -rx "core show hints | grep agent001" Part 9: Monitoring & Reporting
Dashboard Query: Real-Time In-Group Status
SELECT ig.ingroup_id, ig.ingroup_name, COUNT(DISTINCT CASE WHEN u.status='READY' THEN u.user_id END) as available_agents, COUNT(DISTINCT CASE WHEN u.status='IN_CALL' THEN u.user_id END) as busy_agents, COUNT(DISTINCT CASE WHEN u.status='PAUSED' THEN uSource:https://dev.to/moisi_trungu_31647b7ac300/how-to-configure-vicidial-closer-campaigns-in-groups-2759Related posts
Postgres LISTEN/NOTIFY vs Redis Pub/Sub: Latency, Write Cost and Failure Cases, Tested
LlamaIndex Workflows vs LangGraph: Event-Driven Architecture and Async State Overhead Showdown
