Permanently remove leads, recycle phone numbers, and reset list state without data loss or orphaned records.
List management in ViciDial involves three distinct operations: deactivation (marking leads inactive while keeping records), reset (clearing call history and status to retry a campaign), and deletion (removing records from the database). Each operation interacts differently with the call log, agent notes, and list state. Performing them incorrectly leaves orphaned records, duplicate dials, or blocks legitimate callback attempts. This tutorial covers the safe procedures for all three, the database tables involved, and recovery methods when operations go wrong.
Tested on ViciDial 2.14+ (SVN 3555+), Asterisk 16/18, MariaDB 10.5+
Prerequisites
- SSH access to the ViciDial server with sudo or root privileges
- MySQL/MariaDB command-line client or phpMyAdmin access
- Understanding of ViciDial list structure (lead_id, phone_number, status, vendor_id, list_id)
- Current database backups in place (daily automated backups are minimum)
- Familiarity with ViciDial admin panel at /vicidial/admin.php
- Agents logged out during bulk list operations to prevent lock contention
Understanding ViciDial list status codes
Before performing any operation, you must understand how ViciDial stores lead state. A lead is defined by its record in the vicidial_list table. Its disposition is stored in the status field using two-letter codes.
Common status codes:
NEW: Never dialedACTIVE: In use, currently being workedDEAD: Leads no longer called (bad number, do-not-call, etc.)DNC: Do-not-call list matchXFER: Transferred to another listCALLBACK: Scheduled callbackSALE: Completed transactionDISPO: Disposition set by agentDROP: Dropped call by systemINBOUND: Inbound callXFERSALE: Transferred and sold
The status field is case-sensitive. Always verify actual status values in your database before filtering:
mysql -u asterisk -p asterisk -e "SELECT DISTINCT status FROM vicidial_list LIMIT 20;"
Deactivating leads without deletion
Deactivation marks leads inactive while preserving all call history, notes, and audit trails. This is the safest approach for removing leads from active campaigns without losing data.
Why deactivate instead of delete?
Deleted records cannot be recovered without a database restore. Deactivated records remain queryable for reports, compliance audits, and callback analysis. If a client later disputes a call or charges back, the record still exists. Regulatory bodies (FCC, TCPA) expect records to persist for the call history they represent.
Mark a list as inactive via the web interface
Navigate to /vicidial/admin.php and log in as administrator.
- Go to Lists menu > List Management
- Find the list by list_id or description
- Click Edit
- Set "Status" to "INACTIVE"
- Save
This changes only the list record, not individual leads. Active agents still see the list in their dropdown but cannot dial it.
Deactivate specific leads by phone number
To deactivate a subset of leads (e.g., complaints, do-not-calls), use the database directly. Back up first:
mysqldump -u asterisk -p asterisk vicidial_list > /backup/vicidial_list_$(date +%Y%m%d_%H%M%S).sql
Then update leads matching phone numbers:
UPDATE vicidial_list
SET status = 'DEAD'
WHERE phone_number IN (
'5551234567',
'5559876543'
)
AND list_id = 101;
Verify the count before executing:
SELECT COUNT(*) FROM vicidial_list
WHERE phone_number IN ('5551234567', '5559876543')
AND list_id = 101;
Deactivate all leads in a list
If you want to halt a campaign entirely, mark all leads in that list as DEAD:
UPDATE vicidial_list
SET status = 'DEAD'
WHERE list_id = 101;
Check the impact first:
SELECT COUNT(*) FROM vicidial_list
WHERE list_id = 101
AND status NOT IN ('DEAD', 'XFER', 'DNC');
This counts leads still available for dialing. If the number is high and you want to pause rather than kill the list, set list status to INACTIVE instead.
Deactivate leads with a specific disposition or age
Deactivate leads that were last dialed before a certain date:
UPDATE vicidial_list
SET status = 'DEAD'
WHERE list_id = 101
AND last_call_time < DATE_SUB(NOW(), INTERVAL 90 DAY)
AND status = 'CALLBACK';
This removes stale callbacks. Verify nothing has already called them recently:
SELECT list_id, phone_number, status, last_call_time, last_local_call_time
FROM vicidial_list
WHERE list_id = 101
AND last_call_time < DATE_SUB(NOW(), INTERVAL 90 DAY)
LIMIT 5;
Resetting leads for re-dialing
Resetting clears the call history and disposition from a lead while keeping the phone number and list assignment. This is used when you want agents to re-attempt leads in a campaign.
Why reset instead of deactivate?
Deactivation removes leads from the active pool permanently. Reset returns them to NEW or CALLBACK status so they can be dialed again. Common reasons: campaign rule changes, new agent skill set, or a manual call from the client asking for a second attempt.
Reset all leads in a list to NEW
Backup the database first:
mysqldump -u asterisk -p asterisk vicidial_list vicidial_log > /backup/before_reset_$(date +%Y%m%d_%H%M%S).sql
Then reset:
UPDATE vicidial_list
SET status = 'NEW',
last_call_time = NULL,
last_local_call_time = NULL,
country_code = NULL
WHERE list_id = 101;
This removes all disposition and call timing but preserves the phone number, owner, and custom fields. Leads appear as if never dialed.
The script will run quickly on lists under 100,000 leads. Large lists may lock the table for 30+ seconds. Run during off-hours or stagger the reset:
UPDATE vicidial_list
SET status = 'NEW',
last_call_time = NULL,
last_local_call_time = NULL
WHERE list_id = 101
AND lead_id >= 1000000
AND lead_id < 1010000
LIMIT 10000;
Repeat with different lead_id ranges to spread the load.
Reset only leads with a specific status
Reset callbacks that were scheduled more than 7 days ago:
UPDATE vicidial_list
SET status = 'NEW',
last_call_time = NOW(),
last_local_call_time = NOW()
WHERE list_id = 101
AND status = 'CALLBACK'
AND callback_datetime < DATE_SUB(NOW(), INTERVAL 7 DAY);
This prevents agents from seeing ancient callbacks in their list.
Reset a sample of leads for AB testing
To test a script change or new IVR on a subset:
UPDATE vicidial_list
SET status = 'NEW',
last_call_time = NULL
WHERE list_id = 101
AND lead_id % 100 < 10
AND status IN ('DEAD', 'DISPO');
The modulo operator (lead_id % 100 < 10) selects roughly 10% of leads evenly distributed. Verify before running:
SELECT COUNT(*) FROM vicidial_list
WHERE list_id = 101
AND lead_id % 100 < 10;
Clear notes and custom fields during reset
To fully reset without preserving notes:
UPDATE vicidial_list
SET status = 'NEW',
comments = '',
last_call_time = NULL,
last_local_call_time = NULL,
user = NULL
WHERE list_id = 101;
This also clears the user field (agent who last handled it). Use with caution on compliance-sensitive lists because comments may hold required audit information.
Deleting leads permanently
Deletion removes records from the database entirely. This is irreversible without a database restore, so perform only after backups and verification.
When to delete leads
Delete only when:
- Leads were imported in error (duplicate import, wrong campaign data)
- Testing data that should never have been in production
- Client explicitly requests deletion for privacy reasons
- Leads are known duplicates within the same list
- Data is corrupt or unrecoverable
Do not delete leads from completed campaigns. Archive by deactivation instead.
Delete leads by phone number
mysqldump -u asterisk -p asterisk vicidial_list vicidial_log > /backup/before_delete_$(date +%Y%m%d_%H%M%S).sql
Then verify count:
SELECT COUNT(*) FROM vicidial_list
WHERE phone_number IN ('5551234567', '5559876543')
AND list_id = 101;
If the count matches expectation:
DELETE FROM vicidial_list
WHERE phone_number IN ('5551234567', '5559876543')
AND list_id = 101;
Delete duplicate leads within a list
Find duplicates first:
SELECT phone_number, COUNT(*) as count
FROM vicidial_list
WHERE list_id = 101
GROUP BY phone_number
HAVING count > 1
ORDER BY count DESC
LIMIT 10;
To keep only the oldest (first-imported) copy and delete newer duplicates:
DELETE vl FROM vicidial_list vl
WHERE list_id = 101
AND lead_id NOT IN (
SELECT MIN(lead_id)
FROM vicidial_list
WHERE list_id = 101
GROUP BY phone_number
);
This is a safe way to remove accidental duplicates. Verify the count:
SELECT COUNT(*) FROM vicidial_list
WHERE list_id = 101
AND lead_id NOT IN (
SELECT MIN(lead_id)
FROM vicidial_list
WHERE list_id = 101
GROUP BY phone_number
);
Delete all leads in a list
Before deleting the entire list:
SELECT COUNT(*) FROM vicidial_list WHERE list_id = 101;
SELECT COUNT(*) FROM vicidial_log WHERE list_id = 101;
SELECT COUNT(*) FROM vicidial_closer_log WHERE list_id = 101;
Then back up:
mysqldump -u asterisk -p asterisk vicidial_list vicidial_log vicidial_closer_log > /backup/list_101_$(date +%Y%m%d).sql
Delete only the leads, not the list itself:
DELETE FROM vicidial_list WHERE list_id = 101;
The call logs remain intact in vicidial_log and vicidial_closer_log. Reporting still works.
Cascade delete (advanced)
If you need to delete leads and all related call log entries:
mysqldump -u asterisk -p asterisk > /backup/full_backup_$(date +%Y%m%d).sql
Get the lead IDs:
SELECT GROUP_CONCAT(lead_id) FROM vicidial_list WHERE list_id = 101 LIMIT 1 \G
Then delete in transaction:
START TRANSACTION;
DELETE FROM vicidial_log WHERE list_id = 101;
DELETE FROM vicidial_closer_log WHERE list_id = 101;
DELETE FROM vicidial_list WHERE list_id = 101;
COMMIT;
This removes the entire record chain. It is slower and locks more tables. Use only when you must remove all traces.
Verifying list state after operations
After any bulk operation, verify consistency:
SELECT
list_id,
COUNT(*) as total_leads,
SUM(CASE WHEN status = 'NEW' THEN 1 ELSE 0 END) as new_leads,
SUM(CASE WHEN status = 'CALLBACK' THEN 1 ELSE 0 END) as callbacks,
SUM(CASE WHEN status = 'DEAD' THEN 1 ELSE 0 END) as dead_leads,
SUM(CASE WHEN status = 'DNC' THEN 1 ELSE 0 END) as dnc_leads
FROM vicidial_list
WHERE list_id = 101
GROUP BY list_id;
Check for orphaned call logs (leads deleted but calls remain):
SELECT COUNT(*) FROM vicidial_log vl
WHERE NOT EXISTS (
SELECT 1 FROM vicidial_list vll
WHERE vll.lead_id = vl.lead_id
AND vll.list_id = vl.list_id
)
AND vl.list_id = 101;
If this returns a non-zero count, those calls were from leads now deleted. The log entries persist and do not cause errors, but they represent orphaned records.
Clearing agent assignments after list operations
After resetting or deactivating a large portion of a list, clear the agent assignments so agents pick up new leads instead of cached ones:
UPDATE vicidial_closer_log
SET lead_id = 0,
status = 'RESET'
WHERE list_id = 101
AND DATE(call_date) = CURDATE()
AND status NOT IN ('SALE', 'DISPO');
This clears today's incomplete work. Agents see a fresh list next session.
Alternatively, log agents out forcibly and clear their session:
mysql -u asterisk -p asterisk -e "DELETE FROM vicidial_live_agents WHERE list_id = 101;"
Agents must log back in. Their screen will refresh and reload leads.
Bulk operations via the ViciDial API
For large recurring resets, automate via the REST API:
curl -X POST http://your_vicidial_ip/api/vicirest.php \
-d "function=update_lead" \
-d "user=admin" \
-d "pass=password" \
-d "lead_id=1000001" \
-d "status=NEW" \
-d "phone_number=5551234567" \
-d "list_id=101"
For bulk operations, use the database directly. The API is slower for large batches.
If your installation supports it, the VICIDIAL CLI at /usr/share/astguiclient/ can also trigger resets:
/usr/share/astguiclient/ADMIN_LIST_RESET.pl --list_id 101 --reset_status NEW
Check your ViciDial version and permissions before running. Some installations disable this script.
Troubleshooting
Leads reappear after deactivation
If deactivated leads are called again, the issue is usually cached agent lists or a list being re-imported. Check:
SELECT * FROM vicidial_list WHERE phone_number = '5551234567' AND list_id = 101;
If the status shows as DEAD but agents still see it, restart the agent process:
sudo systemctl restart vicidial_agent
Or clear the web browser cache. Agent screens cache list data in JavaScript.
If leads reappear in the database, someone imported a backup or ran an inadvertent INSERT. Check audit logs:
tail -100 /var/log/asterisk/messages | grep vicidial
Reset did not clear callbacks
If callbacks remain after reset, they may be in the future or in a different table. Check both places:
SELECT COUNT(*) FROM vicidial_list
WHERE list_id = 101
AND status = 'CALLBACK'
AND callback_datetime IS NOT NULL;
SELECT COUNT(*) FROM vicidial_log
WHERE list_id = 101
AND call_date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
AND disposition = 'CALLBACK';
To clear scheduled callbacks:
UPDATE vicidial_list
SET callback_datetime = NULL,
callback_localtime = NULL
WHERE list_id = 101
AND status = 'CALLBACK';
Then reset status:
UPDATE vicidial_list
SET status = 'NEW'
WHERE list_id = 101
AND status = 'CALLBACK';
Agents report "No leads available"
This occurs after a large deactivation or reset if the list status is set to INACTIVE or if all leads are somehow marked as unavailable. Check:
SELECT list_id, active FROM vicidial_list_parameter WHERE list_id = 101;
The active field must be 'Y':
UPDATE vicidial_list_parameter
SET active = 'Y'
WHERE list_id = 101;
Also verify leads exist and have dialing status:
SELECT COUNT(*) FROM vicidial_list
WHERE list_id = 101
AND status IN ('NEW', 'CALLBACK', 'XFER', 'ACTIVE');
If it is zero, all leads are deactivated. Reset some:
UPDATE vicidial_list
SET status = 'NEW'
WHERE list_id = 101
LIMIT 1000;
Duplicate dials or high hang-up rate after reset
If agents report duplicate dials on the same number or hang-ups on callbacks, the issue is often a reset that did not clear the call log entries. The system sees a lead with status NEW but a recent call entry, and some scripts treat this as "already dialed."
Check for recent calls on fresh leads:
SELECT vl.lead_id, vl.phone_number, vl.status, MAX(vll.call_date)
FROM vicidial_list vl
JOIN vicidial_log vll ON vl.lead_id = vll.lead_id
WHERE vl.list_id = 101
AND vl.status = 'NEW'
AND vll.call_date > DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY vl.lead_id
LIMIT 10;
These are not duplicates; they are normal. The log entries are kept for compliance. The dialer should not retry the same number in the same call. If it does, the issue is in the campaign script or IVR logic, not the list reset.
If agents report actual duplicate dials within seconds, check the dialer script and campaign settings in the ViciDial admin panel.
Database locked during bulk operation
Large updates lock the table momentarily. If agents are actively dialing, they may see "database locked" errors in Asterisk logs. Minimize this by:
- Running operations during off-hours (lunch, overnight)
- Stopping the autodial process:
sudo systemctl stop asterisk - Staggering the operation (batch updates with LIMIT as shown earlier)
- Using a dedicated read replica if your setup has one
If you must operate during active hours, stagger:
UPDATE vicidial_list
SET status = 'NEW'
WHERE list_id = 101
AND lead_id >= 1000000
AND lead_id < 1001000;
-- Wait 5 seconds
-- Repeat with next 1000 lead_ids
This allows brief lock periods interspersed with normal operation.
Frequently asked questions
Can I recover leads after deletion?
Yes, only from a database backup. There is no "undelete" in MySQL. Restore the backup to a separate server, extract the lost records, and re-import them. This requires downtime or a replica database. Prevention is better than recovery: always backup before deleting, and use deactivation instead of deletion unless you are certain the data should never exist again.
What happens to call recordings when I delete a lead?
Deleting a lead from vicidial_list does not automatically delete recordings. Call recordings are stored on the filesystem (usually /var/spool/asterisk/monitor/) and indexed in asterisk_cdr.cdr and vicidial_log. The lead deletion breaks the link in the database but files remain. To delete recordings, manually remove the files or use a cleanup script. Most setups keep recordings separate from the database for this reason.
How do I reset a list but keep agent notes and custom fields?
Only reset the status and call timing fields, not the comments or custom fields. Use this query instead of the full reset: UPDATE vicidial_list SET status = 'NEW', last_call_time = NULL WHERE list_id = 101; This preserves comments and any custom fields you added. Notes in vicidial_log also remain in the call history.
Why does resetting a list not show up immediately in the agent screen?
Agent screens cache list data in the browser and in Asterisk's in-memory list cache. After a database reset, the agent may need to log out and log back in, or refresh the browser. Alternatively, restart the Asterisk process with sudo systemctl restart asterisk. The database change is immediate, but the dialer process reads it once per cycle or upon login.
Can I reset only leads assigned to a specific agent or campaign?
Yes, but the lead assignment is not stored in vicidial_list. It is in vicidial_closer_log for active work or in the agent's current session. To reset only leads an agent touched today: UPDATE vicidial_list SET status = 'NEW' WHERE lead_id IN (SELECT DISTINCT lead_id FROM vicidial_closer_log WHERE agent = 'agent_username' AND DATE(call_date) = CURDATE()) AND list_id = 101; This resets only leads that agent worked today. Other agents' work is unaffected.
Summary
ViciDial list management requires understanding the difference between deactivation, reset, and deletion. Deactivation marks leads as DEAD or moves the list to INACTIVE status, preserving all records for audit and reporting. Reset clears call history and status to NEW, returning leads to the active pool for re-dialing. Deletion removes records entirely and should be used only for test data or documented errors.
Key operations performed in this tutorial:
- Updated
vicidial_liststatus field to DEAD, INACTIVE, or NEW - Cleared call timing fields (
last_call_time,last_local_call_time) on reset - Removed callback schedules before re-opening lists
- Identified and removed duplicate leads using MIN(lead_id)
- Verified list consistency with aggregate queries
- Staged large operations to avoid table locks
Before running any bulk update, always back up the vicidial_list and vicidial_log tables. Verify counts with SELECT before DELETE or UPDATE. Run operations during off-hours if possible.
After completing list operations, check agent screens for updated lead counts, restart agents if they report stale data, and verify call logs for orphaned records. Audit logs in /var/log/asterisk/messages show dialer activity if problems arise.