← All Tutorials

Setting Up ViciDial Callbacks (CALLBK) — Agent & Auto

ViciDial Administration Intermediate 14 min read #46

Learn how to configure and manage both agent-initiated and automatic callbacks in ViciDial with practical examples, database queries, and production troubleshooting techniques.

Prerequisites

Before implementing callbacks in ViciDial, ensure you have:

Verify your installation:

mysql -u root -p asterisk -e "SELECT version FROM version LIMIT 1;"
asterisk -rx "core show version"
ps aux | grep -E "(asterisk|vicidial)" | grep -v grep

Understanding ViciDial Callbacks

What Are Callbacks?

Callbacks in ViciDial allow agents or the system to schedule calls to contact at a later time. This prevents contact overload, respects time zones, and increases connection rates. ViciDial supports two callback types:

  1. Agent Callbacks (CALLBK) — Agents schedule callbacks directly from the call screen
  2. Auto Callbacks — System automatically reschedules based on lead disposition rules

Why Callbacks Matter

Key Database Tables

Callbacks are stored primarily in these tables:

Agent Callbacks Configuration

Step 1: Enable Callbacks in Campaign Settings

Access the ViciDial web interface and navigate to Admin > Campaigns.

  1. Select your campaign
  2. Scroll to Callback Settings
  3. Set Allow Agent Callbacks to YES
  4. Set Callback Time Window (e.g., 1 day, 7 days, 30 days)
  5. Enable Allow Same-Day Callbacks if needed
  6. Set Callback Dial Timeout to 45-60 seconds
  7. Save changes

Verify via database:

SELECT campaign_id, campaign_name, allow_agent_callbacks, callback_time_window 
FROM vicidial_campaigns 
WHERE campaign_name = 'YOUR_CAMPAIGN';

Expected output:

campaign_id | campaign_name | allow_agent_callbacks | callback_time_window
1           | Sales         | Y                     | 1440

Step 2: Configure Agent Callback Permissions

Open /etc/asterisk/extensions-vicidial.conf and locate the agent login context. Add callback-specific permissions:

[from-internal-agent]
exten => *1,1,NoOp(Agent Callback Requested)
exten => *1,2,ViciDial(${CALLERID(num)}|1|${UNIQUEID}|CALLBACK)
exten => *1,3,Hangup()

; Callback with custom time picker
exten => *2,1,NoOp(Schedule Callback with Date/Time)
exten => *2,2,Playback(please-enter-time)
exten => *2,3,Read(callback_minutes|enter-time|||5)
exten => *2,4,ViciDial(${CALLERID(num)}|1|${UNIQUEID}|CALLBACK|${callback_minutes})
exten => *2,5,Hangup()

Step 3: Database Setup for Agent Callbacks

Create callback records directly in the database. ViciDial's agent interface does this automatically, but you should understand the structure:

CREATE TABLE IF NOT EXISTS vicidial_callbacks (
  callback_id INT AUTO_INCREMENT PRIMARY KEY,
  lead_id INT NOT NULL,
  campaign_id INT NOT NULL,
  phone_number VARCHAR(20) NOT NULL,
  callback_time DATETIME NOT NULL,
  callback_date DATE NOT NULL,
  user_id INT,
  agent_id VARCHAR(20),
  status ENUM('PENDING','COMPLETED','FAILED','CANCELLED') DEFAULT 'PENDING',
  notes TEXT,
  created_date DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX (callback_time),
  INDEX (campaign_id),
  INDEX (lead_id),
  FOREIGN KEY (lead_id) REFERENCES vicidial_list(lead_id)
);

Insert a test callback record:

INSERT INTO vicidial_callbacks 
(lead_id, campaign_id, phone_number, callback_time, callback_date, agent_id, status, notes)
VALUES 
(1001, 1, '5551234567', DATE_ADD(NOW(), INTERVAL 2 DAY), CURDATE() + INTERVAL 2 DAY, 'AGENT001', 'PENDING', 'Customer requested callback Friday morning');

-- Verify insertion
SELECT * FROM vicidial_callbacks WHERE lead_id = 1001;

Step 4: Agent-Side Configuration

Agents access callbacks via the main call screen at /agc/vicidial.php. When an agent clicks Schedule Callback:

  1. A popup appears requesting callback time
  2. Agent selects date and time (respecting time zone)
  3. Lead is marked with callback disposition
  4. Record inserted into vicidial_callbacks

To ensure agents see the callback button, verify their user privileges:

SELECT user_id, vicidial_login, modify_callbackcallback_time 
FROM vicidial_users 
WHERE vicidial_login = 'AGENT001';

The user must have modify_callback set to Y:

UPDATE vicidial_users 
SET modify_callback = 'Y' 
WHERE vicidial_login = 'AGENT001';

Step 5: Test Agent Callback

  1. Log in as an agent
  2. Receive or dial a test call
  3. Click Schedule Callback button on call screen
  4. Enter callback date/time (e.g., +2 hours)
  5. Click Confirm
  6. Agent screen should show "CALLBACK SCHEDULED"
  7. Check database:
SELECT * FROM vicidial_callbacks 
WHERE agent_id = 'AGENT001' 
ORDER BY created_date DESC LIMIT 1;

Automatic Callback Configuration

Step 1: Configure Disposition Rules

Automatic callbacks trigger based on call dispositions. Configure this in Admin > Dispositions.

Map dispositions to auto-callback actions:

Disposition Auto-Callback Wait Time Notes
NO ANSWER YES 30 minutes Retry quickly
BUSY YES 1 hour Customer busy
NOT INTERESTED NO Don't retry
CALLBACK REQUESTED YES 24 hours Customer availability
ANSWERING MACHINE YES 48 hours Wait before retry

Database configuration:

SELECT disposition_id, disposition, auto_callback_enabled, auto_callback_delay_minutes 
FROM vicidial_dispositions 
WHERE campaign_id = 1;

Update disposition for auto-callback:

UPDATE vicidial_dispositions 
SET auto_callback_enabled = 'Y', auto_callback_delay_minutes = 30 
WHERE disposition = 'NO ANSWER' AND campaign_id = 1;

Step 2: Configure Auto-Callback Script

ViciDial uses scheduled scripts to process callbacks. Edit or create /usr/share/astguiclient/process_callbacks.pl:

#!/usr/bin/perl
# Process Auto-Callbacks - Run every 15 minutes via cron

use strict;
use DBI;
use DateTime;
use DateTime::TimeZone;

my $dsn = 'DBI:mysql:asterisk:localhost';
my $user = 'root';
my $pass = 'PASSWORD';
my $dbh = DBI->connect($dsn, $user, $pass) or die "Cannot connect: $DBI::errstr";

# Fetch pending callbacks due now
my $sql = qq{
    SELECT cb.callback_id, cb.lead_id, cb.phone_number, 
           cb.callback_time, vl.customer_timezone, vl.campaign_id
    FROM vicidial_callbacks cb
    JOIN vicidial_list vl ON cb.lead_id = vl.lead_id
    WHERE cb.status = 'PENDING'
    AND cb.callback_time <= NOW()
    AND cb.callback_time > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
};

my $sth = $dbh->prepare($sql);
$sth->execute();

while (my $row = $sth->fetchrow_hashref()) {
    my $callback_id = $row->{callback_id};
    my $lead_id = $row->{lead_id};
    my $phone = $row->{phone_number};
    my $campaign_id = $row->{campaign_id};
    
    # Insert into outbound queue
    my $insert_sql = qq{
        INSERT INTO vicidial_log 
        (lead_id, phone_number, campaign_id, status, call_date, 
         user, source_id, callback_id)
        VALUES (?, ?, ?, 'QUEUE', NOW(), 'AUTO-CALLBACK', 'CALLBACK', ?)
    };
    
    my $insert_sth = $dbh->prepare($insert_sql);
    $insert_sth->execute($lead_id, $phone, $campaign_id, $callback_id);
    
    # Mark callback as processed
    my $update_sql = qq{
        UPDATE vicidial_callbacks 
        SET status = 'COMPLETED' 
        WHERE callback_id = ?
    };
    my $update_sth = $dbh->prepare($update_sql);
    $update_sth->execute($callback_id);
    
    print "Processed callback ID: $callback_id for lead $lead_id\n";
}

$sth->finish();
$dbh->disconnect();
exit(0);

Make executable:

chmod +x /usr/share/astguiclient/process_callbacks.pl
chown asterisk:asterisk /usr/share/astguiclient/process_callbacks.pl

Step 3: Schedule Callback Processing via Cron

Add to /etc/cron.d/astguiclient:

# Process auto-callbacks every 15 minutes
*/15 * * * * asterisk /usr/share/astguiclient/process_callbacks.pl >> /var/log/asterisk/callbacks.log 2>&1

# Alternative: every 5 minutes for more responsive system
*/5 * * * * asterisk /usr/share/astguiclient/process_callbacks.pl >> /var/log/asterisk/callbacks.log 2>&1

Restart cron:

systemctl restart cron
# or
systemctl restart crond

Step 4: Configure Asterisk for Callback Delivery

Edit /etc/asterisk/extensions-vicidial.conf to add callback delivery context:

[callback-delivery]
; Callback context - called by ViciDial dialer
exten => s,1,NoOp(Callback delivery for ${CALLBACK_LEAD_ID})
exten => s,2,Set(CALLERID(name)=Your Company)
exten => s,3,Dial(SIP/YOUR_TRUNK/${CALLBACK_PHONE},45,gU(callback-hangup^s^1))
exten => s,4,Hangup()

; Track callback result
[callback-hangup]
exten => s,1,NoOp(Callback hangup - disposition: ${DIALSTATUS})
exten => s,2,NoOp(Returning to IVR/Agent)
exten => s,3,Return()

Reload Asterisk:

asterisk -rx "dialplan reload"

Step 5: Configure Callback Time Window & DNC

Prevent callbacks outside business hours:

-- Set campaign callback hours: 8am-6pm
UPDATE vicidial_campaigns 
SET callback_start_hour = 8, 
    callback_end_hour = 18,
    callback_respect_timezone = 'Y'
WHERE campaign_id = 1;

-- Verify
SELECT campaign_id, callback_start_hour, callback_end_hour, 
       callback_respect_timezone 
FROM vicidial_campaigns 
WHERE campaign_id = 1;

Add DNC (Do Not Call) exceptions for callbacks:

-- Ensure callbacks can override DNC
UPDATE vicidial_campaigns 
SET callback_ignore_dnc = 'Y' 
WHERE campaign_id = 1;

Callback Delivery & Queuing

Step 1: Configure Callback Queue

ViciDial uses a dedicated callback queue. Configure in /etc/asterisk/queues.conf:

[callback-queue]
strategy = ringall
timeout = 45
retry = 3
weight = 0
autopause = no
announce-frequency = 0
announce-holdtime = no
maxlen = 500

Step 2: Callback Outbound Route

Configure outbound routing for callbacks. In /etc/asterisk/extensions-vicidial.conf:

[vicidial-callback-out]
exten => _X.,1,NoOp(Callback outbound route)
exten => _X.,2,Set(CALLBACK_MODE=yes)
exten => _X.,3,Set(CALLERID(name)=Support Team)
exten => _X.,4,Dial(SIP/YOUR_CARRIER/${EXTEN}@carrier.com,45,gU(log-callback^s^1))
exten => _X.,5,Goto(handle-callback-fail,${EXTEN},1)
exten => _X.,6,Hangup()

[handle-callback-fail]
exten => _X.,1,NoOp(Callback failed - Status: ${DIALSTATUS})
exten => _X.,2,Set(callback_status=${DIALSTATUS})
exten => _X.,3,Hangup()

[log-callback]
exten => s,1,NoOp(Callback connected)
exten => s,2,Return()

Step 3: ViciDial Dialer Configuration

Configure the ViciDial dialer to handle callbacks. Edit /etc/asterisk/sip-vicidial.conf:

[vicidial-callback-trunk]
type=peer
host=YOUR_CARRIER_IP
username=YOUR_USERNAME
secret=YOUR_PASSWORD
context=vicidial-callback-out
dtmfmode=rfc2833
relaxdtmf=yes
qualify=yes
qualifyfreq=60
insecure=port,invite
directmedia=no

Reload SIP:

asterisk -rx "sip reload"

Callback Status Tracking

Monitor Callback Queue

asterisk -rx "queue show callback-queue"

Expected output:

callback-queue has 5 calls (max unlimited) in 'ringall' strategy
No members are available
  (1 call waiting)

Query Callback Status

Real-time callback status:

SELECT 
    cb.callback_id,
    cb.lead_id,
    vl.phone_number,
    cb.callback_time,
    cb.status,
    cb.agent_id,
    vl.campaign_id
FROM vicidial_callbacks cb
JOIN vicidial_list vl ON cb.lead_id = vl.lead_id
WHERE cb.status = 'PENDING'
AND cb.callback_time <= NOW()
ORDER BY cb.callback_time ASC;

Callback completion report:

SELECT 
    DATE(cb.created_date) as callback_date,
    COUNT(*) as total_callbacks,
    SUM(CASE WHEN cb.status = 'COMPLETED' THEN 1 ELSE 0 END) as completed,
    SUM(CASE WHEN cb.status = 'FAILED' THEN 1 ELSE 0 END) as failed,
    SUM(CASE WHEN cb.status = 'PENDING' THEN 1 ELSE 0 END) as pending,
    ROUND(100 * SUM(CASE WHEN cb.status = 'COMPLETED' THEN 1 ELSE 0 END) / COUNT(*), 2) as completion_rate
FROM vicidial_callbacks cb
GROUP BY DATE(cb.created_date)
ORDER BY callback_date DESC;

Update Callback Status Manually

If a callback fails and needs rescheduling:

UPDATE vicidial_callbacks 
SET callback_time = DATE_ADD(NOW(), INTERVAL 24 HOUR),
    status = 'PENDING'
WHERE callback_id = 42;

Cancel a callback:

UPDATE vicidial_callbacks 
SET status = 'CANCELLED'
WHERE callback_id = 42;

Advanced Configuration

Time Zone-Aware Callbacks

ViciDial respects customer time zones for callback scheduling. Configure in campaign:

UPDATE vicidial_campaigns 
SET callback_respect_timezone = 'Y' 
WHERE campaign_id = 1;

Set customer timezone in vicidial_list:

UPDATE vicidial_list 
SET customer_timezone = 'America/Chicago' 
WHERE lead_id = 1001;

Supported time zones (see /usr/share/zoneinfo/):

Callback with Voicemail Fallback

Configure fallback to voicemail if callback fails:

[vicidial-callback-out]
exten => _X.,1,NoOp(Callback with voicemail fallback)
exten => _X.,2,Set(CALLERID(name)=Support)
exten => _X.,3,Dial(SIP/TRUNK/${EXTEN},45,gU(callback-hangup^s^1))
exten => _X.,4,Goto(voicemail-fallback,${EXTEN},1)
exten => _X.,5,Hangup()

[voicemail-fallback]
exten => _X.,1,NoOp(Callback failed, attempting voicemail)
exten => _X.,2,VoiceMail(${EXTEN}@default)
exten => _X.,3,Hangup()

Callback with IVR Menu

Route callbacks through an IVR before agent delivery:

[callback-ivr]
exten => s,1,NoOp(Callback IVR)
exten => s,2,Playback(welcome-callback)
exten => s,3,Set(TIMEOUT(digit)=5)
exten => s,4,Set(TIMEOUT(response)=10)
exten => s,5,Read(menu_choice|please-enter-department,1,,3)
exten => s,6,Goto(route-callback,${menu_choice},1)
exten => s,7,Hangup()

[route-callback]
exten => 1,1,Queue(sales-callbacks)
exten => 2,1,Queue(support-callbacks)
exten => 3,1,Queue(billing-callbacks)
exten => i,1,Playback(invalid)
exten => i,2,Goto(callback-ivr,s,1)

Restrict Callback Frequency

Prevent callback storms by tracking attempt frequency:

SELECT 
    lead_id, 
    phone_number,
    COUNT(*) as callback_count,
    MAX(created_date) as last_callback
FROM vicidial_callbacks
WHERE created_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY lead_id
HAVING callback_count > 5;

Block leads with excessive callbacks:

UPDATE vicidial_list 
SET status = 'TEMP_BLOCK' 
WHERE lead_id IN (
    SELECT lead_id FROM vicidial_callbacks
    WHERE created_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
    GROUP BY lead_id
    HAVING COUNT(*) > 3
);

Troubleshooting

Callbacks Not Triggering

Symptom: Callbacks remain in PENDING status past their scheduled time.

Solution:

  1. Verify cron job is running:
ps aux | grep process_callbacks.pl
  1. Check cron logs:
grep process_callbacks /var/log/syslog
# or
tail -50 /var/log/asterisk/callbacks.log
  1. Verify script has correct permissions:
ls -la /usr/share/astguiclient/process_callbacks.pl
# Should show: -rwxr-xr-x asterisk:asterisk
  1. Manually test the script:
sudo -u asterisk /usr/share/astguiclient/process_callbacks.pl
echo $?  # Should return 0 on success
  1. Check MySQL connectivity from callback script:
mysql -u root -p asterisk -e "SELECT COUNT(*) FROM vicidial_callbacks WHERE status='PENDING';"

Callbacks Queued But Not Dialing

Symptom: Callbacks appear in database but agents don't receive calls.

Solution:

  1. Check Asterisk dialplan loaded:
asterisk -rx "dialplan show callback-delivery"
  1. Verify SIP trunk is registered:
asterisk -rx "sip show peers"
  1. Check channel availability:
asterisk -rx "core show channels"
  1. Review Asterisk CLI logs in real-time:
asterisk -rn

Then trigger a callback and watch output.

  1. Test trunk dial directly:
asterisk -rx "channel originate SIP/TRUNK/5551234567 extension test@from-internal"

Callbacks Showing Wrong Time

Symptom: Callbacks display or execute at incorrect times.

Solution:

  1. Verify server timezone:
timedatectl status
date
  1. Verify MySQL timezone:
SELECT @@global.time_zone, @@session.time_zone;

Should show system timezone. If showing SYSTEM, set explicitly:

SET GLOBAL time_zone = 'America/Chicago';
  1. Verify ViciDial campaign timezone setting:
SELECT callback_respect_timezone, campaign_timezone 
FROM vicidial_campaigns 
WHERE campaign_id = 1;
  1. Check individual lead timezone:
SELECT lead_id, customer_timezone 
FROM vicidial_list 
WHERE lead_id = 1001;

Callback Permission Denied

Symptom: Agents click Schedule Callback but get permission error.

Solution:

  1. Verify agent user has callback permission:
SELECT user_id, vicidial_login, modify_callback, campaign_id 
FROM vicidial_users 
WHERE vicidial_login = 'AGENT001';
  1. Enable callback permission:
UPDATE vicidial_users 
SET modify_callback = 'Y' 
WHERE vicidial_login = 'AGENT001';
  1. Verify campaign allows agent callbacks:
SELECT allow_agent_callbacks 
FROM vicidial_campaigns 
WHERE campaign_id = 1;
  1. If disabled, enable:
UPDATE vicidial_campaigns 
SET allow_agent_callbacks = 'Y' 
WHERE campaign_id = 1;
  1. Reload ViciDial web interface (clear browser cache).

High Callback Failure Rate

Symptom: Many callbacks marked FAILED status.

Solution:

  1. Review failure reasons:
SELECT 
    COUNT(*) as failed_count,
    dialstatus,
    disposition
FROM vicidial_log 
WHERE source_id = 'CALLBACK'
AND call_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND status = 'FAILED'
GROUP BY dialstatus, disposition;
  1. Check trunk capacity:
asterisk -rx "core show channels brief" | wc -l
  1. Verify carrier is accepting calls:
asterisk -rx "sip show peers" | grep YOUR_TRUNK
  1. Review Asterisk full logs:
tail -200 /var/log/asterisk/full
grep -i callback /var/log/asterisk/full | tail -50
  1. Check for SIP errors (401 UNAUTHORIZED, 403 FORBIDDEN):
grep -i "^.*SIP/.*401\|^.*SIP/.*403" /var/log/asterisk/full | tail -20
  1. If carrier rejects, verify credentials:
# In /etc/asterisk/sip-vicidial.conf, check:
# - username
# - secret (password)
# - host IP
# - port (usually 5060)

Database Growing Too Large

Symptom: vicidial_callbacks table consuming excessive disk space.

Solution:

Archive old completed callbacks:

-- Create archive table
CREATE TABLE vicidial_callbacks_archive LIKE vicidial_callbacks;

-- Move callbacks older than 90 days
INSERT INTO vicidial_callbacks_archive 
SELECT * FROM vicidial_callbacks 
WHERE created_date < DATE_SUB(NOW(), INTERVAL 90 DAY)
AND status IN ('COMPLETED', 'FAILED', 'CANCELLED');

-- Delete archived records
DELETE FROM vicidial_callbacks 
WHERE created_date < DATE_SUB(NOW(), INTERVAL 90 DAY)
AND status IN ('COMPLETED', 'FAILED', 'CANCELLED');

-- Optimize table
OPTIMIZE TABLE vicidial_callbacks;

Add automatic cleanup to cron:

0 2 * * * asterisk mysql -u root -p PASSWORD asterisk -e "DELETE FROM vicidial_callbacks WHERE created_date < DATE_SUB(NOW(), INTERVAL 120 DAY) AND status IN ('COMPLETED','FAILED','CANCELLED'); OPTIMIZE TABLE vicidial_callbacks;" >> /var/log/asterisk/cleanup.log 2>&1

Summary

ViciDial callbacks—both agent-initiated and automatic—significantly improve contact rates and customer satisfaction when properly configured. This guide covered:

Key Takeaways:

  1. Agent Callbacks allow agents to schedule follow-up calls with customers at their preferred times, improving conversions and reducing contact resistance.

  2. Auto-Callbacks automatically retry failed calls based on disposition rules, maximizing lead recovery without manual agent intervention.

  3. Database structure centers on the vicidial_callbacks table, with records linked to leads, campaigns, and agents for comprehensive tracking.

  4. Asterisk dialplan delivers callbacks through dedicated contexts, supporting SIP trunks, time-zone awareness, and voicemail fallback.

  5. Cron-based processing triggers callbacks at scheduled times, requiring proper permissions and MySQL connectivity to function reliably.

  6. Troubleshooting focuses on verifying permissions, database records, cron execution, timezone settings, and carrier connectivity.

Best Practices:

Next Steps:

Proper callback configuration transforms ViciDial from a simple dialer into an intelligent lead management system that works 24/7 on behalf of your agents.

Need expert help with your setup?

VoIP infrastructure consulting, AI voice agent integration, monitoring stacks, scaling — I've done it all in production.

Get a Free Consultation