SMS Campaign Integration for Call Centers: 10DLC Registration, Dialer Workflows, and Cadences That Convert
Last updated: March 2026 | Reading time: ~26 minutes Your agents dial 400 numbers a day and talk to maybe 60 of the people attached to them. The other 340 calls go to voicemail, get screened, or ring out. Meanwhile, 90% of text messages get read within 3 minutes of delivery. That gap -- between phone calls that don't connect and text messages that get read almost instantly -- is where most call centers are leaving money on the table. The existing guide on SMS campaigns for call centers covers the compliance fundamentals and cadence theory. This guide goes into the technical integration: how to wire SMS into your VICIdial dialer workflow so texts fire automatically based on call outcomes, and how to build multi-channel cadences that actually improve your contact rate. But first, the compliance piece, because if you get that wrong, nothing else matters. Since February 1, 2025,...
Overview
Last updated: March 2026 | Reading time: ~26 minutes
Your agents dial 400 numbers a day and talk to maybe 60 of the people attached to them. The other 340 calls go to voicemail, get screened, or ring out. Meanwhile, 90% of text messages get read within 3 minutes of delivery.
That gap -- between phone calls that don't connect and text messages that get read almost instantly -- is where most call centers are leaving money on the table. The existing guide on SMS campaigns for call centers covers the compliance fundamentals and cadence theory. This guide goes into the technical integration: how to wire SMS into your VICIdial dialer workflow so texts fire automatically based on call outcomes, and how to build multi-channel cadences that actually improve your contact rate.
But first, the compliance piece, because if you get that wrong, nothing else matters.
10DLC Registration: The Non-Optional First Step
Since February 1, 2025, US carriers block 100% of unregistered Application-to-Person (A2P) traffic sent over 10-digit long code (10DLC) numbers. There are no exceptions, no grace period, and no workaround. If your messages are not registered, they are not delivered.
What 10DLC Is
10DLC is a system that lets businesses send text messages using standard 10-digit phone numbers (the same numbers you use for calling) instead of short codes (5-6 digit numbers). The registration happens in two parts through The Campaign Registry (TCR):
Brand Registration -- register your business entity. TCR verifies your EIN, legal name, and contact information. This is a one-time step per business.
Campaign Registration -- register each SMS use case. A sales follow-up campaign is separate from an appointment reminder campaign. Each campaign gets its own throughput limits and content rules.
Registration Process and Timeline
Step
Timeline
Cost
Brand registration
1-5 business days
$4 one-time (standard)
Brand vetting (higher throughput)
3-10 business days
$40 one-time
Campaign registration
1-7 business days
$10/month per campaign
Total from start to sending
1-3 weeks
~$54 to start
The timeline varies by provider. Twilio, Telnyx, and Bandwidth all handle TCR registration through their dashboards. If you are already using one of these for SIP trunking, use the same provider for SMS to simplify the setup.
Throughput Limits by Trust Score
After registration, your brand gets a trust score that determines how many messages you can send:
Trust Score
Message Segments per Second
Daily Cap
Low (unvetted)
0.2
~17,000
Medium
1.0
~86,000
High (vetted)
3.0
~260,000
For a call center running 50 agents at 400 dials per day, you generate roughly 200-300 SMS messages per day (assuming you text every no-answer and voicemail). Even the lowest trust tier handles that volume with room to spare. But if you plan to run marketing blasts or appointment reminders in addition to disposition-triggered texts, get the brand vetting done upfront.
TCPA Compliance for Text Messages
The TCPA applies to text messages exactly like it applies to phone calls. The fines are the same: $500 per violation for unintentional, $1,500 per violation for willful. On a list of 10,000 contacts, that is $5 million to $15 million in theoretical exposure.
Consent Requirements
Since late 2023, the FCC requires one-to-one consent. A lead who gave permission to Company A does not automatically consent to messages from Company B, even if both companies bought the lead from the same source.
Your consent record must include:
Required consent documentation per subscriber:
- Phone number
- Timestamp of consent (UTC)
- IP address (for web opt-ins)
- Exact consent language shown to the subscriber
- Source (web form URL, paper form scan, verbal recording)
- Campaign(s) consented to
- Expected message frequency disclosed
Store this in your CRM or a dedicated consent database. You need it to defend against TCPA claims, and "we had consent but can not prove it" is the same as "we did not have consent" in court.
Opt-Out Handling
As of April 2025, businesses must honor opt-out requests within 10 business days and accept revocation through any reasonable method. In practice, this means:
Respond to STOP, UNSUBSCRIBE, CANCEL, END, and QUIT keywords automatically
Process opt-outs from email requests, phone calls, and web forms
Remove the number from all SMS campaigns (not just the one they replied to)
Send a confirmation message after processing the opt-out
Every outbound SMS must include opt-out instructions. The standard footer:
Reply STOP to unsubscribe. Msg & data rates may apply.
Timing Restrictions
Same as calling: no messages before 8 AM or after 9 PM in the recipient's local time zone. Your messaging gateway needs timezone awareness, just like your dialer.
Wiring SMS Into VICIdial Workflows
VICIdial does not have native SMS sending. You need an external messaging gateway connected via API triggers. The integration pattern is straightforward: VICIdial dispositions a call, a script detects the disposition, and fires an SMS through your provider's API.
Architecture Overview
VICIdial Agent dispositions call → vicidial_log updated
↓
Polling script reads new dispositions every 30-60 seconds
↓
Disposition-to-SMS mapping determines which template to send
↓
API call to Telnyx/Twilio/SignalWire sends the message
↓
SMS delivery status logged to sms_log table
↓
Inbound replies routed back to agent screen or queue
The Disposition Polling Script
This script runs as a cron job, checking for new call dispositions and firing SMS messages based on the outcome:
python
1#!/usr/bin/env python32"""sms_trigger.py - Send SMS based on VICIdial call dispositions"""34import os
5import json
6import time
7import requests
8import mysql.connector
9from datetime import datetime, timedelta
1011# Configuration12DB_CONFIG ={13"host":"localhost",14"user":"cron",15"password": os.environ.get("VICI_DB_PASS",""),16"database":"vicidial"17}1819# Telnyx API (swap for Twilio/SignalWire as needed)20TELNYX_API_KEY = os.environ.get("TELNYX_API_KEY","")21TELNYX_FROM_NUMBER ="+15551234567"22TELNYX_MESSAGING_PROFILE = os.environ.get("TELNYX_MSG_PROFILE","")2324# Disposition-to-SMS mapping25DISPOSITION_SMS_MAP ={26"NA":{27"template":"Hi {first_name}, we tried reaching you about {campaign_topic}. "28"Text back a good time to talk. Reply STOP to opt out.",29"delay_seconds":60,30"max_sends":2,31"cooldown_hours":2432},33"AM":{34"template":"Hi {first_name}, we left you a voicemail about {campaign_topic}. "35"Have a quick question? Text us back. Reply STOP to opt out.",36"delay_seconds":120,37"max_sends":1,38"cooldown_hours":4839},40"CALLBK":{41"template":"Hi {first_name}, confirming your callback for {callback_date}. "42"Text YES to confirm or suggest a new time. Reply STOP to opt out.",43"delay_seconds":30,44"max_sends":1,45"cooldown_hours":046},47"SALE":{48"template":"Thanks {first_name}! Your enrollment is confirmed. "49"Your rep {agent_name} is your point of contact. "50"Reply STOP to opt out.",51"delay_seconds":300,52"max_sends":1,53"cooldown_hours":054}55}5657defget_new_dispositions(since_minutes=2):58"""Pull recent call dispositions from VICIdial."""59 conn = mysql.connector.connect(**DB_CONFIG)60 cursor = conn.cursor(dictionary=True)61 cursor.execute("""
62 SELECT
63 v.uniqueid, v.lead_id, v.user AS agent_user,
64 v.status AS disposition, v.phone_number,
65 v.call_date, v.campaign_id,
66 l.first_name, l.last_name
67 FROM vicidial_log v
68 JOIN vicidial_list l ON v.lead_id = l.lead_id
69 WHERE v.call_date >= NOW() - INTERVAL %s MINUTE
70 AND v.status IN ('NA', 'AM', 'CALLBK', 'SALE')
71 AND v.phone_number NOT IN (
72 SELECT phone_number FROM sms_dnc_list
73 )
74 AND v.phone_number NOT IN (
75 SELECT phone_number FROM sms_log
76 WHERE sent_at >= NOW() - INTERVAL 24 HOUR
77 AND disposition = v.status
78 )
79 ORDER BY v.call_date DESC
80 """,(since_minutes,))81 rows = cursor.fetchall()82 cursor.close()83 conn.close()84return rows
8586defsend_sms(to_number, message):87"""Send SMS via Telnyx API."""88 resp = requests.post(89"https://api.telnyx.com/v2/messages",90 headers={91"Authorization":f"Bearer {TELNYX_API_KEY}",92"Content-Type":"application/json"93},94 json={95"from": TELNYX_FROM_NUMBER,96"to":f"+1{to_number}",97"text": message,98"messaging_profile_id": TELNYX_MESSAGING_PROFILE
99}100)101return resp.status_code ==200, resp.json()102103deflog_sms(lead_id, phone_number, disposition, message, status):104"""Log SMS send to database for tracking and deduplication."""105 conn = mysql.connector.connect(**DB_CONFIG)106 cursor = conn.cursor()107 cursor.execute("""
108 INSERT INTO sms_log
109 (lead_id, phone_number, disposition, message, status, sent_at)
110 VALUES (%s, %s, %s, %s, %s, NOW())
111 """,(lead_id, phone_number, disposition, message, status))112 conn.commit()113 cursor.close()114 conn.close()115116defprocess_dispositions():117"""Main processing loop."""118 dispositions = get_new_dispositions(since_minutes=2)119120for dispo in dispositions:121 sms_config = DISPOSITION_SMS_MAP.get(dispo["disposition"])122ifnot sms_config:123continue124125 message = sms_config["template"].format(126 first_name=dispo.get("first_name","there"),127 campaign_topic="your recent inquiry",128 callback_date="your scheduled time",129 agent_name=dispo.get("agent_user","your representative")130)131132# Check timing restriction (8 AM - 9 PM local)133 hour = datetime.now().hour
134if hour <8or hour >=21:135 log_sms(dispo["lead_id"], dispo["phone_number"],136 dispo["disposition"], message,"deferred_time")137continue138139 success, resp = send_sms(dispo["phone_number"], message)140 log_sms(dispo["lead_id"], dispo["phone_number"],141 dispo["disposition"], message,142"sent"if success else"failed")143144if __name__ =="__main__":145 process_dispositions()
Database Tables for SMS Tracking
Create the tracking tables in your VICIdial database:
1# Run disposition-triggered SMS every 2 minutes during operating hours2*/2 8-20 * * 1-6 python3 /opt/sms-integration/sms_trigger.py >> /var/log/sms/trigger.log 2>&134# Process inbound SMS replies every minute5* 8-21 * * * python3 /opt/sms-integration/sms_inbound.py >> /var/log/sms/inbound.log 2>&167# Daily SMS report807 * * * python3 /opt/sms-integration/sms_report.py >> /var/log/sms/daily_report.log 2>&1
Multi-Touch Cadence Design
Sending a single text after a missed call is table stakes. The real conversion improvement comes from building multi-touch cadences that alternate voice and text across multiple days.
The 7-Day High-Intent Cadence
For warm leads (web form submissions, inbound inquiries):
The breakup message on Day 14 is surprisingly effective. Something like: "Last attempt to reach you about [topic]. If the timing isn't right, no hard feelings. Text LATER if you want us to try again next month."
The key metric is Call + SMS contact rate compared to call-only contact rate. If you are running 18% contact rate on calls alone and 35% with the SMS cadence layered in, that is a 94% improvement in conversations per lead -- on the same list.
Handling Inbound SMS Replies
When a lead texts back, that reply needs to reach an agent fast. The response window for inbound texts is measured in minutes, not hours. A lead who texts "what time works?" at 2 PM and gets a reply at 5 PM has already moved on.
Routing Replies to Agents
Build an inbound SMS processor that checks for opt-out keywords first, then routes real replies to the agent who handled the original call:
python
1#!/usr/bin/env python32"""sms_inbound.py - Process inbound SMS replies"""34import os
5import mysql.connector
6import requests
78DB_CONFIG ={9"host":"localhost",10"user":"cron",11"password": os.environ.get("VICI_DB_PASS",""),12"database":"vicidial"13}1415OPT_OUT_KEYWORDS ={"stop","unsubscribe","cancel","end","quit"}1617defprocess_inbound_messages():18"""Fetch new inbound messages from provider and process them."""19# Pull unprocessed inbound messages from webhook table20 conn = mysql.connector.connect(**DB_CONFIG)21 cursor = conn.cursor(dictionary=True)22 cursor.execute("""
23 SELECT id, from_number, message_text, received_at
24 FROM sms_inbound_queue WHERE processed = 0
25 ORDER BY received_at ASC
26 """)27 messages = cursor.fetchall()2829for msg in messages:30 phone = msg["from_number"].replace("+1","").strip()31 text_lower = msg["message_text"].strip().lower()3233# Check for opt-out34if text_lower in OPT_OUT_KEYWORDS:35 cursor.execute("""
36 INSERT IGNORE INTO sms_dnc_list
37 (phone_number, opted_out_at, opt_out_keyword) VALUES (%s, NOW(), %s)
38 """,(phone, text_lower))39# Send confirmation40 send_sms(phone,"You have been unsubscribed. No further messages will be sent.")41else:42# Find the original agent and create a callback43 cursor.execute("""
44 SELECT v.user, v.lead_id, v.campaign_id
45 FROM vicidial_log v
46 JOIN sms_log s ON v.lead_id = s.lead_id
47 WHERE s.phone_number = %s
48 ORDER BY v.call_date DESC LIMIT 1
49 """,(phone,))50 original = cursor.fetchone()5152if original:53# Insert callback for the original agent54 cursor.execute("""
55 INSERT INTO vicidial_callbacks
56 (lead_id, list_id, campaign_id, status, user,
57 recipient, callback_time, comments)
58 SELECT lead_id, list_id, %s, 'LIVE', %s,
59 'USERONLY', NOW(), %s
60 FROM vicidial_list WHERE lead_id = %s
61 """,(original["campaign_id"], original["user"],62f"SMS reply: {msg['message_text'][:200]}", original["lead_id"]))6364 cursor.execute("UPDATE sms_inbound_queue SET processed = 1 WHERE id = %s",(msg["id"],))6566 conn.commit()67 cursor.close()68 conn.close()
This creates a VICIdial callback assigned to the original agent when a lead replies by text. The agent sees the callback in their queue with the SMS reply in the comments field, giving them context before they dial back.
SMS Reply Timing
Track your reply-to-callback time. The lead texted you -- they are engaged right now. Every minute of delay reduces the probability of a conversion:
Reply Time
Conversion Impact
Under 5 min
Peak engagement, highest close rate
5-15 min
Good, slight drop-off
15-60 min
Noticeable decline in engagement
1-4 hours
Lead has moved on, reconnection harder
4+ hours
Basically a cold re-contact
If your agents are too busy to handle callbacks quickly, create a dedicated "SMS response" campaign in VICIdial with higher priority routing.
Measuring SMS ROI
Track SMS performance separately from voice to understand the incremental value:
This query shows which disposition triggers produce the most SMS-assisted conversions. If your "NA" (no answer) texts lead to 6% conversions within 7 days but your "AM" (answering machine) texts produce only 1%, allocate more of your messaging budget to the NA workflow and rethink the AM template.
What to Build First
If you are starting from zero SMS integration, here is the priority order:
10DLC registration -- start today, it takes 1-3 weeks. Do not wait.
Consent audit -- verify you have documented one-to-one consent for every contact you plan to text. If you don't, you can not text them.
Single disposition trigger -- start with "NA" (no answer) only. Send one text within 60 seconds of a missed call. This is the highest-ROI single addition.
Opt-out handling -- make sure STOP works before you send a single message.
Multi-touch cadence -- once the basic trigger works, expand to the 7-day cadence.
Reporting and optimization -- measure delivery, response, and conversion rates. A/B test templates.
The teams at ViciStack wire SMS into dialer workflows as part of every contact rate optimization engagement because the combination of voice and text consistently outperforms either channel alone by 40-80%. If you want disposition-triggered SMS running on your VICIdial instance without spending months on the integration, we build that.