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

# Vobiz Messaging API Errors

> Error reference for the Vobiz Messaging API - the {"error"} response body, what each HTTP status code means for messages, numbers, broadcasts, 10DLC and RCS, the providerError diagnostic, idempotent retries, and the difference between 201, 202 and 422 on a send.

Every error from the Messaging API is a flat JSON object with one message:

```json theme={null}
{
  "error": "recipient has opted out: +14155550123"
}
```

The HTTP status code tells you the class of problem; `error` tells you the specific cause in plain text. 10DLC responses that originate at the US registry add a second key, [`providerError`](#the-providererror-field). This page covers the Messaging API under `https://api.vobiz.ai/api/v1/sms`. For the rest of the platform (voice, numbers, accounts) see the [API error reference](/docs/errors).

## Status codes

| Status | Meaning | Typical causes |
| - | - | - |
| `400 Bad Request` | The request body or a parameter is invalid | Malformed JSON; a required field missing (`name is required`, `url is required`, `phoneNumber is required`); a bad enum or range (`ratePerSecond must be between 1 and 50`); MMS validation (`invalid mms: ...`); a 10DLC field problem (`invalid request: ...`) |
| `401 Unauthorized` | Credentials missing or wrong | `X-Auth-ID` or `X-Auth-Token` absent or not valid for an account |
| `402 Payment Required` | Your balance cannot cover the charge | `insufficient balance` on [`POST /numbers/purchase`](/docs/sms/api/numbers/purchase-number) |
| `404 Not Found` | The resource is not on your account | `message not found`, `conversation not found`, `broadcast not found`, `brand not found`, `campaign not found`, `document not found`, `sender not found`, `template not found`, `country not found`, `number not found` |
| `409 Conflict` | The action does not fit the resource's current state | Broadcast lifecycle (`cannot edit a broadcast that is running`, `cannot delete a broadcast that is completed`, `cannot pause a broadcast that is draft`); number purchase (`you already have this number`, `a purchase of this number is already in progress`, `number is no longer available`); number release (`the purchase of this number is still in progress`); 10DLC (`conflict: brand still has 2 campaign(s); delete them first`) |
| `413 Payload Too Large` | An upload exceeds 10 MB | `document exceeds 10MB` on [`POST /10dlc/documents`](/docs/sms/api/10dlc/upload-document); `file is larger than 10MB` on [`POST /broadcasts/{id}/recipients`](/docs/sms/api/broadcasts/upload-recipients) |
| `422 Unprocessable Entity` | The request is well-formed but cannot be carried out | Recipient opted out (`recipient has opted out: +14155550123`); country rules (`SMS numbers are not sold for country XX`, `India uses DLT sender IDs, not long codes`, `this country needs regulatory documents; contact support`); broadcast launch checks (`template is empty`, `the broadcast has no valid recipients to send to`, `+14155550100 is not an SMS number on this account`); too many recipients (`too many recipients: the limit is 10000 per broadcast`); registry rejection (`registration provider rejected the request: ...` with `providerError`) |
| `429 Too Many Requests` | The US 10DLC registry rate-limited the request | Returned on 10DLC endpoints when the registry answers 429; the body carries `providerError`. Wait, then retry |
| `500 Internal Server Error` | Something unexpected happened on the Vobiz side | `internal error`. Retry; if it persists, contact support with the request details |
| `502 Bad Gateway` | The upstream network or registry rejected or did not complete the request | `routing failed: no provider available for channel=sms from=... to=...`; `at least one recipient required`; `number provider could not complete the purchase; your wallet has been refunded`; `registration provider unavailable` |
| `503 Service Unavailable` | The feature is temporarily unavailable | `number purchasing is not available`; `billing is unavailable; try again later`; `number search is busy; try again in a few seconds` |

<Note>
  `404` applies to resources that exist on another account as well as to ids that do not exist. The body is always `{"error": "<resource> not found"}`.
</Note>

## Sending: 201, 202, 422 and 502

[`POST /messages`](/docs/sms/api/messages/send-message) creates one message record per recipient. The status code summarises the whole request, and the body is the first record created.

| Status | Meaning |
| - | - |
| `201 Created` | Every recipient was accepted. This includes an idempotent replay and an RCS send that fell back to SMS with `failoverToSms`. |
| `202 Accepted` | At least one record was created and at least one recipient hit a problem. A rejected recipient is stored with `status: failed` and the reason in `errorMessage`. Check each recipient with [`GET /messages`](/docs/sms/api/messages/list-messages) filtered by `tag` or `to`. |
| `422 Unprocessable Entity` | No record was created and the first problem was an opt-out: `recipient has opted out: +14155550123`. |
| `502 Bad Gateway` | No record was created for another reason, most often routing: `routing failed: no provider available for channel=sms from=+14155550100 to=+14155550123`. Check that `from` is a messaging number on your account with [`GET /numbers`](/docs/sms/api/numbers/list-numbers). |

A message that was accepted but later failed on the network does not change the HTTP response; it changes the message `status` to `failed`, with `errorCode` and `errorMessage` from the delivery report. Watch for it through [webhooks](/docs/sms/webhooks) or [`GET /messages/{id}`](/docs/sms/api/messages/get-message).

## Retrying safely with idempotencyKey

Set `idempotencyKey` on every send. If you retry a request with the same key, Vobiz returns the original message with `201` instead of sending again, whatever the first attempt's outcome. The key is scoped to your account; with several recipients the key is applied per recipient as `<key>:<to>`.

Retry on `502` and `503` with the **same** key after a short delay. A `400`, `402`, `404`, `409` or `422` describes something in the request or the account state, so fix the cause before retrying. The same advice applies to [`POST /broadcasts/{id}/launch`](/docs/sms/api/broadcasts/launch-broadcast): read the `422` message, fix the draft, launch again.

<Warning>
  Retrying a send without an `idempotencyKey` can deliver the same message twice and charge you twice. Make the key part of your request from the start.
</Warning>

## The providerError field

10DLC brand and campaign calls are relayed to the US 10DLC registry. When the registry rejects or cannot process a request, the response carries both keys:

```json theme={null}
{
  "error": "registration provider rejected the request: the brand's tax ID could not be verified",
  "providerError": "..."
}
```

`error` is the message to show your users. `providerError` is an opaque diagnostic string: log it and quote it to support if you need help, but do not parse it or build logic on its contents. Status codes on these responses:

| Status | Meaning |
| - | - |
| `400`, `404`, `409`, `422`, `429` | The registry answered with that code; the body explains why |
| `502` | The registry could not be reached or answered with an unexpected error: `registration provider unavailable` |
| `503` | The registration service is temporarily unavailable; retry later |

Validation that Vobiz performs before contacting the registry uses the `invalid request: ...` prefix, for example `invalid request: brandType is required` or `invalid request: toll-free campaigns use POST /v1/10dlc/campaigns/toll-free`.

## RCS senders and templates

The [sender and template](/docs/sms/rcs-senders) endpoints report most problems as `400` with the platform's message in `error`, for example `provider create sender failed: ...`. `GET /senders/{id}` and `GET /senders/{senderId}/templates/{templateName}` return `404` for unknown ids.

## Money-moving endpoints

[`POST /numbers/purchase`](/docs/sms/api/numbers/purchase-number) debits your balance before buying the number. If the upstream provider then cannot complete the purchase, the response is `502` and the message states what happened to the money: `number provider could not complete the purchase; your wallet has been refunded` or `...; your refund is being processed by support`. [`POST /numbers/{e164}/release`](/docs/sms/api/numbers/release-number) ends the rental without a refund of the current period.

<Warning>
  Purchases, releases and broadcast launches spend money or send real messages. Run them only with your own credentials and after confirming the request is what you intend.
</Warning>

## Handling errors in code

<CodeGroup>
  ```python Python theme={null}
  import requests

  BASE = "https://api.vobiz.ai/api/v1/sms"
  HEADERS = {
      "X-Auth-ID": "MA_XXXXXXXX",
      "X-Auth-Token": "YOUR_AUTH_TOKEN",
      "Content-Type": "application/json",
  }

  def send(payload, retries=3):
      for attempt in range(retries):
          r = requests.post(f"{BASE}/messages", headers=HEADERS, json=payload, timeout=30)
          if r.status_code in (201, 202):
              return r.json()
          body = r.json() if r.headers.get("content-type", "").startswith("application/json") else {}
          if r.status_code in (502, 503) and attempt < retries - 1:
              continue  # same idempotencyKey, safe to retry
          raise RuntimeError(f"{r.status_code}: {body.get('error', r.text)}")

  msg = send({
      "from": "+14155550100",
      "to": ["+14155550123"],
      "text": "Acme: Your order #4821 has shipped. Reply STOP to opt out.",
      "idempotencyKey": "order-4821-shipped",
  })
  print(msg["id"], msg["status"])
  ```

  ```javascript Node theme={null}
  const BASE = "https://api.vobiz.ai/api/v1/sms";
  const headers = {
    "X-Auth-ID": "MA_XXXXXXXX",
    "X-Auth-Token": "YOUR_AUTH_TOKEN",
    "Content-Type": "application/json",
  };

  async function send(payload, retries = 3) {
    for (let attempt = 0; attempt < retries; attempt++) {
      const res = await fetch(`${BASE}/messages`, {
        method: "POST",
        headers,
        body: JSON.stringify(payload),
      });
      if (res.status === 201 || res.status === 202) return res.json();
      const body = await res.json().catch(() => ({}));
      if ((res.status === 502 || res.status === 503) && attempt < retries - 1) continue; // same key, safe
      throw new Error(`${res.status}: ${body.error ?? res.statusText}`);
    }
  }

  const msg = await send({
    from: "+14155550100",
    to: ["+14155550123"],
    text: "Acme: Your order #4821 has shipped. Reply STOP to opt out.",
    idempotencyKey: "order-4821-shipped",
  });
  console.log(msg.id, msg.status);
  ```
</CodeGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Platform error reference" icon="triangle-exclamation" href="/docs/errors">
    Status codes for the voice and account APIs.
  </Card>

  <Card title="Send a message" icon="paper-plane" href="/docs/sms/api/messages/send-message">
    Request schema and response codes.
  </Card>
</CardGroup>

## Before you go live

<CardGroup cols={2}>
  <Card title="Compliance overview" icon="shield-check" href="/docs/sms/compliance/overview">
    Avoid registry rejections before they happen.
  </Card>

  <Card title="Messaging numbers" icon="hashtag" href="/docs/sms/numbers">
    Buying, billing and number registration status.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.