Tools: Setting Up ViciDial Callbacks (CALLBK) — Agent & Auto - Expert Insights

Tools: Setting Up ViciDial Callbacks (CALLBK) — Agent & Auto - Expert Insights

Setting Up ViciDial Callbacks (CALLBK) — Agent & Auto

Prerequisites

Understanding ViciDial Callbacks

What Are Callbacks?

Why Callbacks Matter

Key Database Tables

Agent Callbacks Configuration

Step 1: Enable Callbacks in Campaign Settings

Step 2: Configure Agent Callback Permissions

Step 3: Database Setup for Agent Callbacks

Step 4: Agent-Side Configuration

Step 5: Test Agent Callback

Automatic Callback Configuration

Step 1: Configure Disposition Rules

Step 2: Configure Auto-Callback Script

Step 3: Schedule Callback Processing via Cron

Step 4: Configure Asterisk for Callback Delivery

Step 5: Configure Callback Time Window & DNC

Callback Delivery & Queuing

Step 1: Configure Callback Queue

Step 2: Callback Outbound Route

Step 3: ViciDial Dialer Configuration

Callback Status Tracking

Monitor Callback Queue

Query Callback Status

Update Callback Status Manually

Advanced Configuration

Time Zone-Aware Callbacks

Callback with Voicemail Fallback

Callback with IVR Menu

Restrict Callback Frequency

Troubleshooting

Callbacks Not Triggering

Callbacks Queued But Not Dialing

Callbacks Showing Wrong Time

Callback Permission Denied

High Callback Failure Rate

Database Growing Too Large

Summary Learn how to configure and manage both agent-initiated and automatic callbacks in ViciDial with practical examples, database queries, and production troubleshooting techniques. Before implementing callbacks in ViciDial, ensure you have: Verify your installation: 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: Callbacks are stored primarily in these tables: Access the ViciDial web interface and navigate to Admin > Campaigns. Open /etc/asterisk/extensions-vicidial.conf and locate the agent login context. Add callback-specific permissions: Create callback records directly in the database. ViciDial's agent interface does this automatically, but you should understand the structure: Insert a test callback record: Agents access callbacks via the main call screen at /agc/vicidial.php. When an agent clicks Schedule Callback: To ensure agents see the callback button, verify their user privileges: The user must have modify_callback set to Y: Automatic callbacks trigger based on call dispositions. Configure this in Admin > Dispositions. Map dispositions to auto-callback actions: Database configuration: Update disposition for auto-callback: ViciDial uses scheduled scripts to process callbacks. Edit or create /usr/share/astguiclient/process_callbacks.pl: Add to /etc/cron.d/astguiclient: Edit /etc/asterisk/extensions-vicidial.conf to add callback delivery context: Prevent callbacks outside business hours: Add DNC (Do Not Call) exceptions for callbacks: ViciDial uses a dedicated callback queue. Configure in /etc/asterisk/queues.conf: Configure outbound routing for callbacks. In /etc/asterisk/extensions-vicidial.conf: Configure the ViciDial dialer to handle callbacks. Edit /etc/asterisk/sip-vicidial.conf: Real-time callback status: Callback completion report: If a callback fails and needs rescheduling: ViciDial respects customer time zones for callback scheduling. Configure in campaign: Set customer timezone in vicidial_list: Supported time zones (see /usr/share/zoneinfo/): Configure fallback to voicemail if callback fails: Route callbacks through an IVR before agent delivery: Prevent callback storms by tracking attempt frequency: Block leads with excessive callbacks: Symptom: Callbacks remain in PENDING status past their scheduled time. Symptom: Callbacks appear in database but agents don't receive calls. Then trigger a callback and watch output. Symptom: Callbacks display or execute at incorrect times. Should show system timezone. If showing SYSTEM, set explicitly: Symptom: Agents click Schedule Callback but get permission error. Symptom: Many callbacks marked FAILED status. Symptom: vicidial_callbacks table consuming excessive disk space. Archive old completed callbacks: Add automatic cleanup to cron: ViciDial callbacks—both agent-initiated and automatic—significantly improve contact rates and customer satisfaction when properly configured. This guide covered: Agent Callbacks allow agents to schedule follow-up calls with customers at their preferred times, improving conversions and reducing contact resistance. Auto-Callbacks automatically retry failed calls based on disposition rules, maximizing lead recovery without manual agent intervention. Database structure centers on the vicidial_callbacks table, with records linked to leads, campaigns, and agents for comprehensive tracking. Asterisk dialplan delivers callbacks through dedicated contexts, supporting SIP trunks, time-zone awareness, and voicemail fallback. Cron-based processing triggers callbacks at scheduled times, requiring proper permissions and MySQL connectivity to function reliably. Troubleshooting focuses on verifying permissions, database records, cron execution, timezone settings, and carrier connectivity. Proper callback configuration transforms ViciDial from a simple dialer into an intelligent lead management system that works 24/7 on behalf of your agents. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

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 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 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 SELECT campaign_id, campaign_name, allow_agent_callbacks, callback_time_window FROM vicidial_campaigns WHERE campaign_name = 'YOUR_CAMPAIGN'; SELECT campaign_id, campaign_name, allow_agent_callbacks, callback_time_window FROM vicidial_campaigns WHERE campaign_name = 'YOUR_CAMPAIGN'; SELECT campaign_id, campaign_name, allow_agent_callbacks, callback_time_window FROM vicidial_campaigns WHERE campaign_name = 'YOUR_CAMPAIGN'; campaign_id | campaign_name | allow_agent_callbacks | callback_time_window 1 | Sales | Y | 1440 campaign_id | campaign_name | allow_agent_callbacks | callback_time_window 1 | Sales | Y | 1440 campaign_id | campaign_name | allow_agent_callbacks | callback_time_window 1 | Sales | Y | 1440 [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() [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() [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() 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) ); 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) ); 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 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; 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; 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; SELECT user_id, vicidial_login, modify_callbackcallback_time FROM vicidial_users WHERE vicidial_login = 'AGENT001'; SELECT user_id, vicidial_login, modify_callbackcallback_time FROM vicidial_users WHERE vicidial_login = 'AGENT001'; SELECT user_id, vicidial_login, modify_callbackcallback_time FROM vicidial_users WHERE vicidial_login = 'AGENT001'; UPDATE vicidial_users SET modify_callback = 'Y' WHERE vicidial_login = 'AGENT001'; UPDATE vicidial_users SET modify_callback = 'Y' WHERE vicidial_login = 'AGENT001'; UPDATE vicidial_users SET modify_callback = 'Y' WHERE vicidial_login = 'AGENT001'; SELECT * FROM vicidial_callbacks WHERE agent_id = 'AGENT001' ORDER BY created_date DESC LIMIT 1; SELECT * FROM vicidial_callbacks WHERE agent_id = 'AGENT001' ORDER BY created_date DESC LIMIT 1; SELECT * FROM vicidial_callbacks WHERE agent_id = 'AGENT001' ORDER BY created_date DESC LIMIT 1; SELECT disposition_id, disposition, auto_callback_enabled, auto_callback_delay_minutes FROM vicidial_dispositions WHERE campaign_id = 1; SELECT disposition_id, disposition, auto_callback_enabled, auto_callback_delay_minutes FROM vicidial_dispositions WHERE campaign_id = 1; SELECT disposition_id, disposition, auto_callback_enabled, auto_callback_delay_minutes FROM vicidial_dispositions WHERE campaign_id = 1; UPDATE vicidial_dispositions SET auto_callback_enabled = 'Y', auto_callback_delay_minutes = 30 WHERE disposition = 'NO ANSWER' AND campaign_id = 1; UPDATE vicidial_dispositions SET auto_callback_enabled = 'Y', auto_callback_delay_minutes = 30 WHERE disposition = 'NO ANSWER' AND campaign_id = 1; UPDATE vicidial_dispositions SET auto_callback_enabled = 'Y', auto_callback_delay_minutes = 30 WHERE disposition = 'NO ANSWER' AND campaign_id = 1; #!/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); #!/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); #!/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); chmod +x /usr/share/astguiclient/process_callbacks.pl chown asterisk:asterisk /usr/share/astguiclient/process_callbacks.pl chmod +x /usr/share/astguiclient/process_callbacks.pl chown asterisk:asterisk /usr/share/astguiclient/process_callbacks.pl chmod +x /usr/share/astguiclient/process_callbacks.pl chown asterisk:asterisk /usr/share/astguiclient/process_callbacks.pl # 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 # 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 # 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 systemctl restart cron # or systemctl restart crond systemctl restart cron # or systemctl restart crond systemctl restart cron # or systemctl restart crond [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() [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() [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() asterisk -rx "dialplan reload" asterisk -rx "dialplan reload" asterisk -rx "dialplan reload" -- 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; -- 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; -- 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; -- Ensure callbacks can override DNC UPDATE vicidial_campaigns SET callback_ignore_dnc = 'Y' WHERE campaign_id = 1; -- Ensure callbacks can override DNC UPDATE vicidial_campaigns SET callback_ignore_dnc = 'Y' WHERE campaign_id = 1; -- Ensure callbacks can override DNC UPDATE vicidial_campaigns SET callback_ignore_dnc = 'Y' WHERE campaign_id = 1; [callback-queue] strategy = ringall timeout = 45 retry = 3 weight = 0 autopause = no announce-frequency = 0 announce-holdtime = no maxlen = 500 [callback-queue] strategy = ringall timeout = 45 retry = 3 weight = 0 autopause = no announce-frequency = 0 announce-holdtime = no maxlen = 500 [callback-queue] strategy = ringall timeout = 45 retry = 3 weight = 0 autopause = no announce-frequency = 0 announce-holdtime = no maxlen = 500 [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() [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() [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() [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 [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 [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 asterisk -rx "sip reload" asterisk -rx "sip reload" asterisk -rx "sip reload" asterisk -rx "queue show callback-queue" asterisk -rx "queue show callback-queue" asterisk -rx "queue show callback-queue" callback-queue has 5 calls (max unlimited) in 'ringall' strategy No members are available (1 call waiting) callback-queue has 5 calls (max unlimited) in 'ringall' strategy No members are available (1 call waiting) callback-queue has 5 calls (max unlimited) in 'ringall' strategy No members are available (1 call waiting) 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; 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; 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; 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; 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; 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 vicidial_callbacks SET callback_time = DATE_ADD(NOW(), INTERVAL 24 HOUR), status = 'PENDING' WHERE callback_id = 42; UPDATE vicidial_callbacks SET callback_time = DATE_ADD(NOW(), INTERVAL 24 HOUR), status = 'PENDING' WHERE callback_id = 42; UPDATE vicidial_callbacks SET callback_time = DATE_ADD(NOW(), INTERVAL 24 HOUR), status = 'PENDING' WHERE callback_id = 42; UPDATE vicidial_callbacks SET status = 'CANCELLED' WHERE callback_id = 42; UPDATE vicidial_callbacks SET status = 'CANCELLED' WHERE callback_id = 42; UPDATE vicidial_callbacks SET status = 'CANCELLED' WHERE callback_id = 42; UPDATE vicidial_campaigns SET callback_respect_timezone = 'Y' WHERE campaign_id = 1; UPDATE vicidial_campaigns SET callback_respect_timezone = 'Y' WHERE campaign_id = 1; UPDATE vicidial_campaigns SET callback_respect_timezone = 'Y' WHERE campaign_id = 1; UPDATE vicidial_list SET customer_timezone = 'America/Chicago' WHERE lead_id = 1001; UPDATE vicidial_list SET customer_timezone = 'America/Chicago' WHERE lead_id = 1001; UPDATE vicidial_list SET customer_timezone = 'America/Chicago' WHERE lead_id = 1001; [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() [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() [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-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) [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) [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) 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; 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; 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; 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 ); 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 ); 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 ); ps aux | grep process_callbacks.pl ps aux | grep process_callbacks.pl ps aux | grep process_callbacks.pl grep process_callbacks /var/log/syslog # or tail -50 /var/log/asterisk/callbacks.log grep process_callbacks /var/log/syslog # or tail -50 /var/log/asterisk/callbacks.log grep process_callbacks /var/log/syslog # or tail -50 /var/log/asterisk/callbacks.log ls -la /usr/share/astguiclient/process_callbacks.pl # Should show: -rwxr-xr-x asterisk:asterisk ls -la /usr/share/astguiclient/process_callbacks.pl # Should show: -rwxr-xr-x asterisk:asterisk ls -la /usr/share/astguiclient/process_callbacks.pl # Should show: -rwxr-xr-x asterisk:asterisk sudo -u asterisk /usr/share/astguiclient/process_callbacks.pl echo $? # Should return 0 on success sudo -u asterisk /usr/share/astguiclient/process_callbacks.pl echo $? # Should return 0 on success sudo -u asterisk /usr/share/astguiclient/process_callbacks.pl echo $? # Should return 0 on success mysql -u root -p asterisk -e "SELECT COUNT(*) FROM vicidial_callbacks WHERE status='PENDING';" mysql -u root -p asterisk -e "SELECT COUNT(*) FROM vicidial_callbacks WHERE status='PENDING';" mysql -u root -p asterisk -e "SELECT COUNT(*) FROM vicidial_callbacks WHERE status='PENDING';" asterisk -rx "dialplan show callback-delivery" asterisk -rx "dialplan show callback-delivery" asterisk -rx "dialplan show callback-delivery" asterisk -rx "sip show peers" asterisk -rx "sip show peers" asterisk -rx "sip show peers" asterisk -rx "core show channels" asterisk -rx "core show channels" asterisk -rx "core show channels" asterisk -rn asterisk -rn asterisk -rn asterisk -rx "channel originate SIP/TRUNK/5551234567 extension test@from-internal" asterisk -rx "channel originate SIP/TRUNK/5551234567 extension test@from-internal" asterisk -rx "channel originate SIP/TRUNK/5551234567 extension test@from-internal" timedatectl status date timedatectl status date timedatectl status date SELECT @@global.time_zone, @@session.time_zone; SELECT @@global.time_zone, @@session.time_zone; SELECT @@global.time_zone, @@session.time_zone; SET GLOBAL time_zone = 'America/Chicago'; SET GLOBAL time_zone = 'America/Chicago'; SET GLOBAL time_zone = 'America/Chicago'; SELECT callback_respect_timezone, campaign_timezone FROM vicidial_campaigns WHERE campaign_id = 1; SELECT callback_respect_timezone, campaign_timezone FROM vicidial_campaigns WHERE campaign_id = 1; SELECT callback_respect_timezone, campaign_timezone FROM vicidial_campaigns WHERE campaign_id = 1; SELECT lead_id, customer_timezone FROM vicidial_list WHERE lead_id = 1001; SELECT lead_id, customer_timezone FROM vicidial_list WHERE lead_id = 1001; SELECT lead_id, customer_timezone FROM vicidial_list WHERE lead_id = 1001; SELECT user_id, vicidial_login, modify_callback, campaign_id FROM vicidial_users WHERE vicidial_login = 'AGENT001'; SELECT user_id, vicidial_login, modify_callback, campaign_id FROM vicidial_users WHERE vicidial_login = 'AGENT001'; SELECT user_id, vicidial_login, modify_callback, campaign_id FROM vicidial_users WHERE vicidial_login = 'AGENT001'; UPDATE vicidial_users SET modify_callback = 'Y' WHERE vicidial_login = 'AGENT001'; UPDATE vicidial_users SET modify_callback = 'Y' WHERE vicidial_login = 'AGENT001'; UPDATE vicidial_users SET modify_callback = 'Y' WHERE vicidial_login = 'AGENT001'; SELECT allow_agent_callbacks FROM vicidial_campaigns WHERE campaign_id = 1; SELECT allow_agent_callbacks FROM vicidial_campaigns WHERE campaign_id = 1; SELECT allow_agent_callbacks FROM vicidial_campaigns WHERE campaign_id = 1; UPDATE vicidial_campaigns SET allow_agent_callbacks = 'Y' WHERE campaign_id = 1; UPDATE vicidial_campaigns SET allow_agent_callbacks = 'Y' WHERE campaign_id = 1; UPDATE vicidial_campaigns SET allow_agent_callbacks = 'Y' WHERE campaign_id = 1; 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; 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; 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; asterisk -rx "core show channels brief" | wc -l asterisk -rx "core show channels brief" | wc -l asterisk -rx "core show channels brief" | wc -l asterisk -rx "sip show peers" | grep YOUR_TRUNK asterisk -rx "sip show peers" | grep YOUR_TRUNK asterisk -rx "sip show peers" | grep YOUR_TRUNK tail -200 /var/log/asterisk/full grep -i callback /var/log/asterisk/full | tail -50 tail -200 /var/log/asterisk/full grep -i callback /var/log/asterisk/full | tail -50 tail -200 /var/log/asterisk/full grep -i callback /var/log/asterisk/full | tail -50 grep -i "^.*SIP/.*401\|^.*SIP/.*403" /var/log/asterisk/full | tail -20 grep -i "^.*SIP/.*401\|^.*SIP/.*403" /var/log/asterisk/full | tail -20 grep -i "^.*SIP/.*401\|^.*SIP/.*403" /var/log/asterisk/full | tail -20 # In /etc/asterisk/sip-vicidial.conf, check: # - username # - secret (password) # - host IP # - port (usually 5060) # In /etc/asterisk/sip-vicidial.conf, check: # - username # - secret (password) # - host IP # - port (usually 5060) # In /etc/asterisk/sip-vicidial.conf, check: # - username # - secret (password) # - host IP # - port (usually 5060) -- 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; -- 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; -- 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; 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 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 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 - ViciDial version 2.14.1 or higher installed and running - Root or sudo access to the ViciDial server - MySQL/MariaDB access with administrative privileges - Asterisk 13+ properly configured with SIP trunks or IAX2 - At least one active campaign set up in ViciDial - Working agent logins and active phone extensions - Proper dial permissions configured for agents - Call recording enabled (optional but recommended for compliance) - Outbound carrier/trunk verification — test a manual dial first - Agent Callbacks (CALLBK) — Agents schedule callbacks directly from the call screen - Auto Callbacks — System automatically reschedules based on lead disposition rules - Higher contact rates: Callbacks reach customers when they're available - Better agent efficiency: Agents don't waste time on busy/no-answer leads - Compliance: Schedule calls during business hours per time zone - Lead recovery: Systematically retry abandoned or incomplete interactions - Customer satisfaction: Reduces unwanted interruptions - vicidial_log — Call history with called_count, status, callback_time - vicidial_callbacks — Dedicated callback records with phone, time, agent - vicidial_carrier_log — Route tracking for callback attempts - vicidial_lists — Campaign list configuration with callback settings - Select your campaign - Scroll to Callback Settings - Set Allow Agent Callbacks to YES - Set Callback Time Window (e.g., 1 day, 7 days, 30 days) - Enable Allow Same-Day Callbacks if needed - Set Callback Dial Timeout to 45-60 seconds - Save changes - A popup appears requesting callback time - Agent selects date and time (respecting time zone) - Lead is marked with callback disposition - Record inserted into vicidial_callbacks - Log in as an agent - Receive or dial a test call - Click Schedule Callback button on call screen - Enter callback date/time (e.g., +2 hours) - Click Confirm - Agent screen should show "CALLBACK SCHEDULED" - Check database: - America/New_York - America/Chicago - America/Denver - America/Los_Angeles - America/Anchorage - Pacific/Honolulu - Verify cron job is running: - Check cron logs: - Verify script has correct permissions: - Manually test the script: - Check MySQL connectivity from callback script: - Check Asterisk dialplan loaded: - Verify SIP trunk is registered: - Check channel availability: - Review Asterisk CLI logs in real-time: - Test trunk dial directly: - Verify server timezone: - Verify MySQL timezone: - Verify ViciDial campaign timezone setting: - Check individual lead timezone: - Verify agent user has callback permission: - Enable callback permission: - Verify campaign allows agent callbacks: - If disabled, enable: - Reload ViciDial web interface (clear browser cache). - Review failure reasons: - Check trunk capacity: - Verify carrier is accepting calls: - Review Asterisk full logs: - Check for SIP errors (401 UNAUTHORIZED, 403 FORBIDDEN): - If carrier rejects, verify credentials: - Agent Callbacks allow agents to schedule follow-up calls with customers at their preferred times, improving conversions and reducing contact resistance. - Auto-Callbacks automatically retry failed calls based on disposition rules, maximizing lead recovery without manual agent intervention. - Database structure centers on the vicidial_callbacks table, with records linked to leads, campaigns, and agents for comprehensive tracking. - Asterisk dialplan delivers callbacks through dedicated contexts, supporting SIP trunks, time-zone awareness, and voicemail fallback. - Cron-based processing triggers callbacks at scheduled times, requiring proper permissions and MySQL connectivity to function reliably. - Troubleshooting focuses on verifying permissions, database records, cron execution, timezone settings, and carrier connectivity. - Always test callbacks with a single lead before enabling for entire campaign - Monitor callback completion rates weekly via SQL queries - Set reasonable callback windows (e.g., 1-30 days) to avoid lead staleness - Respect time zones—configure per-lead or per-campaign as needed - Archive old callback records monthly to maintain database performance - Document your dialplan changes and script modifications for future maintenance - Review Asterisk logs (/var/log/asterisk/full) during troubleshooting - Deploy the process_callbacks.pl script with cron scheduling - Configure callback rules for your top 2-3 dispositions - Train agents on the callback button and best practices - Monitor first week for issues via database queries and Asterisk CLI - Adjust callback timing based on success rates