DEV Community

Moisi Trungu
Moisi Trungu

Posted on • Originally published at flixzone.icu

ViciDial Lead Recycling & List Management Best Practices

ViciDial Lead Recycling & List Management Best Practices

Master advanced lead recycling strategies, implement intelligent list management workflows, and optimize your ViciDial contact center performance with production-tested configurations and real-world examples.

Prerequisites

Before implementing the strategies in this tutorial, ensure you have:

  • ViciDial 2.14+ installed and running (2.14.1 or later recommended)
  • Administrator access to the ViciDial web interface at /vicidial/admin.php
  • SSH access to the ViciDial server with sudo privileges
  • MySQL/MariaDB command-line access to the asterisk database
  • At least one active campaign configured
  • Basic understanding of ViciDial list structure and lead statuses
  • Backup of your database before implementing changes

Verify your installation:

mysql -u root -p asterisk -e "SELECT VERSION();"
asterisk -v
grep "vicidial" /etc/asterisk/extensions-vicidial.conf | head -5
Enter fullscreen mode Exit fullscreen mode

Understanding Lead Lifecycle in ViciDial

Lead recycling is the process of requeuing contacts after specific outcomes, allowing multiple attempts with different agents, strategies, or time intervals. Understanding the lead lifecycle is foundational to effective recycling.

Lead Status Codes and Their Meanings

ViciDial uses standardized status codes stored in the vicidial_log table. Key statuses relevant to recycling:

Status Code Meaning Recyclable Default Seconds
NEW Untouched lead Yes 0
CALL Active call No N/A
CALLACK Callback scheduled No User-defined
BUSY Busy signal Yes 3600
NOANSWER No answer Yes 1800
NOTCALLABLE Invalid number No 0
BADNUMBER Bad format No 0
DNC Do Not Call No 0
XFER Transfer attempted Yes 900
SALE Successful No 0
DEAD Duplicate/Dead No 0

The Recycling Decision Tree

Lead Called
    ├─ Answer
    │   ├─ Sale → No recycle
    │   ├─ Not Interested → Recycle (optional)
    │   └─ Wrong Number → No recycle (flag DNC)
    ├─ No Answer
    │   ├─ Retry ≤ 3 → Recycle NOW
    │   ├─ Retry 4-5 → Recycle in 24h
    │   └─ Retry > 5 → Archive
    └─ Busy
        ├─ Retry ≤ 2 → Recycle in 30 min
        └─ Retry > 2 → Recycle in 2h
Enter fullscreen mode Exit fullscreen mode

Database Schema for Recycling Management

Key Tables

Understanding the database structure is essential for custom recycling logic.

-- View current lead status and recycle information
SELECT 
    vl.lead_id,
    vl.phone_number,
    vl.status,
    vl.call_date,
    vl.length_in_sec,
    vl.user,
    vl.campaign_id,
    COUNT(*) as attempt_count
FROM vicidial_log vl
WHERE vl.campaign_id = 'TESTCAMP'
    AND vl.phone_number = '5551234567'
GROUP BY vl.lead_id
ORDER BY vl.call_date DESC;
Enter fullscreen mode Exit fullscreen mode

Core Recycling Tables

vicidial_list — Contains lead master records:

DESC vicidial_list;
Enter fullscreen mode Exit fullscreen mode

Key fields for recycling:

  • lead_id — Unique identifier
  • phone_number — Contact number
  • status — Current lead status
  • list_id — Associated campaign list
  • gmt_offset_now — Timezone for callback scheduling
  • created_date — Lead entry date
  • last_local_call_time — Last contact attempt timestamp

vicidial_log — Call attempt history:

DESC vicidial_log;
Enter fullscreen mode Exit fullscreen mode

Key fields:

  • lead_id — Links to vicidial_list
  • call_date — Attempt timestamp
  • status — Outcome status
  • length_in_sec — Duration
  • user — Agent identifier
  • campaign_id — Campaign code
  • phone_number — Dialed number
  • call_type — INBOUND/OUTBOUND

vicidial_campaign — Campaign configuration:

DESC vicidial_campaign;
Enter fullscreen mode Exit fullscreen mode

Critical recycling fields:

  • campaign_id — Unique identifier
  • lead_recycle_method — Recycling strategy
  • reuse_trunk_level — Trunk reuse behavior
  • caller_id_method — Caller ID assignment

Configuring Lead Recycling Strategies

Method 1: Campaign-Level Automatic Recycling

Access the campaign configuration and enable recycling globally:

# Log into ViciDial web interface
# Navigate to: Admin → Campaigns → [Your Campaign]
# Look for these settings:
Enter fullscreen mode Exit fullscreen mode

In the database, verify campaign recycling configuration:

SELECT 
    campaign_id,
    lead_recycle_method,
    auto_dial_level,
     voicemail_ext,
    hopper_level
FROM vicidial_campaign
WHERE campaign_id = 'TESTCAMP';
Enter fullscreen mode Exit fullscreen mode

Update recycling method:

UPDATE vicidial_campaign
SET lead_recycle_method = 1
WHERE campaign_id = 'TESTCAMP';
Enter fullscreen mode Exit fullscreen mode

Recycling methods:

  • 0 — No recycling
  • 1 — Standard recycling (uses vicidial_closer_log status settings)
  • 2 — Advanced recycling (requires custom scripting)

Method 2: Status-Based Recycling Rules

Create granular recycling behavior through status configuration in vicidial_closer_log:

-- View current status recycling rules
SELECT 
    campaign_id,
    status,
    recyclable,
    rank
FROM vicidial_closer_log
WHERE campaign_id = 'TESTCAMP'
ORDER BY status;
Enter fullscreen mode Exit fullscreen mode

This table determines which statuses trigger recycling. Insert recycling rules:

-- Recycle NOANSWER leads after 30 minutes
INSERT INTO vicidial_closer_log 
(campaign_id, status, recyclable, seconds_to_next_call, rank)
VALUES 
('TESTCAMP', 'NOANSWER', 'Y', 1800, 10)
ON DUPLICATE KEY UPDATE 
    recyclable = 'Y',
    seconds_to_next_call = 1800;

-- Recycle BUSY after 15 minutes
INSERT INTO vicidial_closer_log 
(campaign_id, status, recyclable, seconds_to_next_call, rank)
VALUES 
('TESTCAMP', 'BUSY', 'Y', 900, 20)
ON DUPLICATE KEY UPDATE 
    recyclable = 'Y',
    seconds_to_next_call = 900;

-- Do NOT recycle DNCPROSPECT
INSERT INTO vicidial_closer_log 
(campaign_id, status, recyclable, seconds_to_next_call, rank)
VALUES 
('TESTCAMP', 'DNCPROSPECT', 'N', 0, 5)
ON DUPLICATE KEY UPDATE 
    recyclable = 'N',
    seconds_to_next_call = 0;
Enter fullscreen mode Exit fullscreen mode

Method 3: Custom Recycling with AGI Scripts

For advanced recycling logic, implement AGI (Asterisk Gateway Interface) scripts. Create a custom recycling script:

cat > /usr/share/astguiclient/agi-bin/recycle_custom.agi << 'EOF'
#!/usr/bin/perl
# Custom Lead Recycling AGI Script for ViciDial
# Place in: /usr/share/astguiclient/agi-bin/

use strict;
use warnings;
use Asterisk::AGI;
use DBI;

my $agi = new Asterisk::AGI;
my $lead_id = $agi->get_variable("lead_id");
my $campaign_id = $agi->get_variable("campaign_id");
my $status = $agi->get_variable("call_status");
my $attempt_count = $agi->get_variable("attempt_count");

# Connect to database
my $dbh = DBI->connect(
    "DBI:mysql:asterisk:localhost",
    "asteriskuser",
    "DBPASSWORD",
    {AutoCommit => 1}
) or die "Cannot connect: $DBI::errstr";

# Recycling logic
my $recycle = 0;
my $recycle_seconds = 0;

if ($status eq 'NOANSWER') {
    if ($attempt_count < 3) {
        $recycle = 1;
        $recycle_seconds = 1800;  # 30 minutes
    } elsif ($attempt_count < 5) {
        $recycle = 1;
        $recycle_seconds = 86400; # 24 hours
    }
} elsif ($status eq 'BUSY') {
    if ($attempt_count < 4) {
        $recycle = 1;
        $recycle_seconds = 900;   # 15 minutes
    }
} elsif ($status eq 'XFER' && $attempt_count < 2) {
    $recycle = 1;
    $recycle_seconds = 3600;     # 1 hour
}

# Update vicidial_list with recycle status
if ($recycle) {
    my $sth = $dbh->prepare(
        "UPDATE vicidial_list 
         SET status = ?, 
             last_local_call_time = DATE_ADD(NOW(), INTERVAL ? SECOND)
         WHERE lead_id = ?"
    );
    $sth->execute('RECYCLE', $recycle_seconds, $lead_id);
    $agi->set_variable("recycled", 1);
}

$dbh->disconnect();
exit(0);
EOF

chmod +x /usr/share/astguiclient/agi-bin/recycle_custom.agi
Enter fullscreen mode Exit fullscreen mode

Call this AGI from your dialplan:

; Add to /etc/asterisk/extensions-vicidial.conf
[vicidial-handler]
exten => s,1,AGI(agi-bin/recycle_custom.agi)
exten => s,n,Hangup()
Enter fullscreen mode Exit fullscreen mode

Implementing Intelligent List Management

List Segmentation Strategy

Separate leads into logical groups for targeted recycling:

-- Create a custom list segmentation view
CREATE OR REPLACE VIEW lead_segments AS
SELECT 
    vl.lead_id,
    vl.phone_number,
    vl.list_id,
    vl.campaign_id,
    CASE 
        WHEN COUNT(*) <= 1 THEN 'FRESH'
        WHEN COUNT(*) BETWEEN 2 AND 3 THEN 'WARM'
        WHEN COUNT(*) > 3 THEN 'HOT'
    END as segment,
    COUNT(*) as attempt_count,
    MAX(vlog.call_date) as last_attempt
FROM vicidial_list vl
LEFT JOIN vicidial_log vlog ON vl.lead_id = vlog.lead_id
GROUP BY vl.lead_id;

-- Query segmented leads
SELECT * FROM lead_segments WHERE campaign_id = 'TESTCAMP' AND segment = 'FRESH';
Enter fullscreen mode Exit fullscreen mode

Automatic List Purging and Archive

Prevent list bloat by archiving old, unrecyclable leads:

-- Archive DEAD leads older than 60 days
CREATE PROCEDURE archive_old_leads()
BEGIN
    INSERT INTO vicidial_list_archive
    SELECT * FROM vicidial_list
    WHERE status IN ('DEAD', 'NOTCALLABLE', 'BADNUMBER')
        AND DATE_SUB(NOW(), INTERVAL 60 DAY) > created_date;

    DELETE FROM vicidial_list
    WHERE status IN ('DEAD', 'NOTCALLABLE', 'BADNUMBER')
        AND DATE_SUB(NOW(), INTERVAL 60 DAY) > created_date;
END;

-- Execute once daily via cron
CALL archive_old_leads();
Enter fullscreen mode Exit fullscreen mode

Add to crontab:

# Run daily at 2 AM
0 2 * * * mysql -u root -p'PASSWORD' asterisk -e "CALL archive_old_leads;"
Enter fullscreen mode Exit fullscreen mode

Dynamic Callback Scheduling

Implement time-zone aware callbacks for optimal contact:

-- Schedule callbacks during business hours (9 AM - 6 PM local time)
UPDATE vicidial_list vl
SET status = 'CALLBACK',
    last_local_call_time = DATE_ADD(
        NOW(),
        INTERVAL (
            (9 - HOUR(CONVERT_TZ(NOW(), 'UTC', 'America/Chicago')))
            + IF(HOUR(CONVERT_TZ(NOW(), 'UTC', 'America/Chicago')) >= 18, 24, 0)
        ) HOUR
    )
WHERE lead_id IN (
    SELECT lead_id FROM vicidial_log 
    WHERE status = 'CALLBACK' 
        AND DATE_SUB(NOW(), INTERVAL 24 HOUR) < call_date
)
AND campaign_id = 'TESTCAMP';
Enter fullscreen mode Exit fullscreen mode

Monitoring and Reporting Lead Recycling

Real-Time Recycling Dashboard Query

Monitor active recycling in real-time:

SELECT 
    COUNT(DISTINCT lead_id) as recyclable_leads,
    SUM(CASE WHEN status = 'RECYCLE' THEN 1 ELSE 0 END) as pending_recycle,
    SUM(CASE WHEN status = 'CALLBACK' THEN 1 ELSE 0 END) as scheduled_callbacks,
    campaign_id,
    MAX(last_local_call_time) as next_available
FROM vicidial_list
WHERE campaign_id = 'TESTCAMP'
    AND status IN ('RECYCLE', 'CALLBACK', 'BUSY', 'NOANSWER')
GROUP BY campaign_id;
Enter fullscreen mode Exit fullscreen mode

Recycling Effectiveness Metrics

Measure recycling performance:

-- Calculate recycle-to-sale conversion rate
SELECT 
    vlog.campaign_id,
    COUNT(DISTINCT vlog.lead_id) as total_attempts,
    COUNT(DISTINCT CASE WHEN vlog.status = 'SALE' THEN vlog.lead_id END) as sales,
    ROUND(
        (COUNT(DISTINCT CASE WHEN vlog.status = 'SALE' THEN vlog.lead_id END) 
        / COUNT(DISTINCT vlog.lead_id) * 100), 2
    ) as conversion_rate_pct,
    AVG(CASE WHEN vlog.status = 'SALE' THEN vlog.length_in_sec ELSE NULL END) as avg_sale_duration
FROM vicidial_log vlog
WHERE vlog.campaign_id = 'TESTCAMP'
    AND vlog.call_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY vlog.campaign_id;
Enter fullscreen mode Exit fullscreen mode

Lead Aging Report

Identify leads requiring urgency or removal:

-- List leads by age and status
SELECT 
    vl.lead_id,
    vl.phone_number,
    vl.status,
    DATEDIFF(NOW(), vl.created_date) as days_in_system,
    COUNT(vlog.lead_id) as total_attempts,
    MAX(vlog.call_date) as last_attempt,
    DATEDIFF(NOW(), MAX(vlog.call_date)) as days_since_last_attempt
FROM vicidial_list vl
LEFT JOIN vicidial_log vlog ON vl.lead_id = vlog.lead_id
WHERE vl.campaign_id = 'TESTCAMP'
GROUP BY vl.lead_id
HAVING days_in_system > 90
ORDER BY last_attempt DESC;
Enter fullscreen mode Exit fullscreen mode

Configuring Recycling via CLI Commands

Bulk Recycle Operations

Recycle entire batches of leads using command-line operations:

# Recycle all NOANSWER leads from a specific campaign
mysql -u asteriskuser -p'PASSWORD' asterisk << EOF
UPDATE vicidial_list
SET status = 'RECYCLE',
    last_local_call_time = DATE_ADD(NOW(), INTERVAL 1800 SECOND)
WHERE campaign_id = 'TESTCAMP'
    AND status = 'NOANSWER'
    AND DATE_SUB(NOW(), INTERVAL 6 HOUR) > last_local_call_time;
EOF
Enter fullscreen mode Exit fullscreen mode

Check Recycling Queue Status

Verify pending recycled leads:

# Connect to Asterisk CLI and check lead queue
asterisk -rx "agi show" | grep -i recycle

# Monitor ViciDial dialer process
ps aux | grep -E "vici_|astguiclient"

# Check for recycling script errors
tail -50 /var/log/asterisk/messages | grep -i recycle
Enter fullscreen mode Exit fullscreen mode

Force Immediate Recycle

Manually trigger recycle for specific leads:

# SSH to ViciDial server
# Run manual recycle operation
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"UPDATE vicidial_list 
 SET status = 'RECYCLE', 
     last_local_call_time = NOW() 
 WHERE lead_id = 12345 
 AND campaign_id = 'TESTCAMP';"

# Verify update
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT lead_id, phone_number, status, last_local_call_time 
 FROM vicidial_list 
 WHERE lead_id = 12345;"
Enter fullscreen mode Exit fullscreen mode

Advanced: Time-Zone Aware Recycling

Handle multi-zone campaigns properly:

-- Create time-zone aware recycling procedure
DELIMITER $$

CREATE PROCEDURE recycle_by_timezone(
    IN p_campaign_id VARCHAR(20)
)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE v_lead_id INT;
    DECLARE v_phone VARCHAR(20);
    DECLARE v_gmt_offset INT;
    DECLARE v_local_hour INT;

    DECLARE cur CURSOR FOR
        SELECT lead_id, phone_number, gmt_offset_now
        FROM vicidial_list
        WHERE campaign_id = p_campaign_id
            AND status IN ('NOANSWER', 'BUSY');

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur;

    read_loop: LOOP
        FETCH cur INTO v_lead_id, v_phone, v_gmt_offset;
        IF done THEN
            LEAVE read_loop;
        END IF;

        -- Calculate local hour for this lead's timezone
        SET v_local_hour = HOUR(DATE_ADD(NOW(), INTERVAL v_gmt_offset HOUR));

        -- Only recycle during business hours (9 AM - 6 PM)
        IF v_local_hour >= 9 AND v_local_hour < 18 THEN
            UPDATE vicidial_list
            SET status = 'RECYCLE',
                last_local_call_time = NOW()
            WHERE lead_id = v_lead_id;
        END IF;
    END LOOP;

    CLOSE cur;
END$$

DELIMITER ;

-- Execute
CALL recycle_by_timezone('TESTCAMP');
Enter fullscreen mode Exit fullscreen mode

Best Practices and Optimization

Lead Fatigue Prevention

Avoid over-calling contacts:

-- Set maximum attempts per lead
ALTER TABLE vicidial_list ADD COLUMN max_attempts INT DEFAULT 5;

-- Prevent recycling beyond maximum
UPDATE vicidial_list
SET status = 'MAXED'
WHERE (SELECT COUNT(*) FROM vicidial_log 
       WHERE lead_id = vicidial_list.lead_id) >= max_attempts
AND campaign_id = 'TESTCAMP';
Enter fullscreen mode Exit fullscreen mode

Quality Assurance

Monitor recycling quality metrics:

#!/bin/bash
# Script: check_recycle_health.sh

CAMPAIGN="TESTCAMP"
THRESHOLD=10  # Alert if recycle queue exceeds 10%

TOTAL_LEADS=$(mysql -N -u asteriskuser -p'PASSWORD' asterisk \
    -e "SELECT COUNT(*) FROM vicidial_list WHERE campaign_id='$CAMPAIGN';")

RECYCLE_LEADS=$(mysql -N -u asteriskuser -p'PASSWORD' asterisk \
    -e "SELECT COUNT(*) FROM vicidial_list WHERE campaign_id='$CAMPAIGN' AND status='RECYCLE';")

PERCENTAGE=$((RECYCLE_LEADS * 100 / TOTAL_LEADS))

echo "Campaign: $CAMPAIGN"
echo "Total Leads: $TOTAL_LEADS"
echo "Recycled Leads: $RECYCLE_LEADS"
echo "Recycle Queue: $PERCENTAGE%"

if [ $PERCENTAGE -gt $THRESHOLD ]; then
    echo "WARNING: Recycle queue exceeds $THRESHOLD% threshold!"
fi
Enter fullscreen mode Exit fullscreen mode

Run via cron:

0 * * * * /usr/local/bin/check_recycle_health.sh | mail -s "Recycle Health Alert" admin@company.com
Enter fullscreen mode Exit fullscreen mode

Database Maintenance

Regular optimization for recycling performance:

#!/bin/bash
# Add to crontab - Run weekly

mysql -u asteriskuser -p'PASSWORD' asterisk << EOF
-- Optimize tables for query performance
OPTIMIZE TABLE vicidial_list;
OPTIMIZE TABLE vicidial_log;
OPTIMIZE TABLE vicidial_closer_log;

-- Rebuild indexes
ANALYZE TABLE vicidial_list;
ANALYZE TABLE vicidial_log;

-- Check for corrupted records
CHECK TABLE vicidial_list;
CHECK TABLE vicidial_log;
EOF
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Lead Recycling Issues

Issue: Leads Not Recycling

Symptom: Status remains unchanged after expected recycle interval.

Diagnosis:

# 1. Check if campaign recycling is enabled
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT campaign_id, lead_recycle_method FROM vicidial_campaign WHERE campaign_id='TESTCAMP';"

# Expected output: lead_recycle_method should be 1 or higher

# 2. Verify vicidial_dialer process is running
ps aux | grep vicidial | grep -v grep

# 3. Check for errors in logs
tail -100 /var/log/asterisk/messages | grep -i recycle
grep "ERROR\|recycle\|RECYCLE" /var/log/asterisk/messages | tail -20
Enter fullscreen mode Exit fullscreen mode

Solution:

# Enable recycling in campaign
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"UPDATE vicidial_campaign SET lead_recycle_method = 1 WHERE campaign_id = 'TESTCAMP';"

# Restart vicidial_dialer service
sudo service vicidial_dialer restart

# Monitor service startup
tail -f /var/log/asterisk/messages | grep vicidial
Enter fullscreen mode Exit fullscreen mode

Issue: Recycled Leads Not Dialing

Symptom: RECYCLE status leads remain in queue but don't get dialed.

Diagnosis:

# Check hopper queue depth
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT COUNT(*) as hopper_count FROM vicidial_hopper WHERE campaign_id='TESTCAMP';"

# View next leads to be dialed
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT lead_id, phone_number, status, last_local_call_time 
 FROM vicidial_list 
 WHERE campaign_id = 'TESTCAMP' 
 AND status = 'RECYCLE' 
 ORDER BY last_local_call_time 
 LIMIT 10;"

# Check if leads meet recycle time requirement
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT lead_id, phone_number, status, 
 TIMESTAMPDIFF(SECOND, NOW(), last_local_call_time) as seconds_until_recycle
 FROM vicidial_list 
 WHERE campaign_id = 'TESTCAMP' 
 AND status = 'RECYCLE' 
 LIMIT 5;"
Enter fullscreen mode Exit fullscreen mode

Solution:

# If leads show negative seconds_until_recycle, they're ready
# Check if hopper is full (capacity issue)

mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT * FROM vicidial_campaign WHERE campaign_id = 'TESTCAMP';" | grep -E "hopper_level|lead_recycle"

# If hopper is too small, update it
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"UPDATE vicidial_campaign SET hopper_level = 200 WHERE campaign_id = 'TESTCAMP';"

# Force immediate hopper fill
asterisk -rx "vicidial hopper $CAMPAIGN"
Enter fullscreen mode Exit fullscreen mode

Issue: Excessive Recycling / Lead Fatigue

Symptom: Leads dialed repeatedly in short intervals, agents complaining about repetition.

Diagnosis:

# Identify heavily recycled leads
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"SELECT 
    vl.lead_id, 
    vl.phone_number, 
    COUNT(*) as call_attempts,
    MIN(vlog.call_date) as first_call,
    MAX(vlog.call_date) as last_call,
    TIMESTAMPDIFF(DAY, MIN(vlog.call_date), MAX(vlog.call_date)) as days_span
 FROM vicidial_list vl
 INNER JOIN vicidial_log vlog ON vl.lead_id = vlog.lead_id
 WHERE vl.campaign_id = 'TESTCAMP'
 GROUP BY vl.lead_id
 HAVING call_attempts > 10
 ORDER BY call_attempts DESC
 LIMIT 20;"
Enter fullscreen mode Exit fullscreen mode

Solution:

# Implement attempt limits in closer_log
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"INSERT INTO vicidial_closer_log 
 (campaign_id, status, recyclable, seconds_to_next_call, rank)
 VALUES ('TESTCAMP', 'NOANSWER', 'Y', 3600, 10)
 ON DUPLICATE KEY UPDATE seconds_to_next_call = 3600;"

# Archive over-attempted leads
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"UPDATE vicidial_list vl
 SET status = 'ARCHIVE'
 WHERE vl.campaign_id = 'TESTCAMP'
 AND (SELECT COUNT(*) FROM vicidial_log WHERE lead_id = vl.lead_id) > 8;"
Enter fullscreen mode Exit fullscreen mode

Issue: Recycling Causing Database Lock

Symptom: Database queries slow, connection timeout errors, agents report lag.

Diagnosis:

# Check for long-running queries
mysql -u asteriskuser -p'PASSWORD' asterisk -e "SHOW PROCESSLIST;" | grep -i UPDATE

# Monitor table locks
mysqladmin -u asteriskuser -p'PASSWORD' -i 2 processlist | grep -E "UPDATE|LOCK"

# Check innodb status
mysql -u asteriskuser -p'PASSWORD' asterisk -e "SHOW ENGINE INNODB STATUS\G" | grep -A 20 "LATEST DETECTED DEADLOCK"
Enter fullscreen mode Exit fullscreen mode

Solution:

# Implement batching in recycling procedures
mysql -u asteriskuser -p'PASSWORD' asterisk << EOF
-- Update in smaller chunks to reduce lock time
SET @batch_size = 100;
UPDATE vicidial_list vl
SET status = 'RECYCLE'
WHERE campaign_id = 'TESTCAMP'
    AND status = 'NOANSWER'
    AND lead_id IN (
        SELECT lead_id FROM (
            SELECT lead_id FROM vicidial_list vl2
            WHERE campaign_id = 'TESTCAMP' AND status = 'NOANSWER'
            LIMIT @batch_size
        ) tmp
    );
EOF

# Add index for faster recycling queries
mysql -u asteriskuser -p'PASSWORD' asterisk -e \
"ALTER TABLE vicidial_list ADD INDEX idx_recycle (campaign_id, status, last_local_call_time);"
Enter fullscreen mode Exit fullscreen mode

Summary

ViciDial lead recycling is a sophisticated system requiring careful planning and ongoing optimization. The key takeaways:

  1. Understand Status Flow: Different statuses recycle at different intervals; configure per vicidial_closer_log.

  2. Database-Driven Configuration: Recycling behavior lives in vicidial_list, vicidial_log, and vicidial_campaign tables—customize via SQL.

  3. Time-Zone Awareness: Use gmt_offset_now and last_local_call_time to avoid off-hours contact.

  4. Prevent Lead Fatigue: Set attempt limits, monitor call frequency, and archive over-contacted leads.

  5. Monitor Performance: Track recycle-to-sale conversion rates, queue depths, and recycling effectiveness.

  6. Automate Maintenance: Use cron jobs and stored procedures to archive old leads, optimize tables, and maintain database health.

  7. Test Before Deploying: Always back up your database and test recycling changes in a staging environment first.

  8. Document Custom Logic: If implementing AGI scripts or complex procedures, maintain clear documentation for future maintenance.

Effective lead recycling balances outreach frequency with lead quality, agent productivity, and customer satisfaction. Use the SQL queries and scripts provided here as templates, adjusting parameters for your specific campaign requirements.

Top comments (0)