DEV Community

Jason Shouldice
Jason Shouldice

Posted on • Edited on • Originally published at vicistack.com

VICIdial List Discipline: Why Two Identical Dialers Produce Wildly Different Results

Two call centers. Same VICIdial build. Same number of agents. Same carrier. Same product offer. One consistently hits 12-15% contact rates and 3-4% conversion. The other hovers around 6% contact and can't figure out why their cost per acquisition keeps climbing.

The difference isn't technology. It's list discipline.

Center A segments leads by age, source, and prior contact history. Center B dumps everything into a single list and lets the dialer chew through it. The predictive algorithm doesn't know a fresh web form from a 45-day-old purchased record that's been called 6 times. Your segmentation strategy is what gives the dialer the intelligence it lacks on its own.

Lead costs have increased across nearly every vertical over the past three years. If you're paying $8-25 per lead in insurance, solar, or home services, burning through a 50,000-record list in two days without proper recycling and segmentation isn't just inefficient -- it's financially reckless.

How VICIdial Organizes Lead Data

Every lead lives in one MySQL table: vicidial_list. Whether you have 10,000 leads or 10 million, same table, differentiated by list_id. The key columns:

Column Purpose
lead_id Auto-increment primary key
list_id Associates lead with a list
status Current disposition (NEW, NA, B, SALE, DNC, etc.)
phone_number Primary dial number
called_count Total dial attempts
called_since_last_reset Y/N -- controls hopper eligibility
gmt_offset_now Timezone offset for compliance filtering
rank Numeric priority for lead ordering
entry_date When the lead was loaded

Lists are logical groupings assigned to campaigns. A single campaign can dial from multiple lists simultaneously. This is the foundation of segmentation: separate lists for different sources, age cohorts, or quality tiers, activated or deactivated within a campaign as needed.

Critical point: VICIdial does not enforce cross-list deduplication by default. The same phone number can exist in multiple lists. Use CHECK FOR DUPLICATES BY PHONE IN CAMPAIGN LISTS during upload. For deeper dedup:

/usr/share/astguiclient/VICIDIAL_DEDUPE_leads.pl --campaign=SALES --duplicate-list=99999
Enter fullscreen mode Exit fullscreen mode

The Upload Process That Prevents Garbage Data

Before any file touches VICIdial's lead loader:

Phone numbers: Strip all non-numeric characters. Remove leading 1 for US numbers (VICIdial adds phone_code separately). Minimum 10 digits for US.

State field: Exactly 2 characters. Longer values get truncated silently, breaking timezone lookups and compliance filtering.

DNC scrubbing: Run against the national DNC registry before upload, not after. Loading DNC numbers and relying on VICIdial's internal check adds dead records to your database and slows hopper processing.

File format: Use tab-delimited. CSV with commas in address or comment fields causes silent parsing corruption.

Character encoding: UTF-8. Non-ASCII characters cause parsing failures in certain VICIdial versions.

After every upload, verify:

# Compare loaded count to source file
wc -l /tmp/upload-file.txt
mysql asterisk -e "SELECT COUNT(*) FROM vicidial_list WHERE list_id = 1001;"
Enter fullscreen mode Exit fullscreen mode

1-2% variance from dedup is normal. Above 5% means a formatting problem in your source file.

Segmentation: The Highest-Impact Thing You're Not Doing

A blended, unsegmented list forces VICIdial to treat every lead identically. A fresh, high-intent web form from 10 minutes ago gets the same dial priority as a 45-day-old purchased lead called 6 times. The predictive algorithm only sees dial statuses and called counts. Your segmentation gives it the intelligence it lacks.

Dimension 1: Lead Age (Highest Impact)

Industry data: leads contacted within 5 minutes of form submission convert at 8-10x the rate of leads contacted after 30 minutes. Create separate lists:

  • Hot leads (0-1 hour): Highest priority, highest auto-dial level
  • Warm leads (1-24 hours): Standard priority
  • Aging leads (1-7 days): Lower priority, recycling candidates
  • Cold leads (7+ days): Lowest priority, different script

Dimension 2: Lead Source

Not all sources produce equal quality. A targeted landing page form converts differently than a shared list broker. Separate by source to measure performance and allocate dial time by ROI.

Dimension 3: Geography

Time zone compliance (legally required) and regional performance optimization. Some products sell better in certain states. Some carriers have better completion rates in certain area codes.

Dimension 4: Prior Contact History

A lead that answered and said "call me back" is a completely different animal from one that's never been reached. Different lists, different scripts, different cadence.

Dimension 5: Product or Offer

If you sell multiple products, segment by interest. Cross-selling from a blended list produces lower conversion than matching leads to what they inquired about.

Implementation

Use lead order DOWN RANK and set rank values during upload for priority scoring. Use lead filters for SQL-based restrictions:

entry_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
Enter fullscreen mode Exit fullscreen mode

Override campaign settings at the list level where needed -- per-list caller ID, agent script, and transfer settings. Show local caller ID for geographic lists or load different scripts for callback leads.

Disposition Management

Dispositions are the taxonomy of your operation. Every call outcome maps to a status that determines what happens next.

Custom Statuses You Need

Beyond VICIdial's defaults (NEW, NA, B, A, SALE, DNC, etc.), create campaign-specific statuses:

Status Name Dialable Purpose
LMVM Left Message on VM Y Agent left voicemail; recycle after delay
DECMKR Decision Maker Unavail Y Reached household, not right person
WRNUMB Wrong Number N Remove from outbound
CBHOLD Callback Hold N Interested but needs time
HUAGNT Hung Up on Agent Y Immediate hangup; recycle once
APPT Appointment Set N Remove from outbound
DNQUAL Did Not Qualify N Contacted, doesn't meet criteria
INVNUM Invalid Number N Valid format but wrong party/fax/modem

Rules: Every human-answered call gets a specific disposition (not generic NA). Terminal statuses have Dialable = N. Separate "left voicemail" (LMVM) from "answering machine" (A) -- different recycling strategy for each.

Create status categories: CONTACTED (all human-answer statuses), UNCONTACTED (NA, B, A, AA, DC, DROP), TERMINAL (SALE, DNC, DC, WRNUMB), RECYCLABLE (NA, B, A, LMVM, DECMKR, HUAGNT). Categories simplify reporting and campaign logic.

Time Zone Filtering: Not Optional

Under TCPA and FTC rules, marketing calls must occur between 8 AM and 9 PM in the recipient's local time zone. Per-call statutory damages: $500-$1,500. A single day of misconfigured timezone settings across a large list creates six-figure liability exposure.

VICIdial determines timezone from area code via the gmt_offset_now column. Ensure the NANPA table is populated:

/usr/share/astguiclient/ADMIN_area_code_populate.pl
Enter fullscreen mode Exit fullscreen mode

Stagger lists by timezone. Activate Eastern leads first each morning, add Central an hour later. Contact rates peak at 10 AM-12 PM and 4-6 PM local time for residential calls. Concentrate efforts in those windows.

Some states impose restrictions beyond the federal 8 AM-9 PM window. California, Florida, and New York have specific telemarketing time restrictions. Configure per-state restrictions under Admin > Call Times > State Call Times.

Hopper Configuration

The hopper (vicidial_hopper) is VICIdial's staging area -- a small, fast table the dialer pulls from instead of scanning the massive vicidial_list every time an agent becomes available. The background process AST_VDhopper.pl refills it continuously.

Automatic Hopper Level: Set to Y. Formula:

Hopper Level = Agents x Dial Level x (60 / Dial Timeout) x Multiplier
Enter fullscreen mode Exit fullscreen mode

20 agents, dial level 3, 26-second timeout, multiplier 1 = 138 leads in hopper.

Lead Order: DOWN RANK if using lead scoring. DOWN (newest first) for speed-to-lead. Never UP (oldest first) unless you have a specific reason -- produces worst contact rates.

Auto Trim Hopper: Y. Removes leads that no longer qualify (timezone lockout, filter changes).

If the hopper shows 0 leads, check dial statuses, lead filter, and timezone settings.

Hopper priority order:

  1. Scheduled callbacks (highest)
  2. Recycled leads
  3. Auto-alt-dial leads
  4. Standard hopper entries

The Multi-Day Re-Dial Cadence

Since recycling maxes out within a single day, multi-day strategy uses nightly list resets combined with dial statuses and lead filters.

Day Attempts Strategy
Day 1 3-4 Intra-day recycling at 1-2 hour intervals
Day 2 2-3 Shifted time window from Day 1
Day 3 2 Different time of day again
Day 4 Rest day Breaks pattern, reduces spam risk
Day 5 2 Resume with fresh timing
Day 6 1 Final standard attempt
Day 7 1 Last attempt before aging list

After Day 7, unccontacted leads move to an aging list with reduced frequency and a different caller ID. Consider a different script: acknowledging the multiple contacts works better than pretending it's a first call.

The key to making this work automatically: schedule nightly cron jobs that reset called_since_last_reset for leads matching your criteria (right statuses, under the attempt cap, modified more than N hours ago). No manual list resets, no forgotten lead pools. The system runs itself.

Hopper Tuning for Large Lists

The hopper fill query scans vicidial_list looking for callable leads. On a table with 10 million+ rows and inadequate indexes, this query can take 10+ seconds, blocking everything else during the lock. Two mitigations:

  1. Add indexes: The composite index on (list_id, status) dramatically speeds up hopper fills for large lists. The index on (status, modify_date) helps recycling queries.

  2. Keep active lists lean: Don't have 5 million leads in a single active list. Split large lead pools into multiple lists. Only activate the lists you're currently dialing from. Inactive lists don't participate in hopper fills.

A healthy hopper should refill faster than agents can drain it. If you see the hopper hitting zero during production hours, the fill query is too slow. Either add indexes, reduce list sizes, or simplify your lead filter.

State-Level Calling Restrictions

Some states impose restrictions beyond the federal 8 AM-9 PM window. California, Florida, and New York have specific telemarketing time restrictions that differ from the federal standard. VICIdial supports per-state and per-day-of-week restrictions through State Call Times.

Configure at Admin > Call Times. For each state with special rules, add a State Call Time entry that defines the allowed calling window for that state. VICIdial checks both the federal call time and the state-specific restriction, using whichever is more restrictive.

This is not optional compliance hardening -- it's legally required. A center dialing California leads at 8 PM Pacific when the state restriction ends at 7 PM faces per-call liability. The cost of configuring state restrictions correctly is zero. The cost of getting it wrong is potentially catastrophic.

Auto-Alt-Dial: Working Multiple Numbers Per Lead

When VICIdial has alternate phone numbers for a lead (alt_phone, address3 used as a secondary number), auto-alt-dial can attempt these numbers when the primary number fails. Configure per-campaign under Auto-Alt-Dial settings.

The auto-alt process attempts alternate numbers based on the disposition of the primary attempt. If the primary gets NA, the alt is attempted. If the primary gets B, the alt is attempted. You control which dispositions trigger alt-dialing and how many alt attempts are allowed.

This integrates with recycling: when all numbers for a lead have been attempted, the lead enters the recycling pool based on its final disposition. The hopper tracks alt-dial leads separately from standard leads, giving them appropriate priority.

Lead Aging: When to Stop Dialing

Tier Age Attempts Action
Fresh 0-7 days 0-8 Full recycling
Warm 8-21 days 9-15 Reduced frequency, longer delays
Aged 22-45 days 16-20 Minimal, 1-2 per week
Retired 46-90 days 21+ Remove from active dialing
Expired 90+ days Any Archive or purge

Schedule tier transitions as nightly cron jobs. Retired leads aren't necessarily dead -- reactivation campaigns after 60+ days rest can be productive. Different caller ID, different script, different offer. Lower priority than fresh leads.

Campaign-Level List Controls

Within Admin > Campaigns > [Campaign], the List Settings section controls how campaigns interact with lists:

Dial Statuses: Which lead statuses the campaign will attempt to dial. If a status isn't checked, leads with that status never enter the hopper.

Lead Order: Controls the sequence leads are pulled into the hopper:

Setting Behavior Best For
UP Oldest leads first (by lead_id) Almost never appropriate
DOWN Newest leads first Speed-to-lead focus
UP COUNT Lowest called_count first Evening underworked leads
DOWN COUNT Highest called_count first Rarely useful
DOWN RANK Highest rank first Lead scoring operations
RANDOM Random ordering Breaking time-of-day patterns

Lead Filter: SQL WHERE clause that restricts hopper eligibility beyond dial statuses. This is where attempt caps live:

called_count < 8 AND entry_date > DATE_SUB(NOW(), INTERVAL 30 DAY)
Enter fullscreen mode Exit fullscreen mode

List Order Randomize: Adds randomization to prevent predictable dial patterns.

The interplay between dial statuses, lead filters, and lead ordering is where most list management strategy lives. Getting these three settings right matters more than any amount of trunk optimization.

List-Level Overrides

In Admin > Lists > [List] > List Modification, you can override campaign settings per-list:

  • Caller ID: Show local CID for geographic lists
  • Agent Script: Different script for callback leads vs cold leads
  • Transfer Settings: Different transfer destinations by list
  • Web Form: Different CRM URL by lead source

This is powerful for segmented operations -- same campaign, multiple lists, each with context-appropriate settings.

Recycling Logic: Intelligent Re-Dial Sequences

Lead recycling is configured per-campaign under Admin > Campaigns > Lead Recycling. For each recyclable status, set an attempt delay (120 to 43,199 seconds) and attempt maximum (1-10). VICIdial automatically re-queues leads after the delay expires.

Recommended recycling schedule:

Status Delay Max Rationale
B (Busy) 5 min 5 Busy signals clear quickly
NA (No Answer) 1 hr 3 Stagger across different times
A (Answering Machine) 2 hr 2 Back off if hitting VM twice
ADC (Carrier No Answer) 30 min 3 Network congestion clears
AA (Auto-Detected VM) 3 hr 2 Similar to A

Critical limitation: Maximum delay is 43,199 seconds, just under 12 hours. This doesn't reliably span midnight boundaries. For multi-day re-dial, use nightly list resets combined with dial statuses and lead filters for attempt caps.

The 7-Day Re-Dial Cadence

Day Attempts Strategy
1 3-4 Intra-day recycling at 1-2 hour intervals
2 2-3 Shift time window (morning Day 1, try afternoon Day 2)
3 2 Different time of day again
4 Rest No dials -- breaks pattern, reduces spam risk
5 2 Resume with fresh timing
6 1 Final standard attempt
7 1 Last attempt before moving to aged list

After Day 7, leads without contact move to an aging list with reduced dial frequency.

Time Zone Compliance in Practice

Stagger lists by timezone. Create separate lists for Eastern, Central, Mountain, Pacific. Activate Eastern first each morning, add Central an hour later.

East Coast expires first. If your operation starts at 8 AM Pacific, East Coast leads are already at 11 AM Eastern, past the optimal morning window. By 6 PM Pacific, East Coast leads are locked out (9 PM Eastern) while you still have three hours of West Coast dialing.

Lead filter by timezone:

gmt_offset_now >= -5.00 AND gmt_offset_now <= -5.00
Enter fullscreen mode Exit fullscreen mode

This restricts dialing to Eastern timezone only. Adjust as your day progresses.

Daily Hopper Management Routine

  1. Before shift: Verify active lists have sufficient undailed leads
  2. Shift start: Monitor real-time report to confirm hopper is filling. Zero = check dial statuses, filter, timezone settings
  3. Mid-shift: Watch for depletion. If hopper drains faster than it refills, increase multiplier or add lists
  4. End of shift: Review counts and plan next day's resets

Web-form leads that need to jump the queue can be injected via the Non-Agent API with high rank values:

/vicidial/non_agent_api.php?function=add_lead&phone_number=5551234567&list_id=1001&phone_code=1&rank=99
Enter fullscreen mode Exit fullscreen mode

Leads with high rank and DOWN RANK ordering get dialed before lower-ranked leads within minutes.

Lead Performance Tracking by List

You can't manage what you don't measure. Track these KPIs per list:

Contact rate by list: Which lead sources produce the highest contact rates? If Source A lists consistently contact at 18% while Source B lists contact at 8%, Source A is delivering better data.

Conversion rate by list: The ultimate metric. Track conversions per list, not just per campaign. A list with high contact rate but low conversion may have data quality issues -- correct phone numbers but poor lead qualification.

Cost per acquisition by list: Total spend on leads in this list divided by conversions. This tells you which sources deliver profitable leads and which are burning money.

Contact rate by time of day per list: Different lead types answer at different times. Business leads pick up during work hours. Residential leads answer in the evening. Geographic lists have timezone-dependent peak windows. Track this by list and shift your dial timing accordingly.

Run a weekly list performance review. Every Monday, pull contact rate, conversion rate, and CPA by list for the previous week. Deactivate consistently underperforming lists. Double down on top performers.

SELECT l.list_id, l.list_name,
       COUNT(vl.lead_id) as total_leads,
       SUM(CASE WHEN vl.status IN ('SALE','APPT','CALLBK') THEN 1 ELSE 0 END) as conversions,
       ROUND(SUM(CASE WHEN vl.called_count > 0 THEN 1 ELSE 0 END) / COUNT(*) * 100, 1) as worked_pct
FROM vicidial_lists l
JOIN vicidial_list vl ON l.list_id = vl.list_id
WHERE l.active = 'Y'
GROUP BY l.list_id
ORDER BY conversions DESC;
Enter fullscreen mode Exit fullscreen mode

Lead Reactivation

Retired leads aren't necessarily dead. Circumstances change -- a prospect uninterested in January may have new needs in April.

Reactivation criteria:

  • Minimum 30-60 days since last attempt
  • Different caller ID, script, and offer
  • Lower hopper priority than fresh leads
  • Track separately with a custom status (REACT) so you can measure reactivation performance independently

The gap between amateur and professional operations is list discipline. If you're running more than 20 agents on unsegmented lists, you're underperforming. ViciStack audits list structures and has seen segmentation alone lift conversion by 20-40%.

Originally published at https://vicistack.com/blog/vicidial-list-management/

Top comments (0)