DEV Community

Moisi Trungu
Moisi Trungu

Posted on • Originally published at flixzone.icu

How to Upload a Lead List to ViciDial — CSV Format & Mapping

How to Upload a Lead List to ViciDial — CSV Format & Mapping

Master the complete process of importing lead lists into ViciDial with proper CSV formatting, field mapping, and validation techniques to avoid common import failures in production environments.

Prerequisites

Before uploading a lead list to ViciDial, ensure you have the following:

  • ViciDial installation (tested on 2.14.x and later)
  • Admin access to the web interface (/vicidial/admin.php)
  • Database credentials for the asterisk database
  • Sufficient disk space in /var/spool/vicidial/LEADS_IN/ (typically 5GB+ for large lists)
  • Linux command-line access to the ViciDial server
  • Campaign already created in ViciDial (required before list assignment)
  • MySQL/MariaDB client installed on your local machine (if importing via SSH tunnel)
  • Basic understanding of CSV format and field delimiters

Understanding ViciDial Lead List Structure

Core Lead Tables

ViciDial stores lead data primarily in two tables:

-- Main lead table
SHOW CREATE TABLE vicidial_list\G

-- This shows the structure containing:
-- lead_id (primary key, auto-increment)
-- phone_number (VARCHAR 20)
-- first_name, last_name, address, city, state, postal_code
-- email, vendor_lead_code, source_id
-- list_id (foreign key to campaign list)
-- status (NEW, ACTIVE, etc.)
-- gmt_offset_now (for timezone handling)
Enter fullscreen mode Exit fullscreen mode

The most critical fields for import are:

  • phone_number – Required; primary dialing field
  • list_id – Links lead to campaign
  • first_name, last_name – Optional but recommended for agent context
  • email – Optional but valuable for callback/document delivery
  • vendor_lead_code – Unique external identifier; prevents duplicates
  • source_id – Tracks lead source for compliance/analytics
  • gmt_offset_now – Critical for timezone-aware calling windows

List Management in ViciDial

Campaigns in ViciDial are containers with associated lists. Each campaign can have multiple lists, and each list contains leads.

-- View existing campaigns
SELECT campaign_id, campaign_name, active, dial_method 
FROM vicidial_campaigns 
LIMIT 10;

-- View lists under a campaign
SELECT list_id, campaign_id, list_name, lead_count 
FROM vicidial_lists 
WHERE campaign_id = 'TEST_CAMPAIGN';
Enter fullscreen mode Exit fullscreen mode

Preparing Your CSV File

CSV Format Specifications

ViciDial expects a specific CSV structure with proper field ordering and encoding.

phone_number,first_name,last_name,email,vendor_lead_code,source_id,gmt_offset_now
5551234567,John,Doe,john@example.com,VND-123456,WEB,America/New_York
5559876543,Jane,Smith,jane@example.com,VND-123457,PHONE,America/Chicago
5559998888,Bob,Johnson,bob@example.com,VND-123458,REFERRAL,America/Los_Angeles
Enter fullscreen mode Exit fullscreen mode

Required vs. Optional Fields

Required fields:

  • phone_number – E.164 or 10-digit format (ViciDial accepts both)

Strongly recommended:

  • first_name – Improves agent interaction quality
  • vendor_lead_code – Prevents duplicate imports
  • gmt_offset_now – Essential for respecting TCPA calling windows

Optional but useful:

  • last_name, email, address, city, state, postal_code
  • source_id – Track which source generated the lead
  • Custom fields (if your ViciDial instance uses extended schema)

CSV Field Mapping Reference

Here's the complete list of importable fields and their SQL column equivalents:

CSV Field Name          | SQL Table Column    | Data Type    | Max Length
phone_number            | phone_number        | VARCHAR(20)  | 20
first_name              | first_name          | VARCHAR(50)  | 50
last_name               | last_name           | VARCHAR(50)  | 50
middle_initial          | middle_initial      | VARCHAR(1)   | 1
address                 | address             | VARCHAR(100) | 100
city                    | city                | VARCHAR(50)  | 50
state                   | state               | VARCHAR(2)   | 2
postal_code             | postal_code         | VARCHAR(10)  | 10
country_code            | country_code        | VARCHAR(3)   | 3
email                   | email               | VARCHAR(100) | 100
vendor_lead_code        | vendor_lead_code    | VARCHAR(20)  | 20
source_id               | source_id           | VARCHAR(50)  | 50
gmt_offset_now          | gmt_offset_now      | VARCHAR(10)  | 10
phone_code              | phone_code          | VARCHAR(20)  | 20
lead_source_id          | lead_source_id      | VARCHAR(20)  | 20
Enter fullscreen mode Exit fullscreen mode

CSV Validation Rules

Before uploading, validate your CSV meets these requirements:

  1. No header row – ViciDial list upload expects data rows only (headers are optional in UI but not recommended)
  2. Consistent field count – Every row must have the same number of columns
  3. Proper encoding – UTF-8 without BOM
  4. Phone number format – 10+ digits, no spaces/dashes in raw CSV (formatting applied on import)
  5. No embedded newlines – Use proper CSV escaping if fields contain commas
  6. File size limits – Practical limit 50MB per file; split larger lists

Creating a Validation Script

#!/bin/bash
# validate_csv.sh - Pre-import CSV validation

CSV_FILE="$1"

if [ ! -f "$CSV_FILE" ]; then
    echo "ERROR: File not found: $CSV_FILE"
    exit 1
fi

# Check file encoding
FILE_ENCODING=$(file -b --mime-encoding "$CSV_FILE")
if [ "$FILE_ENCODING" != "utf-8" ]; then
    echo "WARNING: File encoding is $FILE_ENCODING, expected utf-8"
fi

# Count lines
LINE_COUNT=$(wc -l < "$CSV_FILE")
echo "Total lines: $LINE_COUNT"

# Check for consistent field count (assuming comma-delimited)
FIRST_LINE_FIELDS=$(head -1 "$CSV_FILE" | awk -F',' '{print NF}')
echo "Expected fields per row: $FIRST_LINE_FIELDS"

# Validate all rows have same field count
INVALID_ROWS=$(awk -F',' -v expected=$FIRST_LINE_FIELDS '{
    if (NF != expected) print NR": expected "$FIRST_LINE_FIELDS", got "NF
}' "$CSV_FILE")

if [ -n "$INVALID_ROWS" ]; then
    echo "ERROR: Found rows with inconsistent field counts:"
    echo "$INVALID_ROWS"
    exit 1
fi

# Extract and validate phone numbers (column 1)
INVALID_PHONES=$(awk -F',' '{
    gsub(/[^0-9]/, "", $1)
    if (length($1) < 10) print NR": invalid phone "$1
}' "$CSV_FILE")

if [ -n "$INVALID_PHONES" ]; then
    echo "WARNING: Found potentially invalid phone numbers:"
    echo "$INVALID_PHONES" | head -20
fi

echo "Validation complete!"
Enter fullscreen mode Exit fullscreen mode

Usage:

chmod +x validate_csv.sh
./validate_csv.sh leads.csv
Enter fullscreen mode Exit fullscreen mode

Uploading via Web Interface

Step-by-Step Web Upload

  1. Log into ViciDial Admin Interface:
   https://your-vicidial-server/vicidial/admin.php
Enter fullscreen mode Exit fullscreen mode
  1. Navigate to List Management:

    • Click CampaignsLists (or Leads in newer versions)
    • Select the target campaign from the dropdown
  2. Create New List (if needed):

    • Click Create List button
    • Enter list name (e.g., "Q4_2024_Leads")
    • Set list priority (higher = called first)
    • Click Add List
  3. Upload Leads to List:

    • Select the list from the dropdown
    • Click Upload Leads button
    • Choose CSV file from local machine
    • Important: Configure field mapping (see next section)

Field Mapping Configuration

When you upload a CSV, ViciDial prompts you to map columns to database fields.

ViciDial Upload Field Mapping Dialog:

CSV Column 1 → phone_number (REQUIRED)
CSV Column 2 → first_name
CSV Column 3 → last_name
CSV Column 4 → email
CSV Column 5 → vendor_lead_code
CSV Column 6 → source_id
CSV Column 7 → gmt_offset_now

[Checkbox] Skip first row as header
[Checkbox] Replace existing leads
[Checkbox] Auto-detect duplicates by phone_number

[UPLOAD LEADS]
Enter fullscreen mode Exit fullscreen mode

Critical mapping tips:

  • Leave unused columns unmapped (set to "— ignore —")
  • Ensure phone_number is always mapped to Column 1
  • If your CSV has a header row, check "Skip first row as header"
  • Leave "Replace existing leads" unchecked unless you're refreshing the entire list

Command-Line Upload Method

For production environments and large-scale imports, use the CLI approach which is faster and more scriptable.

Using the ViciDial Import Script

ViciDial includes a Perl script for direct database imports:

# Location of import script
/usr/share/astguiclient/VICIDIAL_add_lead_list.pl

# Basic syntax
perl /usr/share/astguiclient/VICIDIAL_add_lead_list.pl \
  --listname "Q4_2024_Leads" \
  --campaign "TEST_CAMPAIGN" \
  --file "leads.csv" \
  --phonefield 1 \
  --firstnamefield 2 \
  --lastnamefield 3 \
  --emailfield 4 \
  --vendorfield 5 \
  --sourcefield 6 \
  --tzfield 7
Enter fullscreen mode Exit fullscreen mode

Complete CLI Import Example

#!/bin/bash
# import_leads.sh - Production lead import script

CAMPAIGN="TEST_CAMPAIGN"
LIST_NAME="$(date +%Y%m%d)_import"
CSV_FILE="leads.csv"
DB_HOST="localhost"
DB_USER="cron"
DB_PASS="1234"
DB_NAME="asterisk"

# Validate file
if [ ! -f "$CSV_FILE" ]; then
    echo "ERROR: CSV file not found: $CSV_FILE"
    exit 1
fi

# Count leads
LEAD_COUNT=$(wc -l < "$CSV_FILE")
echo "Importing $LEAD_COUNT leads to campaign: $CAMPAIGN"

# Run import
perl /usr/share/astguiclient/VICIDIAL_add_lead_list.pl \
    --listname "$LIST_NAME" \
    --campaign "$CAMPAIGN" \
    --file "$CSV_FILE" \
    --phonefield 1 \
    --firstnamefield 2 \
    --lastnamefield 3 \
    --emailfield 4 \
    --vendorfield 5 \
    --sourcefield 6 \
    --tzfield 7 \
    --dbhost "$DB_HOST" \
    --dbuser "$DB_USER" \
    --dbpass "$DB_PASS" \
    --dbname "$DB_NAME" \
    --active "Y" \
    --skipdup "Y" \
    2>&1 | tee import_$(date +%s).log

echo "Import complete. Check log for details."
Enter fullscreen mode Exit fullscreen mode

Direct MySQL Import

For advanced users, direct SQL insertion can be faster for very large lists (10M+ leads):

#!/bin/bash
# direct_sql_import.sh - Ultra-high-volume import

CSV_FILE="$1"
CAMPAIGN_ID="TEST_CAMPAIGN"
LIST_ID="9001"

# Create staging table
mysql -h localhost -u cron -p1234 asterisk << 'EOF'
CREATE TEMPORARY TABLE vicidial_list_staging LIKE vicidial_list;
EOF

# Load CSV into staging table
mysql -h localhost -u cron -p1234 asterisk << EOF
LOAD DATA LOCAL INFILE '$CSV_FILE'
INTO TABLE vicidial_list_staging
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
(phone_number, first_name, last_name, email, vendor_lead_code, source_id, gmt_offset_now);
EOF

# Insert from staging to production, skipping duplicates
mysql -h localhost -u cron -p1234 asterisk << EOF
INSERT INTO vicidial_list 
  (phone_number, first_name, last_name, email, vendor_lead_code, 
   source_id, gmt_offset_now, list_id, campaign_id, status, entry_date)
SELECT 
  phone_number, first_name, last_name, email, vendor_lead_code,
  source_id, gmt_offset_now, $LIST_ID, '$CAMPAIGN_ID', 'NEW', NOW()
FROM vicidial_list_staging
WHERE phone_number NOT IN (
  SELECT phone_number FROM vicidial_list WHERE list_id = $LIST_ID
)
AND phone_number IS NOT NULL
AND LENGTH(REPLACE(phone_number, REGEXP_SUBSTR(phone_number, '[^0-9]'), '')) >= 10;

-- Update lead count
UPDATE vicidial_lists 
SET lead_count = (SELECT COUNT(*) FROM vicidial_list WHERE list_id = $LIST_ID)
WHERE list_id = $LIST_ID;
EOF

echo "Import complete."
Enter fullscreen mode Exit fullscreen mode

Handling Timezones (Critical for TCPA Compliance)

ViciDial respects Do Not Call (DNC) regulations and time-of-day restrictions based on the gmt_offset_now field.

Timezone Values Reference

phone_number,first_name,last_name,gmt_offset_now
5551234567,John,Doe,America/New_York
5559876543,Jane,Smith,America/Chicago
5559998888,Bob,Johnson,America/Denver
5551112222,Alice,Brown,America/Los_Angeles
5551113333,Charlie,Davis,America/Anchorage
5551114444,Diana,Evans,Pacific/Honolulu
Enter fullscreen mode Exit fullscreen mode

Extracting Timezone from Phone Area Code

#!/bin/bash
# area_code_timezone.sh - Auto-detect timezone from area code

# Create mapping file
cat > area_code_tz.txt << 'EOF'
201|America/New_York
202|America/New_York
203|America/New_York
212|America/New_York
# ... (continue for all area codes)
213|America/Los_Angeles
415|America/Los_Angeles
619|America/Los_Angeles
702|America/Los_Angeles
EOF

# Process CSV with timezone detection
awk -F',' '{
    area_code = substr($1, 1, 3)
    cmd = "grep \"^" area_code "|\" area_code_tz.txt | cut -d\"|\" -f2"
    cmd | getline timezone
    close(cmd)
    if (timezone == "") timezone = "America/Chicago"  # default
    print $0 "," timezone
}' input_leads.csv > leads_with_tz.csv
Enter fullscreen mode Exit fullscreen mode

Duplicate Handling and Lead Deduplication

Preventing Duplicate Imports

ViciDial's vendor_lead_code field is your primary defense against duplicates:

# Before importing, deduplicate by vendor_lead_code
sort -t',' -k5 -u leads.csv > leads_dedup.csv

# Or using awk
awk -F',' '!seen[$5]++' leads.csv > leads_dedup.csv
Enter fullscreen mode Exit fullscreen mode

Checking for Existing Leads

-- Find leads already in the system by vendor_lead_code
SELECT list_id, phone_number, vendor_lead_code, status 
FROM vicidial_list 
WHERE vendor_lead_code IN (
    SELECT vendor_lead_code FROM new_import_list
)
LIMIT 100;

-- Find potential phone duplicates
SELECT phone_number, COUNT(*) as count 
FROM vicidial_list 
WHERE list_id = 9001 
GROUP BY phone_number 
HAVING COUNT(*) > 1;
Enter fullscreen mode Exit fullscreen mode

Automated Deduplication Script

#!/bin/bash
# deduplicate_leads.sh

INPUT_CSV="$1"
OUTPUT_CSV="${INPUT_CSV%.csv}_dedup.csv"

# Extract headers (if present)
HEADERS=$(head -1 "$INPUT_CSV")

# Remove duplicates, preserve first occurrence
(echo "$HEADERS"; tail -n +2 "$INPUT_CSV" | sort -t',' -k5 -u) > "$OUTPUT_CSV"

ORIGINAL_COUNT=$(tail -n +2 "$INPUT_CSV" | wc -l)
DEDUP_COUNT=$(tail -n +2 "$OUTPUT_CSV" | wc -l)
REMOVED=$((ORIGINAL_COUNT - DEDUP_COUNT))

echo "Original leads: $ORIGINAL_COUNT"
echo "After dedup: $DEDUP_COUNT"
echo "Removed duplicates: $REMOVED"
echo "Output: $OUTPUT_CSV"
Enter fullscreen mode Exit fullscreen mode

Monitoring Upload Progress

Checking Upload Status via Database

-- Monitor ongoing import
SELECT 
    list_id,
    campaign_id,
    lead_count,
    last_update_date
FROM vicidial_lists 
WHERE list_id IN (SELECT MAX(list_id) FROM vicidial_lists LIMIT 5)
ORDER BY list_id DESC;

-- Check lead distribution
SELECT status, COUNT(*) as count 
FROM vicidial_list 
WHERE list_id = 9001 
GROUP BY status;

-- Check for import errors
SELECT COUNT(*) 
FROM vicidial_list 
WHERE phone_number IS NULL OR phone_number = '';
Enter fullscreen mode Exit fullscreen mode

Real-Time Import Monitoring

#!/bin/bash
# monitor_import.sh - Watch import progress

LIST_ID="9001"
DB_USER="cron"
DB_PASS="1234"
DB_NAME="asterisk"

while true; do
    CURRENT=$(mysql -h localhost -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" \
        -sN -e "SELECT COUNT(*) FROM vicidial_list WHERE list_id = $LIST_ID;")

    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$TIMESTAMP] Leads in list: $CURRENT"

    sleep 10
done
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Common Import Issues

Issue: "File Not Found" or Permission Denied

Cause: CSV file not in expected directory or incorrect permissions

Solution:

# Ensure file is readable
chmod 644 leads.csv

# Copy to ViciDial's standard upload directory
cp leads.csv /var/spool/vicidial/LEADS_IN/

# Verify permissions
ls -la /var/spool/vicidial/LEADS_IN/ | grep leads.csv
Enter fullscreen mode Exit fullscreen mode

Issue: "Invalid Phone Number Format"

Cause: Phone numbers contain non-numeric characters or are too short

Solution:

#!/bin/bash
# clean_phones.sh

sed 's/[^0-9,\n]//g' leads.csv > leads_clean.csv

# Verify minimum 10 digits
awk -F',' '{
    gsub(/[^0-9]/, "", $1)
    if (length($1) >= 10) print $0
}' leads_clean.csv > leads_valid.csv
Enter fullscreen mode Exit fullscreen mode

Issue: "Field Mapping Mismatch" or Blank Imports

Cause: Incorrect field mapping or CSV column order mismatch

Solution:

Verify your CSV columns match your mapping:

# Show first row with column numbers
head -1 leads.csv | awk -F',' '{for(i=1;i<=NF;i++) print i": "$i}'

# Expected output should match your mapping:
# 1: phone_number
# 2: first_name
# 3: last_name
# etc.
Enter fullscreen mode Exit fullscreen mode

Issue: Duplicate Leads Being Imported

Cause: vendor_lead_code not set or duplicate checking disabled

Solution:

# Check if vendor_lead_code is unique
mysql -u cron -p1234 asterisk -e \
  "SELECT vendor_lead_code, COUNT(*) FROM vicidial_list 
   WHERE list_id = 9001 AND vendor_lead_code IS NOT NULL 
   GROUP BY vendor_lead_code HAVING COUNT(*) > 1 LIMIT 20;"

# Enable duplicate checking in future imports via web UI:
# Check: [X] Auto-detect duplicates by phone_number
# Or use CLI: --skipdup "Y"
Enter fullscreen mode Exit fullscreen mode

Issue: Out of Memory During Large Import

Cause: Importing massive lists (>50M leads) with insufficient RAM

Solution:

#!/bin/bash
# batch_import.sh - Split large files into chunks

CSV_FILE="$1"
CHUNK_SIZE=100000
CAMPAIGN="TEST_CAMPAIGN"

TOTAL_LINES=$(wc -l < "$CSV_FILE")
CHUNKS=$((($TOTAL_LINES / $CHUNK_SIZE) + 1))

echo "Splitting $TOTAL_LINES lines into $CHUNKS chunks of $CHUNK_SIZE..."

for i in $(seq 1 $CHUNKS); do
    START=$(( ($i - 1) * $CHUNK_SIZE + 1 ))
    END=$(( $i * $CHUNK_SIZE ))

    CHUNK_FILE="chunk_${i}.csv"
    sed -n "${START},${END}p" "$CSV_FILE" > "$CHUNK_FILE"

    echo "Importing chunk $i: $CHUNK_FILE"

    perl /usr/share/astguiclient/VICIDIAL_add_lead_list.pl \
        --listname "${CAMPAIGN}_$(date +%s)_chunk_$i" \
        --campaign "$CAMPAIGN" \
        --file "$CHUNK_FILE" \
        --phonefield 1 \
        --firstnamefield 2 \
        --lastnamefield 3 \
        --dbhost localhost \
        --dbuser cron \
        --dbpass 1234 \
        --dbname asterisk \
        --active "Y" 2>&1 | tee "import_chunk_${i}.log"

    rm "$CHUNK_FILE"

    sleep 5  # Prevent database strain
done

echo "All chunks imported."
Enter fullscreen mode Exit fullscreen mode

Issue: Leads Show "NEW" Status But Don't Dial

Cause: Campaign not active or dialers not running

Solution:

# Verify campaign is active
mysql -u cron -p1234 asterisk -e \
  "SELECT campaign_id, campaign_name, active FROM vicidial_campaigns 
   WHERE campaign_id = 'TEST_CAMPAIGN';"

# Check if dialers are running
ps aux | grep -E "vicidial_dial|[p]erl.*dial"

# Start ViciDial daemons if stopped
systemctl restart vicidial

# Check for dialer errors in logs
tail -50 /var/log/asterisk/messages | grep -i dial
Enter fullscreen mode Exit fullscreen mode

Issue: Slow Import Speed

Cause: Single-threaded import, high disk I/O, or database lock contention

Solution:

# Check database performance
mysql -u cron -p1234 asterisk -e "SHOW PROCESSLIST;" | grep -i vicidial

# Optimize table before import
mysql -u cron -p1234 asterisk -e "OPTIMIZE TABLE vicidial_list;"

# Increase insert buffer during import
mysql -u cron -p1234 asterisk << EOF
SET SESSION insert_delayed_insert_limit=100000;
SET SESSION bulk_insert_buffer_size=16000000;
EOF

# Use parallel import with multiple chunks
# (See batch_import.sh above)
Enter fullscreen mode Exit fullscreen mode

Database Verification After Import

Post-Import Validation Queries

-- Count total leads imported
SELECT COUNT(*) as total_leads 
FROM vicidial_list 
WHERE list_id = 9001;

-- Verify required fields populated
SELECT 
    COUNT(*) as total,
    SUM(CASE WHEN phone_number IS NULL THEN 1 ELSE 0 END) as missing_phone,
    SUM(CASE WHEN first_name IS NULL THEN 1 ELSE 0 END) as missing_first,
    SUM(CASE WHEN vendor_lead_code IS NULL THEN 1 ELSE 0 END) as missing_vendor_code
FROM vicidial_list 
WHERE list_id = 9001;

-- Check status distribution
SELECT status, COUNT(*) as count 
FROM vicidial_list 
WHERE list_id = 9001 
GROUP BY status 
ORDER BY count DESC;

-- Verify timezone distribution
SELECT gmt_offset_now, COUNT(*) as count 
FROM vicidial_list 
WHERE list_id = 9001 
GROUP BY gmt_offset_now 
ORDER BY count DESC;

-- Find any malformed phone numbers
SELECT phone_number, COUNT(*) 
FROM vicidial_list 
WHERE list_id = 9001 
AND (LENGTH(REPLACE(phone_number, REGEXP_SUBSTR(phone_number, '[^0-9]'), '')) < 10 
     OR phone_number IS NULL)
GROUP BY phone_number;

-- Verify list_id assignment
SELECT campaign_id, list_id, lead_count 
FROM vicidial_lists 
WHERE list_id = 9001;
Enter fullscreen mode Exit fullscreen mode

Production Best Practices

Pre-Import Checklist

#!/bin/bash
# pre_import_checklist.sh

echo "=== ViciDial Pre-Import Checklist ==="

# 1. Backup database
echo "[ ] Backup database"
mysqldump -u cron -p1234 asterisk > backup_$(date +%s).sql

# 2. Validate CSV
echo "[ ] Validate CSV format and encoding"
file -b --mime-encoding "$1"
head -5 "$1"

# 3. Check campaign exists
echo "[ ] Verify campaign exists"
mysql -u cron -p1234 asterisk -sN -e \
  "SELECT campaign_id FROM vicidial_campaigns WHERE campaign_id = 'TEST_CAMPAIGN';"

# 4. Check disk space
echo "[ ] Check available disk space"
df -h /var/spool/vicidial/

# 5. Check database connection
echo "[ ] Test database connection"
mysql -u cron -p1234 asterisk -e "SELECT COUNT(*) FROM vicidial_list LIMIT 1;"

# 6. Verify daemons running
echo "[ ] Verify ViciDial daemons running"
ps aux | grep -E "vicidial_dial|vicidial_agent" | grep -v grep

echo "=== Checklist Complete ==="
Enter fullscreen mode Exit fullscreen mode

Backup Strategy

#!/bin/bash
# backup_before_import.sh

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/vicidial"

mkdir -p "$BACKUP_DIR"

echo "Creating database backup: $BACKUP_DIR/asterisk_$TIMESTAMP.sql.gz"
mysqldump -u cron -p1234 --single-transaction --quick asterisk | \
    gzip > "$BACKUP_DIR/asterisk_$TIMESTAMP.sql.gz"

# Verify backup
gunzip -t "$BACKUP_DIR/asterisk_$TIMESTAMP.sql.gz"
if [ $? -eq 0 ]; then
    echo "Backup verified successfully"
else
    echo "ERROR: Backup verification failed!"
    exit 1
fi

# Keep only last 10 backups
ls -t "$BACKUP_DIR"/asterisk_*.sql.gz | tail -n +11 | xargs rm -f
Enter fullscreen mode Exit fullscreen mode

Logging and Audit Trail

#!/bin/bash
# import_with_audit.sh

IMPORT_LOG="/var/log/vicidial/lead_imports.log"
CSV_FILE="$1"
CAMPAIGN="TEST_CAMPAIGN"

# Log import start
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting import: $CSV_FILE" >> "$IMPORT_LOG"

# Get file metadata
FILE_SIZE=$(du -h "$CSV_FILE" | cut -f1)
FILE_HASH=$(md5sum "$CSV_FILE" | awk '{print $1}')
LEAD_COUNT=$(wc -l < "$CSV_FILE")

echo "[$(date '+%Y-%m-%d %H:%M:%S')] File: $CSV_FILE | Size: $FILE_SIZE | Leads: $LEAD_COUNT | MD5: $FILE_HASH" >> "$IMPORT_LOG"

# Run import with error capturing
perl /usr/share/astguiclient/VICIDIAL_add_lead_list.pl \
    --listname "${CAMPAIGN}_$(date +%s)" \
    --campaign "$CAMPAIGN" \
    --file "$CSV_FILE" \
    --phonefield 1 \
    --firstnamefield 2 \
    --lastnamefield 3 \
    --emailfield 4 \
    --vendorfield 5 \
    --sourcefield 6 \
    --tzfield 7 \
    --dbhost localhost \
    --dbuser cron \
    --dbpass 1234 \
    --dbname asterisk \
    --active "Y" 2>&1 | tee -a "$IMPORT_LOG"

# Log completion
FINAL_COUNT=$(mysql -u cron -p1234 asterisk -sN -e \
    "SELECT COUNT(*) FROM vicidial_list WHERE list_id = (SELECT MAX(list_id) FROM vicidial_lists);")

echo "[$(date '+%Y-%m-%d %H:%M:%S')] Import complete. Final lead count: $FINAL_COUNT" >> "$IMPORT_LOG"
Enter fullscreen mode Exit fullscreen mode

Summary

Uploading lead lists to ViciDial requires careful attention to CSV formatting, field mapping, and validation. The key steps are:

  1. Prepare your CSV with proper encoding (UTF-8), consistent field counts, and valid phone numbers
  2. Map fields correctly, ensuring phone_number is primary and vendor_lead_code is set for duplicate detection
  3. Set timezones (gmt_offset_now) for TCPA compliance and proper calling windows
  4. Choose your upload method — web UI for small lists, CLI for production/large-scale imports
  5. Validate after import using database queries to ensure data integrity
  6. Implement safeguards with backups, deduplication, and audit logging for production reliability

For optimal results in production environments:

  • Always deduplicate leads before import using vendor_lead_code
  • Split very large lists (>50M leads) into chunks to avoid memory issues
  • Test imports with a small sample first before running full campaigns
  • Monitor upload progress with real-time database queries
  • Keep comprehensive logs and backups for compliance and troubleshooting
  • Use the CLI/Perl scripts for scriptable, repeatable imports rather than relying on the web UI

With these practices, you can reliably and efficiently manage lead lists at any scale in ViciDial production environments.

Top comments (0)