# SMS delivery error codes: retry, remove the number, or fix your sender

Twilio, Sinch and MessageBird delivery error codes sorted by what to do next, and which of them a phone lookup can warn you about before you send.

Date: 2026-09-27

An SMS delivery error code says why a message failed: the number is not an active line, the line cannot take text messages, the handset was out of reach at that moment, the recipient opted out, or your own sender was refused. Sinch 150 and MessageBird 1 mean the number is not an active line, so remove it; Twilio 30006, Sinch 152 and MessageBird 11 mean the line cannot take text messages, so reach the person another way; Twilio 30003 and MessageBird 29 mean the handset was off or out of reach, so retry later; Twilio 21610 and MessageBird 103 mean the recipient opted out, so stop; Twilio 30007 and 30034 mean your sender or your content was refused, and removing the number would only hide that. Twilio publishes no delivery code that proves a number is gone.

This guide sorts the codes of three SMS providers by what to do next, from each provider's own documentation, and says which failures a phone lookup can warn you about before you send. The code lists are the ones Spaw's status webhooks read, so every code in the tables below is recorded the way this page describes.

## Five things a failed receipt can mean

| What failed | What to do | Twilio | Sinch | MessageBird | Can a lookup warn you first? |
| --- | --- | --- | --- | --- | --- |
| The number is not an active line, or its line is barred (`permanent`) | Remove the number; for a barred line, stop until the recipient has the bar lifted | none | `150`, `151` | `1` | Only when the regulator's table shows no carrier holding the number's block, which the lookup refuses as invalid |
| The line cannot take SMS (`not_sms_capable`) | Stop texting it and reach the person another way; for `30006` from a short code, Twilio suggests a long-code fallback first, and Sinch `154` can pass once the recipient is off the roaming network | `30006` | `152`, `154`, `204`, `212` | `10`, `11`, `21` | A hint where the plan tells landlines from mobiles (`line_type: fixed_line`), which the plans of the US, Canada, several other +1 areas and Denmark do not; nothing for a plan, SIM or network without SMS |
| The recipient opted out (the `opted_out` outcome) | Stop, and keep it stopped | `21610` | none in Spaw's table | `103` | Only from your own records (`meta.opted_out`) |
| The handset or its network, this time (`transient`) | Retry later, and give up after repeats | 4 codes: `30003`, `30004`, `30005`, `30008` | 20 codes, such as `153`, `159`, `201` | 21 codes, such as `27`, `28`, `29` | No: these describe the handset or its network when the message was sent, or are too vague to pin on the number (MessageBird marks `2` and `9`, both about the handset's registration, permanent) |
| Your sender, content or sending rate (`sender_side`) | Fix your registration, content or rate | 12 codes, such as `30007`, `30034`, `30032`, `30018` | 40 codes, such as `60`, `62`, `64`, `310` | 11 codes, such as `104`, `105`, `110` | No lookup of the recipient can; MessageBird `131`, a destination its pumping guard forbids, is covered below |

The words in brackets are the `failure_class` Spaw records for a failed delivery, and `opted_out` is recorded as an outcome of its own, never as a failure. The providers' own labels differ: Sinch and MessageBird mark most codes permanent or temporary ("intermediate" in MessageBird's table), and Twilio marks none. Spaw's classes answer a narrower question than the providers' labels do: is the failure about the number? A code the provider calls permanent because the sender is unregistered, such as MessageBird `104`, is `sender_side` here, because the number is fine.

## The codes that are about the number

Thirteen codes across the three providers say something about the recipient's number rather than the moment or the sender (MessageBird `131`, a destination its pumping guard forbids, is kept with the sender-side codes, for the reason given further down). The descriptions paraphrase each provider's documentation: [Twilio's error dictionary](https://www.twilio.com/docs/api/errors), [Sinch's SMPP error codes](https://developers.sinch.com/docs/sms/smpp/error-codes), which Spaw reads from the `code` field of its delivery reports, and [MessageBird's SMS API reference](https://developers.messagebird.com/api/sms-messaging/), all checked 27 September 2026.

| Provider | Code | Provider's name | Spaw records | What the provider's documentation says | Before sending |
| --- | --- | --- | --- | --- | --- |
| Twilio | `30006` | Landline or unreachable carrier | `not_sms_capable` | The destination is a landline, or the carrier cannot be reached from a short code | A hint from `fixed_line`, where the plan tells landlines from mobiles |
| Twilio | `21610` | Attempt to send to unsubscribed recipient | `opted_out` | The recipient sent STOP or another opt-out keyword to your number, sender or Messaging Service; Twilio also lists a recently reassigned number as a cause | Your own suppression list |
| Sinch | `150` | Unknown subscriber | `permanent` | The number is not active with the carrier | Only if the regulator's table shows no holder for its block |
| Sinch | `151` | Call barred | `permanent` | The recipient's line is barred; Sinch says it might be best to stop sending to it for now, until the recipient has the bar lifted | No |
| Sinch | `152` | Teleservice not provisioned | `not_sms_capable` | The line has no SMS subscription | A hint from `fixed_line`, where the plan tells landlines from mobiles |
| Sinch | `154` | Facility not supported | `not_sms_capable` | SMS is not provisioned in the network the recipient is visiting, so it fails while they roam there and can pass once they are back on their home network | No |
| Sinch | `204` | Facility not supported | `not_sms_capable` | SMS is not provisioned on the recipient's SIM | No |
| Sinch | `212` | Device not short message equipped | `not_sms_capable` | The device cannot receive texts, such as a data-only SIM | No |
| MessageBird | `1` | EC_UNKNOWN_SUBSCRIBER | `permanent` | The number is not associated with an active line | Only if the regulator's table shows no holder for its block |
| MessageBird | `10` | EC_BEARERSERVICE_NOT_PROVISIONED | `not_sms_capable` | The subscriber has two numbers and this one is not enabled for SMS | No |
| MessageBird | `11` | EC_TELESERVICE_NOT_PROVISIONED | `not_sms_capable` | Not a mobile, such as an IoT number, or a plan without SMS | A hint from `fixed_line`, where the plan tells landlines from mobiles |
| MessageBird | `21` | EC_FACILITY_NOT_SUPPORTED | `not_sms_capable` | The receiving network does not support SMS | No |
| MessageBird | `103` | EC_SUBSCRIBER_OPTEDOUT | `opted_out` | The recipient opted out; only an opt-in keyword such as START from them reverses it | Your own suppression list |

Two labels in the providers' tables read differently from Spaw's. Sinch marks `212` temporary, and MessageBird marks `10`, `11` and `21` intermediate; Spaw keeps all four with the not-SMS-capable codes because each describes the line, the SIM or the network rather than the handset's state at that moment. MessageBird also files `21` under the status reason "received network error", which it calls temporary. Not-SMS-capable failures are never scored, so reading these four that way never counts them against the number.

Sinch has no opt-out row. Its SMPP table describes `255` and `257`, both personal service barring, as an end user who opted out of messages, but gives `255` a second meaning for toll-free senders: a message the carrier blocked for spam filtering. Its [10DLC and toll-free error page](https://developers.sinch.com/docs/sms/smpp/10dlc-errors) (checked 27 September 2026) maps toll-free `255` to spam and only `257` to a STOP. Spaw's table reads neither, so a receipt carrying either is recorded as an unclassified failure. When you know the recipient opted out, report the number yourself with the outcome `opted_out`.

## Twilio has no code that proves a number is gone

Spaw reads eighteen of Twilio's codes. The two about the number are in the table above; these are the other sixteen, and the four that can be about the handset are all read as `transient`, including 30005, whose name reads like a number that is gone.

| Code | Twilio's name | Spaw records |
| --- | --- | --- |
| `30003` | Unreachable destination handset | `transient` |
| `30004` | Message blocked | `transient` |
| `30005` | Unknown destination handset | `transient` |
| `30008` | Unknown error | `transient` |
| `30001` | Queue overflow | `sender_side` |
| `30002` | Account suspended | `sender_side` |
| `30007` | Message filtered | `sender_side` |
| `30017` | Carrier network congestion | `sender_side` |
| `30018` | Destination carrier requires sender ID pre-registration | `sender_side` |
| `30019` | Content size exceeds carrier limit | `sender_side` |
| `30022` | US A2P 10DLC - Rate Limits Exceeded | `sender_side` |
| `30023` | US A2P 10DLC - Daily Message Cap Reached | `sender_side` |
| `30024` | Numeric Sender ID Not Provisioned on Carrier | `sender_side` |
| `30027` | US A2P 10DLC - T-Mobile Daily Message Limit Reached (deprecated; Twilio says it no longer generates it) | `sender_side` |
| `30032` | Toll-Free Number Has Not Been Verified | `sender_side` |
| `30034` | US A2P 10DLC - Message from an Unregistered Number | `sender_side` |

Twilio's page for 30005 says the destination number "is unknown and may no longer exist", and then lists a handset that is switched off or out of signal, a number that cannot receive SMS and a problem at the carrier among the possible causes. A code with four explanations, two of them passing, cannot justify removing a number on one sighting, so Spaw counts it with the other temporary failures: two of them with no delivery after them put the number on your suppression list. The same goes for 30004, whose causes include a blocked destination and filtering of your content.

Twilio documents two more codes about the number: 21211, an invalid `To` number, and 21614, "'To' number is not a valid mobile number". Both describe the `To` number of the API request that creates the message, and neither page says whether the error comes back on that request or on a status callback. Spaw's Twilio table does not read them, so a callback carrying either is recorded as an unclassified failure. 21614, which Twilio says may be a landline, is the one a `fixed_line` answer can hint at. For messages sent through a Messaging Service, 21610 arrives on the status callback: Twilio's [Advanced Opt-Out documentation](https://www.twilio.com/docs/messaging/tutorials/advanced-opt-out) (a Messaging Service feature, checked 27 September 2026) says later messages to a recipient who opted out fail with 21610 asynchronously.

The twelve sender-side codes are about 10DLC and toll-free registration, sender IDs, content, sending rate, daily caps, carrier congestion and the account. Almost none of them says anything about the recipient: Twilio's 30024 page also lists a destination that recently ported, or is not provisioned for SMS, among its causes, which is one more reason Spaw never scores these codes against the number. The same failure meets the next person on your list until the registration, the content, the sending rate or the account is fixed, or until the day's cap resets (30023 resets at midnight Pacific time) or the carrier's congestion clears (30017, congestion in MMS traffic, which Twilio retries before it returns the code). Sinch's equivalents include `60`, an originator whose campaign is not active, `62`, a keyword that triggered a filter, and `64`, a quota exceeded; its [10DLC and toll-free error page](https://developers.sinch.com/docs/sms/smpp/10dlc-errors) (checked 27 September 2026) maps the carriers' reasons to them. MessageBird's include `104` and `105`, an unregistered sender or content, `130`, which its surge guard returns when it sees a traffic-pumping pattern in your sending, and `131`, which it returns when it has marked the destination itself as forbidden for suspected traffic pumping. Spaw files `131` as sender-side so that it is never scored against the number; one lookup cannot say a number is pumped, and [what the default SMS policy lets through](/guides/sms-pumping-what-the-default-sms-policy-lets-through) covers the rules that watch your own traffic for the pattern.

## What a lookup can warn you about before you send

**A block nobody holds.** A number answers `valid: false` with the reason `unassigned_block` when the regulator's table lists its block as unassigned, whether the block was never handed out or was returned and is resting before reuse, or when its block is missing from a table that lists every assigned block and was synced within the last 45 days. The SMS policy blocks every invalid number, so a flow that checks `ok_to_send` never sends to it. The test number +1 202 555 0104 answers exactly that, `ok_to_send: false` with `blocked_by: "invalid"`, and like every test number it costs nothing. This is the only part of the "remove the number" row a lookup can see: a number that was assigned and later disconnected usually sits in a block its carrier still holds, and passes. Which regulators publish such a table, and whether each is complete, is on [the phone data sources page](/tools/phone-number-lookup/sources).

The live carrier-network check (`hlr: true`) is not enabled on the service: `hlr_checked` answers false and nothing extra is charged.

**A fixed line, where the plan can tell.** Where the numbering plan tells landlines from mobiles, the answer carries `line_type: fixed_line` from the plan; a London 020 number answers that way, and the page for [the United Kingdom](/tools/phone-number-lookup/countries/gb) gives the digits its mobile numbers start with. It is a hint rather than a verdict, because some fixed lines are enabled for text. The default SMS policy blocks no line type; add `fixed_line` to `blocked_line_types` if you would rather not try. In the United States and Canada no real number answers `fixed_line`: the plan answers `fixed_line_or_mobile`, and the holder of the number's block can settle it only to `mobile` or `voip`, for the reasons in [why US numbers cannot be split into mobile and landline](/guides/why-us-numbers-cannot-be-split-into-mobile-and-landline). The same holds for Puerto Rico, the Dominican Republic, Jamaica, Guam, the Northern Mariana Islands, the US Virgin Islands and Denmark, and for part of India's plan; the other +1 areas, such as the Bahamas and Trinidad and Tobago, do answer `fixed_line`. A `fixed_line_or_mobile` answer predicts nothing about 30006.

**Not toll-free.** `line_type: toll_free` is not a sign that a number cannot take SMS. In the United States and Canada toll-free numbers can be enabled for texting, which is why Twilio has a code, 30032, for sending from one that has not been verified.

**Not the plan, the SIM or the network.** MessageBird `10`, `11` and `21` and Sinch `154`, `204` and `212` describe a subscriber's second number, a plan or SIM without SMS, a data-only device or a network that does not carry SMS. No public data shows any of these before a message is sent; only where `11` means the number is not a mobile at all can the `fixed_line` hint above flag a landline.

**An opt-out, from your own list only.** Once a recipient has opted out and you or your provider reported it, the number is on your suppression list with the reason `opted_out`, a single lookup answers `meta.opted_out: true`, and under the default policy that answer is `ok_to_send: false` with `blocked_by: "opted_out"`. No public data says who opted out of whose messages. `ok_to_send` is a fraud and deliverability decision: it is not consent to message anyone and not a do-not-call scrub, and Spaw knows only the opt-outs you or your provider report to it.

**A number that delivers but should not get the code.** A number published on an SMS-receiving website takes messages, so the trouble with it is not a failed delivery and no error code flags it. Spaw can flag it before sending when one of the lists it syncs carries the number: the test number +1 202 555 0103 answers `is_disposable: true` and, under the default policy, `blocked_by: "disposable"`. When one slips through, report it as `abused`, not as undelivered.

## Sending the receipts back

Point each provider's status callback at `https://spaw.co/api/v1/phone/feedback/{provider}/{key}`, with `twilio`, `sinch` or `messagebird` as the provider and your account's feedback key, and the codes above are read from `ErrorCode` (Twilio), `code` (Sinch) or `statusErrorCode` (MessageBird). Spaw reads Sinch's SMS API delivery reports; Sinch's SMS API reference (checked 27 September 2026) says that API is available to its existing customers and that new customers send through its Conversation API, whose callbacks this webhook does not read. A MessageBird report without an error code falls back to its status reason, and `unknown subscriber` without the code stays `transient`, because MessageBird files eight codes under that reason and only `1` is read as permanent. The [webhook reference](/docs/api/sms-status-webhook) lists the fields each provider sends. If your provider is not one of these, [`POST /api/v1/phone/feedback`](/docs/api/report-phone-feedback) takes the same outcomes with a `failure_class` of your own reading.

What happens next depends on the class:

- **`permanent`, `transient` and unclassified failures count against the number.** While the latest report about it in the last 90 days is a failure, your later lookups of the number carry [`reported_undelivered`](/docs/signals/phone/reported_undelivered), +50. One permanent failure, or two of the others with no delivery or verification after them, puts it on your suppression list, which batch, bulk and monitored runs answer as `suppressed`, free. A later delivery or verified code takes it off.
- **`not_sms_capable` and `sender_side` failures are kept and never scored.** They appear in `undelivered_by_class` on your [feedback summary](/docs/api/phone-feedback-summary), which is where a jump in `sender_side` shows up as a registration or content problem rather than a list problem.
- **An opt-out is recorded as `opted_out`.** It never scores, and it keeps the number on your suppression list until you remove the entry.

A code none of the tables lists is recorded as an unclassified failure, which counts against the number like a `transient` one: it adds `reported_undelivered` (+50) while it is the latest report, and two unclassified or transient failures with no delivery or verification after them put the number on your suppression list. Delivered, undelivered and opted-out reports only ever affect your own account's lookups; no other account reads them.

## What to do next

- Set the status callback URL in each provider's console to the [SMS status webhook](/docs/api/sms-status-webhook) for that provider.
- Check `undelivered_by_class` on the [feedback summary](/docs/api/phone-feedback-summary) after a campaign: sender-side failures are yours to fix, not your list's.
- Read [what an unassigned block means](/guides/what-an-unassigned-block-means) for the one "remove the number" case a lookup refuses before you send: a block nobody holds.
- Set the line types and signals you refuse once, as [gating SMS verification codes with the SMS policy](/guides/gate-sms-verification-codes-with-the-sms-policy) describes.
- For the email side of the same question, read [SMTP bounce codes explained](/guides/smtp-bounce-codes-explained).

Reference: https://spaw.co/guides/sms-delivery-error-codes-explained
