LEARN · DEBUGGING GUIDE

Twilio SMS Delivery Failure Debugging

When Twilio says 'sent' but the phone never buzzes, you're hunting carrier rejections, incorrect webhooks, or silent drops. This guide gives you the exact commands and logs to find the real failure.

IntermediateHTTP / Networking7 min read

What this usually means

Twilio delivery failures almost always involve one of three layers: (1) the Twilio API accepts the message but the upstream carrier rejects it (error codes 30003–30008, 21610); (2) the message is delivered but filtered by a carrier's spam policy or the recipient's device; or (3) your webhook or status callback configuration silently drops failure notifications. The most common non-obvious cause is carrier filtering based on content (URLs, keywords) or sending patterns (velocity, time-of-day). Another frequent issue: alphanumeric sender IDs that are not supported in the destination country, causing silent drops.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 11. Check Message Resource status via Twilio CLI: `twilio api:core:messages:list --status failed --no-limit --properties sid,errorCode,errorMessage`
  • 22. Verify status callback URL is correctly set and reachable: `curl -I https://your-webhook.com/twilio-status`
  • 33. Inspect Twilio Console > Monitor > Logs > Errors for error codes: filter by 30007, 30008, 21610, 21611
  • 44. Test with a different recipient number (same carrier) to isolate number-specific vs carrier-wide issue
  • 55. Send a test message with minimal content (no URLs, no special characters) to rule out content filtering
  • 66. Check if the destination country supports alphanumeric sender IDs; fall back to a long code or short code
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchTwilio Console > Monitor > Logs > Errors: shows all error codes with timestamps
  • searchTwilio Console > Phone Numbers > Active Numbers: verify number capabilities (SMS, MMS) and country support
  • searchTwilio Message Resource logs via API: `GET /2010-04-01/Accounts/{AccountSid}/Messages.json?Status=failed`
  • searchStatus callback webhook server logs: grep for `SmsStatus` or `ErrorCode` in your application logs
  • searchCarrier-specific delivery reports (if available via Twilio Copilot or toll-free verification)
  • searchTwilio Support tickets: carrier filtering logs if you have a premium support plan
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningCarrier spam filtering: messages with URLs, keywords like 'free' or 'click', or high velocity trigger blocks
  • warningAlphanumeric sender ID not supported in the destination country (e.g., India, US require long codes)
  • warningIncorrect or unreachable status callback URL: missing webhook or HTTPS cert failure causes silent loss
  • warningRecipient number ported to a different carrier that Twilio routes incorrectly
  • warningToll-free number not verified for A2P 10DLC: messages dropped by US carriers (error 30007)
  • warningRecipient device blocking SMS from unknown senders (iOS Filter Unknown Senders, Android spam filter)
  • warningTwilio account suspended or SMS quota exhausted: messages accepted but queued and never sent
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildSwitch to a long code or short code if alphanumeric sender ID fails in the destination country
  • buildComplete A2P 10DLC registration for toll-free numbers and use brand/template registration
  • buildAdd a grace period between messages to avoid velocity-based carrier filtering (e.g., 1 msg per 10 seconds)
  • buildReplace URLs with short domains or use link shortening services that have good carrier reputation
  • buildImplement exponential backoff and retry with different sender IDs when error 30007 or 30008 occurs
  • buildSet up a fallback webhook endpoint and monitor using a health check service (e.g., Pingdom, Datadog)
  • buildEnable delivery receipts and log all status callbacks to a database for post-mortem analysis
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedSend a test message and confirm status callback returns 'delivered' with no error code
  • verifiedCheck Twilio Console > Monitor > Logs > Errors: zero new errors after the fix
  • verifiedVerify recipient receives the message on the device (ask them to confirm with screenshot)
  • verifiedRun a script that sends 50 messages across multiple carriers and assert 100% delivered status
  • verifiedMonitor status callback endpoint logs: all messages should have a final 'delivered' or 'failed' event
  • verifiedUse Twilio's Message Feedback API to confirm carrier delivery receipt: if supported, returns 'confirmed'
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningAssuming 'sent' status means delivered—Twilio 'sent' only means accepted by carrier
  • warningIgnoring error 30007—it's not always carrier blocking; could be invalid number or country restrictions
  • warningUsing the same sender ID for high-volume sends without warm-up—triggers spam filters immediately
  • warningNot testing with a real device on the target carrier—simulators and test numbers don't show carrier filtering
  • warningHardcoding status callback URLs without HTTPS—carriers reject unencrypted callbacks
  • warningOverlooking time-of-day restrictions: some carriers block messages between 9 PM and 8 AM
  • warningFailing to log webhook payloads—when callbacks fail, you have no trace of the error
( 07 )War story

Silent SMS drops after A2P 10DLC migration

Senior Backend EngineerNode.js, Express, Twilio Node SDK, PostgreSQL, AWS Lambda, Datadog

Timeline

  1. 08:15PagerDuty alert: 'Delivery failure rate > 10%' for outbound SMS service
  2. 08:20Check Twilio Console: Message status shows 'sent' but no 'delivered' for ~15% of messages
  3. 08:25Query failed messages via API: error code 30007 with message 'Carrier blocked' on T-Mobile numbers
  4. 08:35Realize we migrated from long code to toll-free number yesterday without A2P 10DLC registration
  5. 08:45Switch sender back to long code for T-Mobile numbers; failure rate drops to 2%
  6. 09:00Submit A2P 10DLC brand and campaign registration via Twilio Console
  7. 09:30Registration approved; re-enable toll-free sender with verified brand
  8. 10:00Monitor Datadog: delivery rate back to 99.5%; no more error 30007

We migrated from a shared long code to a dedicated toll-free number to increase throughput. Within hours, about 15% of messages to T-Mobile numbers silently failed. The Twilio Console showed status 'sent' for these messages, but our status callback webhook never received a 'delivered' event. Our Datadog dashboard showed a spike in the 'failed' status for a subset of messages, but the error details were buried in the logs.

I first queried the Message Resource list with status 'failed' and filtered by error code. The error code 30007 appeared only for T-Mobile numbers. I recalled that as of 2021, US carriers require A2P 10DLC registration for toll-free numbers. We had registered the number but not completed the brand and campaign vetting. Twilio's API accepted our messages, but T-Mobile's carrier filter dropped them silently.

The immediate fix was to revert to the long code for any numbers routed to T-Mobile, using a simple conditional in our sender selection logic. After that, I submitted the A2P 10DLC registration via the Twilio Console. Once approved, we re-enabled the toll-free sender for all carriers. The delivery rate returned to normal. We added a pre-flight check that verifies A2P 10DLC status before sending from toll-free numbers.

Root cause

Toll-free number used without A2P 10DLC brand/campaign registration; T-Mobile carrier filtered all messages from unregistered toll-free senders.

The fix

Reverted to long code for T-Mobile numbers, completed A2P 10DLC registration, and added pre-flight validation.

The lesson

Always verify carrier-specific requirements (A2P 10DLC, alphanumeric sender support) before switching sender types. Monitor 'failed' status, not just 'sent'.

( 08 )Understanding Twilio Message Statuses and Error Codes

Twilio message lifecycle: queued → sent → delivered, or queued → failed. 'Sent' means accepted by carrier; 'delivered' means recipient's device acknowledged. Many developers only check 'sent' and miss silent drops.

Critical error codes: 30003 (unreachable carrier), 30004 (carrier network error), 30005 (carrier unknown), 30006 (carrier blocked), 30007 (carrier blocked—often spam or A2P 10DLC), 30008 (unknown error—carrier didn't provide reason), 21610 (carrier blocked due to content), 21611 (message too long). Use the Twilio API to list messages with these codes.

( 09 )Using Twilio's Message Feedback API for Delivery Receipts

Twilio's Message Feedback API (Beta) provides carrier-level delivery receipts when supported. It returns 'confirmed' for delivered, 'unconfirmed' for pending, or 'failed'. Not all carriers support it (T-Mobile and Verizon do; AT&T partially).

To use: after a message is sent, poll `GET /2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}/Feedback.json`. If the carrier supports it, you get a definitive answer. If not, fall back to status callback heuristics.

( 10 )A2P 10DLC Registration Pitfalls

Toll-free numbers in the US require A2P 10DLC registration as of 2023. Without it, carriers (especially T-Mobile) silently drop messages. The registration requires a brand (company) and campaign (use case). Common mistakes: using a sole proprietor brand when you're a registered business, or selecting the wrong campaign type (e.g., 'Marketing' instead of 'Two-Factor Authentication').

Twilio's Console provides a registration wizard. After submission, approval can take hours. During that window, messages may fail. Best practice: register well before switching to a toll-free number, and use a fallback sender (long code) in the interim.

( 11 )Setting Up Proper Webhook Monitoring

A misconfigured status callback URL can lose failure notifications entirely. Common issues: HTTP instead of HTTPS (carriers reject), missing trailing slash, or the endpoint returns 5xx under load.

Hardened setup: (1) Use a dedicated endpoint like `/twilio/status` with request logging. (2) Set up a dead-letter queue (SQS or similar) for failed callbacks. (3) Use a health check tool (e.g., UptimeRobot) to ping the endpoint every minute. (4) In your handler, always return 200 OK quickly, then process asynchronously.

( 12 )Carrier-Specific Filtering: T-Mobile, Verizon, AT&T

Each US carrier has different filtering policies. T-Mobile is strictest with A2P 10DLC; Verizon blocks messages with certain keywords like 'free' or 'win'; AT&T filters based on message velocity. International carriers have their own quirks: Canada blocks alphanumeric sender IDs, India requires DLT registration.

Practical approach: maintain a per-carrier sending profile. Use Twilio's Copilot to automatically select the best sender type per destination. For high-volume sends, consider using Twilio's 'Notify' service with fallback senders.

Frequently asked questions

Why does Twilio show 'sent' but the message never arrives?

Twilio's 'sent' status means the message was accepted by the upstream carrier. The carrier may then drop it due to spam filters, invalid number (number disconnected or ported), or device-side blocking. Always check error codes and look for 'undelivered' callbacks.

What does Twilio error code 30007 mean exactly?

Error 30007 means 'Carrier blocked the message.' The carrier received it but decided not to deliver. Common reasons: sender not registered for A2P 10DLC (toll-free), content triggers spam filter, or the recipient's carrier has blocked the sender ID.

How can I tell if the issue is on the carrier side vs. Twilio side?

Check the error code: codes 30000–30099 are carrier-side. Also, if the message status transitions to 'sent' but never to 'delivered', it's carrier. If it stays 'queued' or fails immediately with a 21xxx error, it's Twilio-side (e.g., invalid number, quota exceeded).

Do I need to register for A2P 10DLC if I'm using a long code?

No, A2P 10DLC applies only to toll-free numbers and short codes. Long codes (10-digit numbers) are not subject to A2P 10DLC but may have lower throughput and are often filtered for marketing content.

What's the best way to handle delivery failures in production?

Use a status callback webhook to capture all delivery events. Log every event with timestamps. Implement a retry mechanism with exponential backoff for transient errors (30004, 30005). For permanent errors (30006, 30007), escalate to an alert and consider switching sender ID.