← All Tutorials

How to Set Up ViciDial Scheduled Callbacks — Agent & Campaign Level

ViciDial Administration Intermediate 15 min read #95

Master both agent-initiated and campaign-level scheduled callbacks in ViciDial, including database configuration, Asterisk dialplan setup, and production troubleshooting.

Prerequisites

Before implementing scheduled callbacks, ensure you have:

Understanding ViciDial Callback Types

ViciDial supports two primary callback mechanisms:

Agent-Level Scheduled Callbacks

Callbacks initiated by agents during live calls via the agent screen (/agc/vicidial.php). The agent sets a callback time, and the system attempts to reach the lead at the scheduled time.

Campaign-Level Scheduled Callbacks

Callbacks triggered automatically by campaign rules, IVR results, or system events. These run on a larger scale and respect campaign-wide settings.

Both types store data in the vicidial_log and vicidial_closer_log tables but follow different processing workflows.

Section 1: Database Configuration for Callbacks

Step 1.1: Verify Core Tables Exist

Log into your ViciDial MySQL database and confirm callback-related tables:

mysql -u root -p asterisk -e "SHOW TABLES LIKE '%vicidial%';" | grep -E "(log|callback)"

You should see tables including:

Step 1.2: Create or Verify Vicidial_Callbacks Table

If the vicidial_callbacks table doesn't exist, create it:

CREATE TABLE `vicidial_callbacks` (
  `callback_id` int(11) NOT NULL AUTO_INCREMENT,
  `lead_id` int(11) NOT NULL,
  `campaign_id` varchar(8) NOT NULL,
  `callback_time` datetime NOT NULL,
  `callback_type` varchar(10) DEFAULT 'AGENT',
  `user_id` varchar(20) DEFAULT NULL,
  `phone_number` varchar(20) NOT NULL,
  `notes` text,
  `status` varchar(20) DEFAULT 'PENDING',
  `created_date` datetime DEFAULT CURRENT_TIMESTAMP,
  `processed_date` datetime DEFAULT NULL,
  `attempt_count` int(11) DEFAULT 0,
  `max_attempts` int(11) DEFAULT 3,
  PRIMARY KEY (`callback_id`),
  KEY `idx_campaign_time` (`campaign_id`, `callback_time`),
  KEY `idx_lead_campaign` (`lead_id`, `campaign_id`),
  KEY `idx_status` (`status`),
  KEY `idx_callback_time` (`callback_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Step 1.3: Add Callback Fields to Vicidial_List

Ensure the vicidial_list table has callback-related columns:

ALTER TABLE `vicidial_list` ADD COLUMN IF NOT EXISTS `callback_date` datetime DEFAULT NULL;
ALTER TABLE `vicidial_list` ADD COLUMN IF NOT EXISTS `callback_type` varchar(10) DEFAULT 'AGENT';
ALTER TABLE `vicidial_list` ADD COLUMN IF NOT EXISTS `last_callback_attempt` datetime DEFAULT NULL;

CREATE INDEX idx_callback_date ON vicidial_list(callback_date);

Step 1.4: Extend Vicidial_Closer_Log for Callback Tracking

Add callback metadata to agent notes table:

ALTER TABLE `vicidial_closer_log` ADD COLUMN IF NOT EXISTS `scheduled_callback` datetime DEFAULT NULL;
ALTER TABLE `vicidial_closer_log` ADD COLUMN IF NOT EXISTS `callback_phone` varchar(20) DEFAULT NULL;
ALTER TABLE `vicidial_closer_log` ADD COLUMN IF NOT EXISTS `callback_notes` text;

CREATE INDEX idx_scheduled_callback ON vicidial_closer_log(scheduled_callback);

Section 2: Campaign-Level Callback Configuration

Step 2.1: Access Campaign Settings

Log into the ViciDial admin panel:

https://YOUR_VICIDIAL_IP/vicidial/admin.php

Navigate to: Admin → Manage Campaigns

Select your target campaign and scroll to the "Callback Settings" section.

Step 2.2: Configure Campaign Callback Parameters

Set the following fields:

Parameter Value Description
Callback Enabled YES Activates callback feature for the campaign
Callback Attempt Limit 3 Maximum retry attempts
Callback Time Window 09:00-17:00 Hours when callbacks should be attempted
Callback Interval 300 Seconds between retry attempts (300 = 5 min)
Callback Lead Status CBHOLD Lead status after callback is scheduled
Callback Dial Timeout 45 Seconds to wait for answer before hanging up

Step 2.3: Update Campaign in Database

Alternatively, update directly via SQL:

UPDATE vicidial_campaigns 
SET 
  use_callbacks = 'Y',
  callback_attempts = 3,
  callback_interval = 300,
  callback_leadstatus = 'CBHOLD'
WHERE campaign_id = 'YOUR_CAMPAIGN_ID';

Verify the update:

SELECT campaign_id, use_callbacks, callback_attempts, callback_interval, callback_leadstatus 
FROM vicidial_campaigns 
WHERE campaign_id = 'YOUR_CAMPAIGN_ID';

Section 3: Agent-Level Callback Setup via Agent Screen

Step 3.1: Understanding the Agent Screen Callback Button

When an agent is on a live call in /agc/vicidial.php, they can click the "Schedule Callback" button. This triggers a JavaScript modal for entering callback details.

Step 3.2: Modify Agent Screen for Callback Display

Edit the agent application configuration file:

sudo nano /var/www/html/agc/vicidial.php

Locate the callback button section (typically around line 800-1200) and ensure this code block exists:

function schedule_callback_form() {
  var callback_time = prompt("Enter callback time (YYYY-MM-DD HH:MM:SS):", new Date().toISOString().slice(0, 19));
  
  if (callback_time) {
    var callback_phone = prompt("Callback phone number:", document.getElementById('phone_number').value);
    var callback_notes = prompt("Callback notes:", "");
    
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/agc/schedule_callback.php", true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.onload = function() {
      if (xhr.status === 200) {
        alert("Callback scheduled successfully!");
        log_callback_in_notes(callback_time, callback_notes);
      }
    };
    xhr.send("lead_id=" + encodeURIComponent(lead_id) + 
             "&callback_time=" + encodeURIComponent(callback_time) + 
             "&callback_phone=" + encodeURIComponent(callback_phone) + 
             "&callback_notes=" + encodeURIComponent(callback_notes) +
             "&campaign_id=" + encodeURIComponent(campaign_id) +
             "&user_id=" + encodeURIComponent(user_id));
  }
}

Step 3.3: Create Callback Processing PHP Script

Create a new file for handling agent callback submissions:

sudo nano /var/www/html/agc/schedule_callback.php

Add the following code:

<?php
// ViciDial Agent Callback Submission Handler
error_reporting(E_ALL);
ini_set('display_errors', 0);

// Security: Verify session
session_start();
if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    echo json_encode(['error' => 'Unauthorized']);
    exit;
}

// Database connection
$DB_host = 'localhost';
$DB_user = 'vicidial';
$DB_pass = 'VICIDIAL_DB_PASSWORD'; // Change this
$DB_name = 'asterisk';

$mysqli = new mysqli($DB_host, $DB_user, $DB_pass, $DB_name);
if ($mysqli->connect_error) {
    http_response_code(500);
    echo json_encode(['error' => 'Database connection failed']);
    exit;
}

// Input validation
$lead_id = intval($_POST['lead_id']);
$campaign_id = $mysqli->real_escape_string($_POST['campaign_id']);
$callback_time = $mysqli->real_escape_string($_POST['callback_time']);
$callback_phone = preg_replace('/[^0-9]/', '', $_POST['callback_phone']);
$callback_notes = $mysqli->real_escape_string($_POST['callback_notes']);
$user_id = $mysqli->real_escape_string($_SESSION['user_id']);

// Validate callback time is in the future
$callback_timestamp = strtotime($callback_time);
if ($callback_timestamp <= time()) {
    http_response_code(400);
    echo json_encode(['error' => 'Callback time must be in the future']);
    exit;
}

// Insert into vicidial_callbacks table
$query = "INSERT INTO vicidial_callbacks 
          (lead_id, campaign_id, callback_time, callback_type, user_id, phone_number, notes, status) 
          VALUES 
          ($lead_id, '$campaign_id', '$callback_time', 'AGENT', '$user_id', '$callback_phone', '$callback_notes', 'PENDING')";

if ($mysqli->query($query)) {
    // Update vicidial_list to mark callback pending
    $update_query = "UPDATE vicidial_list 
                    SET callback_date = '$callback_time', 
                        callback_type = 'AGENT',
                        last_callback_attempt = NOW()
                    WHERE lead_id = $lead_id";
    
    $mysqli->query($update_query);
    
    // Log to closer log for agent reference
    $closer_query = "INSERT INTO vicidial_closer_log 
                    (lead_id, campaign_id, call_date, user_id, scheduled_callback, callback_phone, callback_notes) 
                    VALUES 
                    ($lead_id, '$campaign_id', NOW(), '$user_id', '$callback_time', '$callback_phone', '$callback_notes')";
    
    $mysqli->query($closer_query);
    
    http_response_code(200);
    echo json_encode(['success' => true, 'callback_id' => $mysqli->insert_id]);
} else {
    http_response_code(500);
    echo json_encode(['error' => $mysqli->error]);
}

$mysqli->close();
?>

Section 4: Asterisk Dialplan Configuration

Step 4.1: Create Callback Processing Extension

Edit your Asterisk extensions configuration:

sudo nano /etc/asterisk/extensions-vicidial.conf

Add a dedicated context for callback processing:

[callback-outbound]
exten => _.,1,NoOp(ViciDial Callback Processing - Leg: ${CALLLEG})
 same => n,Set(TIMEOUT(absolute)=3600)
 same => n,Log(NOTICE, Processing callback for lead: ${lead_id})
 same => n,Dial(SIP/${callback_phone}@callback-carrier,45,tTkKwW)
 same => n,GoTo(callback-result-${DIALSTATUS},1)

exten => callback-result-ANSWER,1,NoOp(Callback answered successfully)
 same => n,Set(DB(vicidial/callback/${lead_id})=ANSWERED)
 same => n,Set(callback_status=ANSWERED)
 same => n,Hangup()

exten => callback-result-BUSY,1,NoOp(Callback line busy - will retry)
 same => n,Set(DB(vicidial/callback/${lead_id})=BUSY)
 same => n,Hangup()

exten => callback-result-NOANSWER,1,NoOp(No answer on callback)
 same => n,Set(DB(vicidial/callback/${lead_id})=NOANSWER)
 same => n,Hangup()

exten => callback-result-CHANUNAVAIL,1,NoOp(Channel unavailable for callback)
 same => n,Set(DB(vicidial/callback/${lead_id})=FAILED)
 same => n,Hangup()

exten => h,1,NoOp(Hangup handler for callback)
 same => n,Log(NOTICE, Callback hangup - Status: ${callback_status})

Step 4.2: Update SIP Configuration for Callback Carrier

Edit the SIP configuration to allow callback outbound routing:

sudo nano /etc/asterisk/sip-vicidial.conf

Add or modify the callback carrier section:

[callback-carrier]
type=peer
host=YOUR_CARRIER_IP_OR_HOSTNAME
port=5060
secret=YOUR_SIP_SECRET
username=YOUR_SIP_USERNAME
fromuser=YOUR_SIP_USERNAME
fromdomain=YOUR_SIP_DOMAIN
dtmfmode=rfc2833
context=from-internal
disallow=all
allow=ulaw
allow=gsm
canreinvite=no
nat=force_rport,comedia
qualify=yes
qualifyfreq=60

Step 4.3: Reload Asterisk Configuration

After editing configuration files, reload Asterisk:

sudo asterisk -rx "sip reload"
sudo asterisk -rx "dialplan reload"
sudo asterisk -rx "core reload"

Verify the changes:

sudo asterisk -rx "sip show peers" | grep callback
sudo asterisk -rx "dialplan show callback-outbound"

Section 5: Cron Jobs and Scheduled Processing

Step 5.1: Create Callback Processing Script

Create the main callback processor script:

sudo nano /usr/share/astguiclient/vicidial_callbacks_processor.pl

Add the following Perl script:

#!/usr/bin/perl
# ViciDial Scheduled Callbacks Processor
# Runs every 60 seconds via cron
# Usage: /usr/share/astguiclient/vicidial_callbacks_processor.pl

use strict;
use warnings;
use DBI;
use DateTime;
use DateTime::Format::MySQL;

my $DB_host = 'localhost';
my $DB_user = 'vicidial';
my $DB_pass = 'VICIDIAL_DB_PASSWORD'; # Change this
my $DB_name = 'asterisk';

my $dbh = DBI->connect("DBI:mysql:$DB_name:$DB_host", $DB_user, $DB_pass)
    or die "Cannot connect to database: $DBI::errstr";

my $now = DateTime->now();
my $now_mysql = DateTime::Format::MySQL->format_datetime($now);

# Query pending callbacks due within the next 60 seconds
my $query = "SELECT cb.callback_id, cb.lead_id, cb.campaign_id, cb.phone_number, 
                    cb.callback_time, cb.attempt_count, cb.max_attempts, vl.phone_number as lead_phone
             FROM vicidial_callbacks cb
             JOIN vicidial_list vl ON cb.lead_id = vl.lead_id
             WHERE cb.status = 'PENDING'
             AND cb.callback_time <= DATE_ADD(NOW(), INTERVAL 60 SECOND)
             AND cb.callback_time > NOW()
             AND cb.attempt_count < cb.max_attempts
             ORDER BY cb.callback_time ASC
             LIMIT 50";

my $sth = $dbh->prepare($query);
$sth->execute() or die "Query failed: $DBI::errstr";

my $callback_count = 0;

while (my $row = $sth->fetchrow_hashref()) {
    my $callback_id = $row->{callback_id};
    my $lead_id = $row->{lead_id};
    my $campaign_id = $row->{campaign_id};
    my $phone_number = $row->{phone_number};
    my $attempt_count = $row->{attempt_count};
    my $max_attempts = $row->{max_attempts};
    
    # Execute Asterisk originate command
    my $asterisk_cmd = qq{
        Originate(SIP/$phone_number\@callback-carrier,exten,callback-outbound,$phone_number,1,,)
    };
    
    system("/usr/sbin/asterisk -rx \"$asterisk_cmd\"");
    
    # Update attempt count
    my $update_query = "UPDATE vicidial_callbacks 
                       SET attempt_count = attempt_count + 1,
                           processed_date = NOW()
                       WHERE callback_id = $callback_id";
    
    $dbh->do($update_query) or warn "Failed to update callback: $DBI::errstr";
    
    # Log the callback attempt
    system("logger -t vicidial_callbacks 'Callback initiated: lead_id=$lead_id, phone=$phone_number, attempt=" . ($attempt_count + 1) . "/$max_attempts'");
    
    $callback_count++;
}

# Mark expired callbacks as EXPIRED
my $expire_query = "UPDATE vicidial_callbacks 
                   SET status = 'EXPIRED'
                   WHERE status = 'PENDING'
                   AND callback_time < DATE_SUB(NOW(), INTERVAL 24 HOUR)";

$dbh->do($expire_query);

# Mark max-attempt callbacks as FAILED
my $fail_query = "UPDATE vicidial_callbacks 
                 SET status = 'FAILED'
                 WHERE status = 'PENDING'
                 AND attempt_count >= max_attempts";

$dbh->do($fail_query);

$dbh->disconnect();

exit(0);

Make the script executable:

sudo chmod 755 /usr/share/astguiclient/vicidial_callbacks_processor.pl

Step 5.2: Add Cron Job for Callback Processing

Edit the crontab:

sudo crontab -e

Add the following line to run callback processor every minute:

* * * * * /usr/share/astguiclient/vicidial_callbacks_processor.pl >> /var/log/asterisk/vicidial_callbacks.log 2>&1

Step 5.3: Monitor Callback Processing Logs

Create a log file and monitor it:

sudo touch /var/log/asterisk/vicidial_callbacks.log
sudo chown asterisk:asterisk /var/log/asterisk/vicidial_callbacks.log
sudo chmod 644 /var/log/asterisk/vicidial_callbacks.log

Monitor in real-time:

tail -f /var/log/asterisk/vicidial_callbacks.log

Section 6: Testing and Verification

Step 6.1: Manual Callback Test via SQL

Insert a test callback 5 minutes in the future:

INSERT INTO vicidial_callbacks 
(lead_id, campaign_id, callback_time, callback_type, user_id, phone_number, notes, status, max_attempts) 
VALUES 
(12345, 'TESTCAMP', DATE_ADD(NOW(), INTERVAL 5 MINUTE), 'AGENT', 'testuser', '15551234567', 'Test callback', 'PENDING', 3);

Verify the insert:

SELECT callback_id, lead_id, callback_time, status, attempt_count 
FROM vicidial_callbacks 
WHERE lead_id = 12345;

Step 6.2: Monitor Asterisk During Callback

In one terminal, watch Asterisk logs:

tail -f /var/log/asterisk/messages | grep -i callback

In another terminal, trigger the callback processor manually:

/usr/share/astguiclient/vicidial_callbacks_processor.pl

Step 6.3: Verify Callback Status in Database

Check the status after callback attempt:

SELECT callback_id, lead_id, status, attempt_count, processed_date 
FROM vicidial_callbacks 
WHERE lead_id = 12345
ORDER BY processed_date DESC;

Step 6.4: Check Agent Screen Integration

Log into the agent screen at /agc/vicidial.php with a test agent account. During a test call, click "Schedule Callback" and verify:

  1. Modal appears with time and phone fields
  2. Callback is inserted into database
  3. Entry appears in agent's call notes
  4. Callback time displays correctly in lead history

Section 7: Production Optimization

Step 7.1: Database Indexing for Performance

Add strategic indexes to improve callback query performance:

CREATE INDEX idx_callbacks_status_time ON vicidial_callbacks(status, callback_time);
CREATE INDEX idx_callbacks_campaign_time ON vicidial_callbacks(campaign_id, callback_time);
CREATE INDEX idx_callbacks_lead ON vicidial_callbacks(lead_id);
CREATE INDEX idx_list_callback ON vicidial_list(callback_date, callback_type);

Step 7.2: Rate Limiting for Callback Dialout

Modify the callback processor to respect concurrent call limits:

# Add to vicidial_callbacks_processor.pl
my $max_concurrent_callbacks = 10;
my $concurrent_query = "SELECT COUNT(*) as active_callbacks 
                       FROM asterisk.vicidial_callbacks 
                       WHERE status = 'PROCESSING'";
my $concurrent_sth = $dbh->prepare($concurrent_query);
$concurrent_sth->execute();
my $concurrent = $concurrent_sth->fetchrow_hashref()->{active_callbacks};

if ($concurrent >= $max_concurrent_callbacks) {
    exit(0); # Skip this run if limit reached
}

Step 7.3: Callback Time Zone Handling

Ensure callbacks respect agent/lead time zones:

ALTER TABLE vicidial_callbacks ADD COLUMN timezone_offset varchar(10) DEFAULT '+00:00';

UPDATE vicidial_callbacks cb
JOIN vicidial_list vl ON cb.lead_id = vl.lead_id
SET cb.timezone_offset = vl.state -- Assumes state field maps to timezone
WHERE cb.timezone_offset = '+00:00';

Update callback processor to account for timezone:

my $callback_time_utc = $row->{callback_time};
my $tz_offset = $row->{timezone_offset};
my $callback_time_local = DateTime->from_epoch(epoch => time(), time_zone => $tz_offset);

Section 8: Troubleshooting

Issue 1: Callbacks Not Triggering

Symptom: Callbacks scheduled but never attempt to dial.

Diagnosis:

# Check if cron job is running
sudo ps aux | grep vicidial_callbacks_processor
# Check cron logs
sudo grep CRON /var/log/syslog | tail -20

Solution:

Ensure the cron job is active and check file permissions:

sudo crontab -l | grep vicidial_callbacks_processor
sudo ls -la /usr/share/astguiclient/vicidial_callbacks_processor.pl

If missing, re-add to crontab as shown in Section 5.2.

Issue 2: Database Connection Errors

Symptom: Cannot connect to database errors in callback logs.

Diagnosis:

Verify database credentials in the Perl script match your installation:

sudo grep -i "DB_pass\|DB_user" /usr/share/astguiclient/*.pl | head -5

Solution:

Update credentials in /usr/share/astguiclient/vicidial_callbacks_processor.pl:

sudo nano /usr/share/astguiclient/vicidial_callbacks_processor.pl
# Edit the DB connection variables at the top

Test connection manually:

mysql -h localhost -u vicidial -p asterisk -e "SELECT COUNT(*) FROM vicidial_callbacks;"

Issue 3: Asterisk Originate Command Fails

Symptom: Callbacks attempted but SIP channels fail to originate.

Diagnosis:

Check Asterisk SIP peer status:

sudo asterisk -rx "sip show peers" | grep callback-carrier
sudo asterisk -rx "core show channels"

Check for SIP authentication errors:

sudo asterisk -rx "sip set debug on"
# Wait for callback to attempt
tail -f /var/log/asterisk/messages | grep -i "401\|403\|authentication"

Solution:

Verify SIP credentials in /etc/asterisk/sip-vicidial.conf:

sudo asterisk -rx "sip show peer callback-carrier"

Test SIP connectivity:

sudo sipsak -U -s sip:YOUR_CARRIER_IP

Issue 4: Callback Phone Numbers Formatted Incorrectly

Symptom: Callbacks dial incomplete or malformed numbers.

Diagnosis:

Check stored phone numbers in database:

SELECT callback_id, phone_number, LENGTH(phone_number) as len 
FROM vicidial_callbacks 
WHERE status IN ('PENDING', 'PROCESSING') 
LIMIT 5;

Solution:

Standardize phone number formatting in the schedule_callback.php script:

function format_phone($phone) {
    $digits = preg_replace('/[^0-9]/', '', $phone);
    if (strlen($digits) == 10) {
        return '1' . $digits; // Add country code if missing
    }
    return $digits;
}

$callback_phone = format_phone($_POST['callback_phone']);

Issue 5: High Database Query Load from Callback Processing

Symptom: Database CPU spikes every minute during callback processor runs.

Diagnosis:

Check query execution time:

sudo mysql -u vicidial -p -e "SET GLOBAL log_queries_not_using_indexes=ON; SET GLOBAL long_query_time=1;"
tail -f /var/log/mysql/slow.log | grep callback

Solution:

Add batch processing and limit queries in the Perl script:

# Modify the query in vicidial_callbacks_processor.pl to add LIMIT
LIMIT 50  # Process max 50 callbacks per run

Increase cron interval from 1 minute to 5 minutes if needed:

*/5 * * * * /usr/share/astguiclient/vicidial_callbacks_processor.pl

Issue 6: Agents Report Callback Button Not Working

Symptom: Agent screen loads but callback scheduling fails silently.

Diagnosis:

Check browser console for JavaScript errors:

  1. Open agent screen in browser
  2. Press F12 (Developer Tools)
  3. Go to Console tab
  4. Attempt to schedule callback
  5. Look for POST request failures

Check PHP error logs:

tail -f /var/log/apache2/error.log | grep schedule_callback
sudo grep -i "php error\|warning" /var/log/apache2/error.log | tail -20

Solution:

Verify schedule_callback.php exists and has correct permissions:

sudo ls -la /var/www/html/agc/schedule_callback.php
sudo chown www-data:www-data /var/www/html/agc/schedule_callback.php

Check if session is properly initialized in agent screen code:

grep -n "session_start" /var/www/html/agc/vicidial.php | head -3

Section 9: Monitoring and Reporting

Step 9.1: Callback Success Rate Report

Generate a callback success report:

SELECT 
    DATE(callback_time) as callback_date,
    campaign_id,
    COUNT(*) as total_callbacks,
    SUM(CASE WHEN status = 'ANSWERED' THEN 1 ELSE 0 END) as answered,
    SUM(CASE WHEN status = 'FAILED' THEN 1 ELSE 0 END) as failed,
    SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) as pending,
    ROUND(SUM(CASE WHEN status = 'ANSWERED' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) as success_rate_pct
FROM vicidial_callbacks
WHERE callback_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(callback_time), campaign_id
ORDER BY callback_date DESC, campaign_id;

Step 9.2: Agent Callback Activity

Track agent callback scheduling activity:

SELECT 
    user_id,
    campaign_id,
    COUNT(*) as callbacks_scheduled,
    AVG(TIMESTAMPDIFF(MINUTE, callback_time, NOW())) as avg_lead_time_minutes
FROM vicidial_callbacks
WHERE callback_type = 'AGENT'
AND created_date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY user_id, campaign_id
ORDER BY callbacks_scheduled DESC;

Summary

You now have a complete, production-ready ViciDial scheduled callback system spanning both agent-initiated and campaign-level callbacks. Here's what you've implemented:

Key Components:

Best Practices Applied:

Next Steps:

  1. Deploy in a test environment first
  2. Validate all database queries execute without errors
  3. Monitor callback success rate for 1-2 weeks
  4. Adjust retry intervals and time windows based on results
  5. Train agents on callback scheduling via /agc/vicidial.php
  6. Set up monitoring alerts for callback processor failures

Use the troubleshooting section to resolve environment-specific issues. Monitor /var/log/asterisk/vicidial_callbacks.log and Asterisk CLI output regularly to ensure callbacks are processing as expected.

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