Execution Flow
1. Campaign Trigger
When the scheduled time arrives:- The scheduler picks up the campaign
- Validates configuration (sending list, audiences, creatives)
- Begins audience evaluation
2. Audience Evaluation
The system queries all selected audiences:- Exclude unsubscribed contacts
- Apply frequency capping (skip if sent recently)
- Verify phone numbers are valid
- Filter contacts based on timezone for TCPA compliance (contacts outside permissible sending hours are excluded)
- Exclude contacts currently in active welcome journeys (
in_workflow=True) if applicable
3. Message Queuing
For each contact:- Select the message variant (based on percentage split or ML selection)
- Resolve any placeholders (, personalization)
- Create a
QueuedTextMessagerecord - Set the sendtime
Queue Priority
Messages are processed in order:- FIFO (first-in, first-out)
- All messages from a campaign are queued together
4. Message Delivery
The Message Executor is a dedicated high-throughput worker that runs on two VMs with a 30-thread pool each:- The Coordinator polls for
QueuedTextMessagerecords, groups them by sending list - Messages are atomically claimed via a
claimed_bytoken to prevent duplicate sends across VMs - Up to 6 concurrent workers per list process messages in parallel
- Each worker: validates the message isn’t stale (30-min threshold), checks blocked content and contact block lists, applies rate limiting, sends via the SMS provider, records
MessageSent, and updates billing
Provider Integration
Each sending list has a provider configuration:| Provider | How Messages Are Sent |
|---|---|
| Infobip | REST API batch submission |
| Twilio | Messaging API |
| CM | Batch API |
Rate Limiting
Rate limiting is enforced via distributed Redis Lua scripts for cross-VM coordination:- Configurable per-list rate limits (default 17,000 messages/minute)
- Inter-batch delays to smooth throughput
- Automatic backoff on provider errors
5. Status Tracking
After sending, the system tracks: Message status is tracked via tristate boolean fields onMessageSent:
| Field | null | true | false |
|---|---|---|---|
submitted | Not yet processed | Accepted by SMS provider | Rejected/failed to send |
delivered | Delivery unknown | Delivered to handset | Delivery failed |
Failure Handling
Messages that can’t be delivered are recorded in theFailedMessage dead-letter collection with Discord alerts. Failure reasons include:
STALE— Message older than 30 minutes (skipped)BLOCKED_CONTENT— Message contains blocked wordsCONTACT_BLOCKED— Contact is on the block list (unsubscribed)RATE_LIMIT_TIMEOUT— Rate limit window expiredPROVIDER_ERROR— SMS provider returned an errorNO_RESPONSE— No response from provider
Retry Behavior
The system has built-in recovery mechanisms:- Stale claim recovery: Claims older than 10 minutes are released for retry by other workers
- Billing backoff: Billing-blocked messages get 5-minute backoff with a 60-minute TTL before dead-letter
- Worker crash recovery: Unclaimed messages are automatically picked up by other workers
Delivery Webhooks
When delivery status updates are received, they are mapped to theMessageSent record. The webhook payload reflects the actual model fields:
Execution Timeline
Typical timeline for a 10,000 message campaign:| Time | Event |
|---|---|
| 0:00 | Campaign triggers |
| 0:01 | Audience evaluated (10,000 contacts) |
| 0:02 | Messages queued |
| 0:02 - 0:04 | Messages sent (~8,333/sec target, ~500k/min) |
| 0:10+ | Delivery confirmations arrive |
- Audience size
- Queue depth
- Provider capacity
Link Tracking
For messages with tracked links:- Original URLs are replaced with short links
MessageShortLinkrecords are created- When clicked, the link service logs the click
- User is redirected to the destination
Failure Handling
Retry Logic
Coming Soon — Automatic message retry logic is not yet implemented. Currently, messages are removed from the queue after processing regardless of outcome.
Permanent Failures
Messages that fail are logged withfailed status. Common causes include:
- Invalid phone number
- Blocked/blacklisted number
- Carrier rejection (spam)
Alerts
Alerts are sent via Discord when configured. This includes notifications for:- Delivery failures
- Spam complaints
- High error rates
Monitoring Execution
Dashboard
View campaign progress in real-time:- Go to Schedules
- Click on an active campaign
- See live stats: sent, delivered, failed
Metrics
| Metric | Description |
|---|---|
| Send Rate | Messages per second |
| Delivery Rate | % successfully delivered |
| Failure Rate | % that failed |
| Click Rate | % that clicked a link |
Troubleshooting
Messages not sending
Messages not sending
Check:
- Is the campaign scheduled and not paused?
- Is the sending list active?
- Are provider credentials valid?
- Is there a queue backlog?
High failure rate
High failure rate
Common causes:
- Invalid phone numbers in list
- Provider issues
- Carrier blocks
- Rate limiting exceeded
Slow delivery
Slow delivery
Possible reasons:
- Large queue from concurrent campaigns
- Provider rate limits
- Network issues
Architecture
Common Pitfalls
Why are contacts being skipped?
Why are contacts being skipped?
Contacts may be skipped during campaign execution for several reasons:
- In-workflow exclusion: The contact is already enrolled in an active Welcome Journey and
in_workflowexclusion is enabled on the schedule. - Cooldown period: The contact received a message recently and the cooldown window (configured per-list) has not elapsed.
- TCPA quiet hours: The send falls outside the contact’s local TCPA-compliant window (typically 8 AM–9 PM in the contact’s timezone).
- Unsubscribed: The contact opted out via STOP keyword and is no longer active on the list.
- Stale message: The message sat in the queue longer than 30 minutes and was dropped to prevent outdated content from being delivered.
Next Steps
Link Tracking
Track clicks and conversions
Discord Alerts
Get notified of issues