An accepted API request is not a delivered SMS
The first troubleshooting mistake is assuming a successful send request means the phone received the message. In most SMS APIs, a successful request means the platform accepted or queued the message. Delivery status arrives later through callbacks, delivery receipts, message logs, or reports.
That delay is normal. The message may move from accepted to queued, sent, delivered, undelivered, failed, expired, or unknown depending on the provider and downstream carrier signals. Some delivery receipts are strong evidence. Others only prove that one stage of the route completed. Your application needs to preserve the whole lifecycle.
| State | What it usually means | Troubleshooting action |
|---|---|---|
| accepted | The platform accepted the request. | Store the message ID and wait for a later signal. |
| queued | The message is waiting for a sender, route, or scheduled send time. | Check account limits, sender pool, and country routing. |
| sent | The message left the provider toward a carrier. | Do not mark the user flow complete yet. |
| delivered | A delivery receipt indicates delivery success. | Treat as successful but keep raw receipt details for support. |
| undelivered | A carrier or provider reported non-delivery. | Inspect error code, country, carrier, sender identity, and content. |
| failed | The message could not be sent or was rejected before delivery. | Fix request, route, account, registration, or policy issue before retrying. |
| expired | The carrier retry window ended before delivery. | Stop blind retries and offer an alternate path. |
| unknown | No useful final delivery information is available. | Keep visible in support tooling and avoid promising certainty. |
Triage by country, carrier, sender, template, and time
A single failed OTP is a support event. A country-specific failure spike is an incident. Your troubleshooting view should group messages by the dimensions that explain carrier behavior instead of only showing a flat message log.
- Country and country code, because registration rules, sender identity support, and receipt quality vary by market.
- Carrier or network code when available, because filtering and outages can be carrier-specific.
- Sender identity, because blocked sender IDs, unregistered 10DLC, and wrong originators often show up as delivery problems.
- Template or message category, because content filtering can affect one message shape while other traffic succeeds.
- Provider route and account, because account limits, geo permissions, and routing changes can create sudden failures.
- Time window, because outages, throttling, and temporary handset unavailability can be missed if support only inspects one message.
Notilify should make these dimensions first-class fields. If the only durable data is recipient, body, and final status, every support investigation becomes guesswork.
A support-ready troubleshooting playbook
- Confirm the recipient number is normalized to E.164 and belongs to the country the user selected.
- Check whether the message is still pending, scheduled, or queued before calling it failed.
- Inspect the provider message ID, latest callback, raw provider status, normalized status, and error code.
- Compare the same template across countries, carriers, and sender identities to identify patterns.
- Check the sender identity approval state for that country and use case.
- Look for content issues: links, URL shorteners, unsupported characters, promotional language in transactional templates, or missing brand context.
- Avoid unlimited resends. Apply cooldowns, idempotency keys, abuse controls, and an alternate verification path.
The playbook should be visible to engineering and customer support. Support does not need provider-console access for every ticket, but it does need an honest status trail and a next action that does not invent certainty.
Webhook observability makes delivery debugging possible
Delivery webhooks are operational data, not just a convenience callback. Save the raw payload, provider signature result, normalized status, provider timestamp, received timestamp, message ID, and any error code. Then process callbacks idempotently so duplicate or out-of-order events do not corrupt the message lifecycle.
type SmsDeliveryEvent = {
messageId: string;
provider: string;
rawStatus: string;
normalizedStatus: 'queued' | 'sent' | 'delivered' | 'undelivered' | 'failed' | 'expired' | 'unknown';
errorCode?: string;
country: string;
carrier?: string;
senderIdentity: string;
receivedAt: string;
};Once the lifecycle is stored this way, the dashboard can answer the useful questions: which countries are degrading, which templates fail, which sender identity needs review, and which customers need a fallback path.
Incident checklist for deliverability drops
- Check provider status pages and internal route change history.
- Compare failure rate against the previous hour, previous day, and same weekday baseline.
- Segment by country, carrier, sender identity, template, provider account, and error code.
- Pause risky retry loops before they create spend spikes or carrier throttling.
- Switch critical flows to approved fallback sender identities or alternate verification channels where available.
- Write a short incident note with affected countries, start time, user impact, current workaround, and next review time.
Give support the delivery trail
Notilify can store the message lifecycle your team needs to debug failed, delayed, and unknown SMS outcomes.
Get your API key