Every message in Trackly SMS passes through a series of states from creation to final delivery (or failure). Understanding these states helps you troubleshoot delivery issues and build reliable integrations.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.
State Diagram
Message States
| State | Terminal? | Description |
|---|---|---|
queued | No | Message is in the send queue, waiting for the executor to claim it |
sent | No | Executor submitted the message to the SMS provider |
delivered | Yes | Provider confirmed delivery to the recipient’s handset |
undelivered | Yes | Provider attempted delivery but the carrier reported failure |
failed | Yes | Hard failure — provider rejected the message or a system error occurred |
dropped | Yes | Message was removed from the queue before sending (see drop reasons below) |
orphaned | Yes | Message had no valid sending list — removed from queue and recorded to dead-letter |
Drop Reasons
When a message is dropped, thestatus_detail field contains the reason.
| Reason | Description |
|---|---|
stale | Message sat in the queue longer than 30 minutes and was discarded |
blocked_content | Message body matched a prohibited content filter (crypto, phishing, bank names) |
contact_blocked | Recipient is unsubscribed or blocked on the sending list |
inactive_list | The sending list is no longer in active status |
billing_expired | Account billing is invalid and the message exceeded the 60-minute retry window |
webhook_not_configured | BYOC (bring-your-own-carrier) list requires a webhook endpoint but none is configured |
orphan | Message had no sending_list reference — likely a data integrity issue |
Executor Pipeline
The message executor processes queued messages through a 10-step pipeline.- Poll queue — Coordinator queries
QueuedTextMessagefor unclaimed messages older than 10 seconds, sorted by send time, in batches of up to 50,000 - Group by list — Messages are grouped by
sending_listfor per-list processing and rate limiting - Validate list — Check that the sending list exists and is in
activestatus; drop messages for inactive or missing lists - Billing preflight — Verify the account has valid billing (payment method, no payment failures, free tier limit not exceeded); back off retryable messages 5 minutes or drop after 60-minute TTL
- Claim messages — Atomically claim a batch of messages using a unique claim token to prevent duplicate processing across VMs
- Filter stale — Discard messages queued longer than 30 minutes (
STALE_THRESHOLD_MINUTES) - Filter blocked content — Run message body through the blocked words checker; drop matches with
BLOCKED_CONTENTstatus - Filter blocked contacts — Check recipients against
ListContactBlock(unsubscribed/blocked contacts); drop matches withCONTACT_BLOCKEDstatus - Send via provider — Submit messages to the SMS provider (Infobip, Twilio, CM) in batches with per-list rate limiting (default 17,000 messages/min)
- Record results — Insert
RawMessagerecords, delete from queue, updateMessageSentstatus, and track billing (segments sent per account)
Webhook Events
Provider delivery reports update message status asynchronously after sending.| Message State | Webhook Event | Description |
|---|---|---|
delivered | message.delivered | Carrier confirmed delivery to handset |
undelivered | message.undelivered | Carrier reported delivery failure |
failed | message.failed | Provider rejected the message |
The
sent state is set immediately when the provider accepts the message. Final delivery status arrives via webhook, typically within seconds.Timing
| Threshold | Value | Description |
|---|---|---|
| Queue pickup delay | 10 seconds | Messages must be at least 10 seconds old before the executor picks them up |
| Stale message cutoff | 30 minutes | Messages in the queue longer than this are dropped as stale |
| Billing retry backoff | 5 minutes | Messages for accounts with billing issues are retried after 5 minutes |
| Billing block TTL | 60 minutes | After 60 minutes of billing failure, messages are permanently dropped |
| Stale claim recovery | 10 minutes | Claims held by crashed workers are released after 10 minutes |
| Cache refresh | 2 minutes | Incremental cache refresh for accounts, lists, and billing configs |
| Full cache reload | 1 hour | Complete cache rebuild as a safety net |
Next Steps
Data Model
Entity relationships
Compliance
Opt-out handling and TCPA