> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surge.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Reference

> Every API error type by category: HTTP status codes, causes, and messaging-layer delivery failures with descriptions.

All errors follow a consistent structure:

```json theme={null}
{
  "error": {
    "type": "error_type_slug",
    "message": "Human-readable description."
  }
}
```

Some error types include a `detail` map with extra context. The most common case is validation errors, which surface per-field errors:

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

***

## Authentication and authorization

| Type              | HTTP status | Description                                            |
| ----------------- | ----------- | ------------------------------------------------------ |
| `missing_api_key` | 401         | No `Authorization` header was provided                 |
| `invalid_api_key` | 401         | The API key doesn't exist or has been revoked          |
| `demo_api_key`    | 403         | The action requires a live key but a demo key was used |
| `platform_locked` | 403         | The project has been locked due to a policy violation  |

***

## Resource errors

| Type                | HTTP status | Description                                                                |
| ------------------- | ----------- | -------------------------------------------------------------------------- |
| `not_found`         | 404         | The requested resource doesn't exist                                       |
| `account_not_found` | 404         | The account ID in the path doesn't exist or isn't accessible               |
| `account_archived`  | 410         | The account has been archived and no longer accepts mutating operations    |
| `bad_request`       | 400         | The request body is malformed (unparseable JSON, wrong content type, etc.) |

***

## Validation errors

| Type                 | HTTP status | Description                                                                         |
| -------------------- | ----------- | ----------------------------------------------------------------------------------- |
| `validation_error`   | 422         | One or more fields failed validation. Check the `detail` map for field-level errors |
| `invalid_pagination` | 422         | The cursor value is invalid or the pagination parameters are out of range           |

***

## State and business rule errors

| Type                      | HTTP status | Description                                                                                  |
| ------------------------- | ----------- | -------------------------------------------------------------------------------------------- |
| `account_not_ready`       | 422         | The account doesn't have enough information to perform this action (e.g., create a campaign) |
| `accounts_limit_exceeded` | 422         | The project has reached its maximum number of accounts                                       |
| `link_size_limit`         | 422         | The message contains too many URLs                                                           |
| `opted_out`               | 422         | The recipient has opted out; sending is not permitted                                        |
| `campaign_locked`         | 422         | The campaign cannot be updated in its current status                                         |

***

## Rate and quota errors

| Type                 | HTTP status | Description                                      |
| -------------------- | ----------- | ------------------------------------------------ |
| `demo_message_limit` | 403         | The demo number's message limit has been reached |

***

## Server errors

| Type                    | HTTP status | Description                                                                                               |
| ----------------------- | ----------- | --------------------------------------------------------------------------------------------------------- |
| `internal_server_error` | 500         | Something went wrong on Surge's side. These alert our team automatically; contact support if they persist |

***

## Messaging-layer errors

These errors appear as `failure_reason` in `message.failed` webhook events after a message was accepted. They come from the carrier network or Surge's delivery pipeline, not from the Surge API directly.

| Type                           | Description                                                                             |
| ------------------------------ | --------------------------------------------------------------------------------------- |
| `account_archived`             | The sending account was archived before the message could be dispatched                 |
| `account_locked`               | The account was locked due to a policy violation                                        |
| `attachment_retrieval_error`   | Surge couldn't fetch the attachment URL to build the MMS                                |
| `blocked`                      | The number has been explicitly blocked at the carrier level                             |
| `carrier_error`                | A transient error from the carrier network                                              |
| `configuration_error`          | The account's carrier credentials are misconfigured                                     |
| `daily_message_cap_reached`    | The campaign's daily T-Mobile volume cap was exceeded                                   |
| `geo_not_enabled`              | The destination country is not in the project's allowed regions                         |
| `invalid_attachment_url`       | The attachment URL is malformed or not publicly accessible                              |
| `invalid_content_type`         | The MMS attachment type isn't supported by the recipient's carrier                      |
| `invalid_mobile_number`        | The destination is not a valid mobile number (permanently undeliverable)                |
| `matching_sender_and_receiver` | The sending and receiving number are identical                                          |
| `message_filtered`             | The carrier's spam filter or Surge's content policy blocked the message                 |
| `message_too_big`              | The message body exceeds the 1,600-character limit                                      |
| `mms_not_supported`            | The recipient's carrier or handset doesn't support MMS                                  |
| `opted_out`                    | The contact has opted out of receiving further messages; the message was not dispatched |
| `premium_rate_number`          | The destination is a premium-rate number                                                |
| `suspected_fraud`              | Surge's fraud detection system flagged the send                                         |
| `unknown_error`                | An unclassified error occurred                                                          |
| `unreachable_carrier`          | The destination carrier network is unreachable (permanently undeliverable)              |
| `unreachable_destination`      | The recipient's number is disconnected or out of service                                |
| `unregistered_number`          | The sending number isn't registered with the carrier for this message type              |
| `unsupported_route`            | No valid carrier route exists for this number pair                                      |

**Permanently undeliverable numbers.** When Surge records `invalid_mobile_number` or `unreachable_carrier` for a destination, that number is flagged globally. All future sends to it (from any account) are cancelled immediately without reaching the carrier. This prevents wasted volume quota. The flag clears automatically if a future send to that number succeeds.

See [Handle Failures](../sending/handle-failures) for per-reason remediation guidance.

***

## Phone number purchase errors

| Type                           | Description                                       |
| ------------------------------ | ------------------------------------------------- |
| `no_matching_numbers`          | No numbers match the requested type and area code |
| `phone_numbers_limit_exceeded` | The account has reached its phone number limit    |

***

## Quick lookup

| Question                                                     | Answer                                                         |
| ------------------------------------------------------------ | -------------------------------------------------------------- |
| Which errors mean "this number will never be delivered to"?  | `invalid_mobile_number`, `unreachable_carrier`                 |
| Which errors should I retry automatically?                   | `carrier_error`, `daily_message_cap_reached` (next day)        |
| Which 4xx codes mean the request was malformed?              | `bad_request` (400) and `validation_error` (422 with `detail`) |
| Where do I find live remediation guidance?                   | [Handle Failures](../sending/handle-failures)                  |
| Where do delivery-time errors appear in the webhook payload? | The `failure_reason` field on `message.failed` events          |
