# Handle Failures
URL: /docs/sending/handle-failures
LLM index: /llms.txt
Description: Every send-time and delivery-time error code, retryability, and specific remediation steps for each failure type.

# Handle Failures

When a message can't be sent or delivered, the API returns a structured error object. Here's what each error means and what to do about it.

## Error structure

All errors follow the same shape:

```json
{
  "error": {
    "type": "opted_out",
    "message": "This contact has opted out of messages."
  }
}
```

Some errors include a `detail` object with field-level information:

```json
{
  "error": {
    "type": "validation_error",
    "message": "Could not perform the request with the provided parameters.",
    "detail": {
      "body": ["can't be blank"]
    }
  }
}
```

## Errors at send time

These errors come back synchronously when you call `POST /accounts/{account_id}/messages`.

### `opted_out` (422)

The recipient has previously replied STOP. Carriers require you to honour opt-outs. Do not attempt to send to this contact until they explicitly re-opt-in by texting START.

**Fix:** Check contact opt-out state before sending. Subscribe to `contact.opted_out` webhook events to update your database in real time.

### `demo_message_limit` (403)

You've sent 25 messages from the demo number. The demo is for exploration — it's not for production use.

**Fix:** [Register your business](../registration/index) and [purchase a phone number](../phone-numbers/purchasing).

### `link_size_limit` (422)

The message body contains more URLs than the campaign's registered `includes` allows, or the total URL content exceeds carrier limits.

**Fix:** Reduce the number of links in the message, or enable link shortening on your account.

### `no_phone_number` (422)

No `from` number was specified and the account has no default phone number configured.

**Fix:** Either set a default phone number on the account in the dashboard, or always pass a `from` value.

### `phone_number_not_found` (422)

The phone number ID passed as `from` doesn't exist or doesn't belong to this account.

**Fix:** Confirm the phone number ID is correct and belongs to the account you're sending from.

## Errors at delivery (via webhooks)

These appear in `message.failed` webhook events after a message was initially accepted. The `failure_reason` field on the event contains the slug.

### `unreachable_destination`

The recipient's number is disconnected, out of service, or unreachable on the carrier network.

**What to do:** Flag the contact as undeliverable in your system. Continued attempts to an unreachable number don't help and count against your volume.

<Warning>
**Permanent undeliverability.** When Surge receives `unreachable_destination`, `invalid_mobile_number`, or `unreachable_carrier` for a number, that number is permanently flagged as undeliverable in Surge's carrier database. All future sends to it (from any account) are cancelled immediately without hitting the carrier. If a number becomes reachable again (e.g., the subscriber reactivates their SIM), the flag clears when a successful delivery is recorded. Stop sending to failed numbers — it has no effect, and your campaign volume quota is still consumed.
</Warning>

### `invalid_content_type`

An MMS attachment's content type isn't supported by the recipient's carrier or handset.

**What to do:** Retry with a different format (prefer `image/jpeg` or `image/png`) or send the content as a link instead of an attachment.

### `message_filtered`

The carrier's spam filter blocked the message. This can happen when message content matches spam patterns, when a campaign isn't fully approved, or when a number isn't attached to an active campaign.

**What to do:**
1. Confirm the sending number is attached to an active, approved campaign
2. Check that message content matches your registered campaign description and sample messages
3. Avoid link shorteners that aren't trusted by carriers (use Surge's built-in link shortening instead)

### Complete failure reason reference

All possible values for `failure_reason` in `message.failed` events:

| Reason | Meaning | Retryable? |
|---|---|---|
| `account_archived` | The sending account has been archived | No |
| `account_locked` | The platform has been locked due to a policy violation | No. Contact support |
| `attachment_retrieval_error` | Surge couldn't fetch the attachment URL when building the MMS | Yes. Fix the URL or hosting |
| `blocked` | The number has been explicitly blocked at the carrier level | No |
| `carrier_error` | A transient error from the carrier network | Yes. Surge retries automatically |
| `configuration_error` | The account's carrier credentials are misconfigured | No. Contact support |
| `daily_message_cap_reached` | The campaign's daily T-Mobile volume cap was hit | Yes. Automatically next day |
| `geo_not_enabled` | The destination country isn't in the platform's allowed regions | No. Contact support to enable the region |
| `invalid_attachment_url` | The attachment URL is malformed or not publicly accessible | Yes. Fix the URL |
| `invalid_content_type` | MMS attachment type unsupported by the recipient's carrier | Yes. Use a supported format |
| `invalid_mobile_number` | The number is not a valid mobile number (e.g., a landline) | No. Permanently undeliverable |
| `matching_sender_and_receiver` | The `from` and `to` numbers are the same | No. Sending to yourself isn't supported |
| `message_filtered` | Blocked by carrier spam filters or Surge's content policy | Investigate content before retrying |
| `message_too_big` | The message body exceeds the 1,600-character limit | Yes. Shorten the message |
| `mms_not_supported` | The recipient's carrier or handset doesn't support MMS | Yes. Surge falls back to SMS automatically |
| `opted_out` | The contact has opted out of this conversation | No. Wait for explicit opt-in |
| `premium_rate_number` | The destination is a premium-rate number | No |
| `suspected_fraud` | Surge's fraud detection flagged the send | No. Contact support |
| `unknown_destination` | The number doesn't exist on any carrier network | No. Permanently undeliverable |
| `unknown_error` | An error occurred that Surge couldn't categorise | Investigate with support |
| `unreachable_carrier` | The destination carrier network is unreachable | No. Permanently undeliverable |
| `unreachable_destination` | The number is disconnected or out of service | No |
| `unregistered_number` | The sending number isn't registered with the carrier for this message type | No. Contact support |
| `unsupported_route` | No valid carrier route exists for this number pair | No. Contact support |

## Validation errors

### `validation_error` (422)

A request field is missing or invalid. The `detail` map contains per-field errors.

```json
{
  "error": {
    "type": "validation_error",
    "message": "Could not perform the request with the provided parameters.",
    "detail": {
      "to": ["is not a valid E.164 phone number"],
      "body": ["can't be blank"]
    }
  }
}
```

**Fix:** Correct the flagged fields. Phone numbers must include the country code with a `+` prefix (e.g. `+18015551234`, not `8015551234`).

## Full error reference

See the [Error Reference](../api-reference/errors) for the complete list of error types, HTTP status codes, and additional context.
