← All Tutorials

How to Configure ViciDial Closer Campaigns & In-Groups

ViciDial Administration Intermediate 16 min read #99

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:

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:

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:

The relationship: Closer Campaign contains In-Groups containing Agents

Part 1: Create the Closer Campaign via Web UI

Step 1.1: Access Campaign Configuration

  1. Log into /vicidial/admin.php with administrator account
  2. Navigate: Admin → Campaigns → Add New Campaign
  3. Select Campaign Type: "Closer"
  4. 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:

  1. Create campaign record in vicidial_campaigns table
  2. Create campaign contexts in /etc/asterisk/extensions-vicidial.conf
  3. 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:

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:

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:

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:

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:

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:

  1. 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';
  1. 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';"
  1. 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"
  1. 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:

  1. If ingroup_id is NULL, agent not assigned to in-group. Re-run Step 3.1.

  2. Verify closer_enabled flag:

mysql -u root -p asterisk -e "SELECT user_id, closer_enabled FROM vicidial_users WHERE user_id='agent001';"
  1. 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';
  1. 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:

  1. Increase agents assigned: Add more agents to in-group or create overflow in-group.

  2. Reduce timeout: Lower call_timeout value (but not below 60 seconds):

UPDATE vicidial_ingroups SET call_timeout='90' WHERE ingroup_id='CLOSER_SALES_MAIN';
  1. Add voicemail option: Ensure voicemail_ext is configured:
UPDATE vicidial_ingroups SET voicemail_ext='9999' WHERE ingroup_id='CLOSER_SALES_MAIN';
  1. 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 u.user_id END) as paused_agents,
  (SELECT COUNT(*) FROM vicidial_log vl 
   WHERE vl.ingroup_id = ig.ingroup_id 
   AND vl.call_status = 'WAITING') as calls_waiting
FROM vicidial_ingroups ig
LEFT JOIN vicidial_ingroup_agents iag ON ig.ingroup_id = iag.ingroup_id
LEFT JOIN vicidial_users u ON iag.user_id = u.user_id
WHERE ig.campaign_id='CLOSER_SALES'
GROUP BY ig.ingroup_id, ig.ingroup_name;

Daily Performance Report

SELECT 
  DATE(start_time) as call_date,
  ingroup_id,
  COUNT(*) as total_calls,
  SUM(CASE WHEN call_status='COMPLETED' THEN 1 ELSE 0 END) as completed_calls,
  SUM(CASE WHEN call_status='ABANDON' THEN 1 ELSE 0 END) as abandoned_calls,
  ROUND(AVG(wait_time), 1) as avg_wait_seconds,
  ROUND(AVG(call_duration), 1) as avg_talk_seconds,
  ROUND(SUM(call_duration)/COUNT(*), 1) as avg_handle_time_seconds
FROM vicidial_closer_log
WHERE campaign_id='CLOSER_SALES'
  AND start_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND NOW()
GROUP BY DATE(start_time), ingroup_id
ORDER BY call_date DESC, ingroup_id;

Agent Productivity Report

SELECT 
  u.user_id,
  u.full_name,
  COUNT(DISTINCT cl.call_id) as calls_handled,
  SUM(cl.call_duration) as total_talk_time,
  ROUND(AVG(cl.call_duration), 1) as avg_talk_time,
  SUM(cl.wait_time) as total_wait_time,
  COUNT(DISTINCT CASE WHEN cl.call_status='COMPLETED' THEN cl.call_id END) as completed_calls
FROM vicidial_users u
LEFT JOIN vicidial_closer_log cl ON u.user_id = cl.assigned_to 
  AND DATE(cl.start_time) = CURDATE()
WHERE u.closer_enabled='1'
GROUP BY u.user_id, u.full_name
ORDER BY calls_handled DESC;

Summary

Configuring ViciDial closer campaigns and in-groups involves seven critical steps:

  1. Create the closer campaign in web UI with appropriate dialing and recording settings
  2. Define in-groups as call queues within the campaign, setting timeouts and priorities
  3. Assign agents to in-groups with individual priorities and closer permissions
  4. Enable Asterisk integration by verifying dialplan contexts and reloading configuration
  5. Test thoroughly using Asterisk CLI and database verification queries
  6. Optimize performance through distribution strategy tuning and timeout adjustments
  7. Monitor continuously with real-time queries and daily reports

Key production-grade practices:

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