SMS Status Codes
Every SMS message carries two status fields:
| Field | Type | Description |
|---|---|---|
status | string | Human-readable lifecycle state (see Message Status) |
delivery_code | integer | null | Numeric code from the gateway (see Delivery Codes) |
Use status for application logic. Use delivery_code to pinpoint the exact gateway reason for failures.
Message Status
The status field tracks the full lifecycle of a message through your system and the network.
| Status | Meaning |
|---|---|
queued | Accepted and waiting to be dispatched to the provider |
sent | Submitted to the SMS gateway — awaiting delivery confirmation |
delivered | Confirmed delivered to the recipient's handset |
failed | The gateway rejected or could not send the message |
undelivered | Sent to the gateway but could not be delivered to the handset |
blocked | Recipient is on the DND registry or has blacklisted this sender — do not retry |
held | Queued but paused — provider credits exhausted, will resume automatically |
cancelled | Cancelled by the user before dispatch |
Delivery Codes
The delivery_code field is set when the gateway provides a definitive outcome. It is null while a message is queued or held, and may remain null for older messages sent before this field was introduced.
Success & In-Progress
| Code | Name | When it appears |
|---|---|---|
100 | Processed | Message delivered to the handset (status: delivered) |
101 | Sent | Message submitted to and accepted by the gateway (status: sent) |
102 | Queued | Message waiting at the gateway before network submission |
Rejection Codes
| Code | Name | Meaning | Action |
|---|---|---|---|
401 | RiskHold | Gateway held the message for risk/fraud review | Wait — usually auto-released |
402 | InvalidSenderId | Sender ID not registered or approved for this network | Register or change the sender ID |
403 | InvalidPhoneNumber | Destination number is malformed or not a valid mobile number | Validate and correct the number |
404 | UnsupportedNumberType | Network or number type not supported by this route | Remove number from your list |
405 | InsufficientBalance | Gateway account has insufficient credits | Top up provider balance |
406 | UserInBlacklist | Recipient has blacklisted this sender ID | Remove from future sends |
407 | CouldNotRoute | Gateway could not find a delivery route | Retry once; if persistent, remove number |
409 | DoNotDisturbRejection | Recipient is registered on the DND registry | Remove permanently from all lists |
Error Codes
| Code | Name | Meaning | Action |
|---|---|---|---|
500 | InternalServerError | Unexpected error on our end | Contact support if persistent |
501 | GatewayError | Gateway configuration or credential issue | Check your sender ID setup |
502 | RejectedByGateway | Gateway accepted the message but the network returned a hard rejection | Check number validity |
How Codes Map to Status
delivery_code | status |
|---|---|
100 | delivered |
101 | sent |
102 | queued or held |
401 | held |
402 – 409 | failed or blocked |
500 – 502 | failed or undelivered |
Examples
Checking codes in campaign results
curl -X POST https://api.v1.talkntalk.africa/v1/sms/campaigns/messages/ \
-H "Authorization: Bearer tk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Organisation-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Content-Type: application/json" \
-d '{"campaign_id": "44210c48-5ba9-4784-b434-dd57bf1d2b46"}'{
"results": [
{
"mobile": "254712345678",
"status": "sent",
"message_status": "delivered",
"delivery_code": 100,
"error": null
},
{
"mobile": "254799999999",
"status": "failed",
"message_status": "failed",
"delivery_code": 403,
"error": "Invalid mobile number"
},
{
"mobile": "254701234567",
"status": "sent",
"message_status": "blocked",
"delivery_code": 409,
"error": "Recipient has blocked promotional messages from this sender."
}
]
}Filtering failures by code in Python
import requests
API_KEY = "tk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ORG_ID = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"X-Organisation-Id": ORG_ID,
"Content-Type": "application/json",
}
r = requests.post(
"https://api.v1.talkntalk.africa/v1/sms/campaigns/messages/",
headers=HEADERS,
json={"campaign_id": "44210c48-5ba9-4784-b434-dd57bf1d2b46", "page_size": 1000},
)
data = r.json()
# Numbers to remove permanently (DND or blacklisted)
do_not_contact = [
m["mobile"] for m in data["results"]
if m["delivery_code"] in (406, 409)
]
# Temporary failures safe to retry
retryable = [
m["mobile"] for m in data["results"]
if m["delivery_code"] in (407, 501, 502)
]
print("Remove permanently:", do_not_contact)
print("Safe to retry:", retryable)Best Practices
- 406 / 409 — Remove these numbers immediately and permanently. Retrying will result in continued rejection and may get your sender ID flagged.
- 403 / 404 — Validate your contact list before sending. Use a number validation service if you maintain a large database.
- 402 — Ensure your sender ID is registered and approved before running campaigns.
- 405 — Monitor your provider balance proactively; messages held due to depletion (
102) resume automatically once topped up. - 502 — Usually a transient network issue. Safe to retry once after 15–30 minutes.