Skip to main content
When a campaign triggers, Trackly’s message execution system queues and delivers messages efficiently. This guide explains what happens behind the scenes.

Execution Flow

1

Campaign Triggers

At the scheduled time, the campaign begins processing
2

Audience Evaluation

The system queries matching contacts from your audiences
3

Message Queuing

Individual messages are created and queued
4

Delivery

The executor sends messages via your SMS provider
5

Status Tracking

Delivery status is tracked and updated

1. Campaign Trigger

When the scheduled time arrives:
  1. The scheduler picks up the campaign
  2. Validates configuration (sending list, audiences, creatives)
  3. Begins audience evaluation
For recurring campaigns, a new execution starts each scheduled occurrence.

2. Audience Evaluation

The system queries all selected audiences:
Audience A: 5,000 contacts
Audience B: 3,000 contacts
Overlap: 500 contacts
-----
Total unique: 7,500 contacts
Filters applied:
  • 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:
  1. Select the message variant (based on percentage split or ML selection)
  2. Resolve any placeholders (, personalization)
  3. Create a QueuedTextMessage record
  4. Set the sendtime
Messages are bulk-inserted for efficiency.

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:
  1. The Coordinator polls for QueuedTextMessage records, groups them by sending list
  2. Messages are atomically claimed via a claimed_by token to prevent duplicate sends across VMs
  3. Up to 6 concurrent workers per list process messages in parallel
  4. 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:
ProviderHow Messages Are Sent
InfobipREST API batch submission
TwilioMessaging API
CMBatch 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 on MessageSent:
Fieldnulltruefalse
submittedNot yet processedAccepted by SMS providerRejected/failed to send
deliveredDelivery unknownDelivered to handsetDelivery failed

Failure Handling

Messages that can’t be delivered are recorded in the FailedMessage dead-letter collection with Discord alerts. Failure reasons include:
  • STALE — Message older than 30 minutes (skipped)
  • BLOCKED_CONTENT — Message contains blocked words
  • CONTACT_BLOCKED — Contact is on the block list (unsubscribed)
  • RATE_LIMIT_TIMEOUT — Rate limit window expired
  • PROVIDER_ERROR — SMS provider returned an error
  • NO_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 the MessageSent record. The webhook payload reflects the actual model fields:
{
  "id": "a1b2c3d4",
  "account": 12,
  "sendingList": 45,
  "messageCreative": 789,
  "fromNumber": "+15551234567",
  "toNumber": "+15559876543",
  "delivered": true,
  "sendType": "campaign",
  "filterBots": false,
  "length": 142,
  "timestamp": "2024-01-15T10:30:00Z"
}
See Delivery Status Webhook for details.

Execution Timeline

Typical timeline for a 10,000 message campaign:
TimeEvent
0:00Campaign triggers
0:01Audience evaluated (10,000 contacts)
0:02Messages queued
0:02 - 0:04Messages sent (~8,333/sec target, ~500k/min)
0:10+Delivery confirmations arrive
Actual timing depends on:
  • Audience size
  • Queue depth
  • Provider capacity
For messages with tracked links:
  1. Original URLs are replaced with short links
  2. MessageShortLink records are created
  3. When clicked, the link service logs the click
  4. User is redirected to the destination
See Link Tracking for details.

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 with failed 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
See Discord Alerts to configure.

Monitoring Execution

Dashboard

View campaign progress in real-time:
  1. Go to Schedules
  2. Click on an active campaign
  3. See live stats: sent, delivered, failed

Metrics

MetricDescription
Send RateMessages per second
Delivery Rate% successfully delivered
Failure Rate% that failed
Click Rate% that clicked a link

Troubleshooting

Check:
  • Is the campaign scheduled and not paused?
  • Is the sending list active?
  • Are provider credentials valid?
  • Is there a queue backlog?
Common causes:
  • Invalid phone numbers in list
  • Provider issues
  • Carrier blocks
  • Rate limiting exceeded
Review failed messages for error codes.
Possible reasons:
  • Large queue from concurrent campaigns
  • Provider rate limits
  • Network issues
Create a support ticket from your dashboard (Support > New Ticket) if delivery is significantly delayed.

Architecture

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Scheduler  │────▶│   Queue     │────▶│  Executor   │
│ (triggers)  │     │  (MongoDB)  │     │  (worker)   │
└─────────────┘     └─────────────┘     └─────────────┘


                                        ┌─────────────┐
                                        │   Provider  │
                                        │ (Infobip)   │
                                        └─────────────┘
The executor runs on dedicated infrastructure for reliable, high-throughput delivery.

Common Pitfalls

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_workflow exclusion 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