> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracklysms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Execution

> How Trackly delivers your messages at scale

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

## Execution Flow

<Steps>
  <Step title="Campaign Triggers">
    At the scheduled time, the campaign begins processing
  </Step>

  <Step title="Audience Evaluation">
    The system queries matching contacts from your audiences
  </Step>

  <Step title="Message Queuing">
    Individual messages are created and queued
  </Step>

  <Step title="Delivery">
    The executor sends messages via your SMS provider
  </Step>

  <Step title="Status Tracking">
    Delivery status is tracked and updated
  </Step>
</Steps>

## 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 ({{link1}}, 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:

| Provider | How Messages Are Sent     |
| -------- | ------------------------- |
| Infobip  | REST API batch submission |
| Twilio   | Messaging API             |
| CM       | Batch API                 |

### Rate Limiting

Rate limiting is enforced **atomically across every worker**, so limits hold globally rather than per machine:

* 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`:

| 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 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:

```json theme={null}
{
  "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 [Webhook Events](/api-reference/v2/webhooks/events) for details.

## 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                  |

Actual timing depends on:

* Audience size
* Queue depth
* Provider capacity

## Link Tracking

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](/guides/link-tracking/overview) for details.

## Failure Handling

### Retry Logic

<Info>
  **Coming Soon** — Automatic message retry logic is not yet implemented. Currently, messages are removed from the queue after processing regardless of outcome.
</Info>

### 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](/integrations/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

| Metric            | Description              |
| ----------------- | ------------------------ |
| **Send Rate**     | Messages per second      |
| **Delivery Rate** | % successfully delivered |
| **Failure Rate**  | % that failed            |
| **Click Rate**    | % that clicked a link    |

## Troubleshooting

<AccordionGroup>
  <Accordion title="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?
  </Accordion>

  <Accordion title="High failure rate">
    Common causes:

    * Invalid phone numbers in list
    * Provider issues
    * Carrier blocks
    * Rate limiting exceeded

    Review failed messages for error codes.
  </Accordion>

  <Accordion title="Slow delivery">
    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.
  </Accordion>
</AccordionGroup>

## Architecture

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Scheduler  │────▶│   Queue     │────▶│  Executor   │
│ (triggers)  │     │ (datastore) │     │  (worker)   │
└─────────────┘     └─────────────┘     └─────────────┘
                                               │
                                               ▼
                                        ┌─────────────┐
                                        │     SMS     │
                                        │  Provider   │
                                        └─────────────┘
```

The executor runs on dedicated infrastructure for reliable, high-throughput delivery.

## Common Pitfalls

<Accordion title="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_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.
</Accordion>

## Next Steps

<CardGroup cols={2}>
  <Card title="Link Tracking" icon="link" href="/guides/link-tracking/overview">
    Track clicks and conversions
  </Card>

  <Card title="Discord Alerts" icon="bell" href="/integrations/discord-alerts">
    Get notified of issues
  </Card>
</CardGroup>
