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

# Errors, idempotency, and rate limits

> Error codes, safe retries with idempotency keys, and UgaText API rate limits.

## Error format

Errors return a non-2xx status code and a JSON body:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Authentication failed. Provide a valid 'Client-ID' and 'Client-Secret' in the request headers."
  }
}
```

## HTTP status codes

| Status | Meaning |
| - | - |
| 200 | Request succeeded. For sends, the message is accepted and `queued`. |
| 201 | Resource created (API key, webhook, Sender ID request, or uploaded document). |
| 400 | The request is invalid. Fix it before retrying. |
| 401 | Authentication failed. |
| 402 | Wallet balance is too low. |
| 403 | Sender ID not approved, message blocked by compliance checks, or IP not whitelisted. |
| 404 | The resource does not exist. |
| 409 | Conflict, for example a Sender ID already registered by another account. |
| 410 | The OTP code expired or the session is already verified. |
| 422 | The phone number is not valid, or the recipient opted out. |
| 429 | Rate limit exceeded or verification limit reached. |

## Error codes

| Status | Code | Meaning |
| - | - | - |
| 400 | `INVALID_REQUEST` | Missing recipient, message, or other required field. |
| 400 | `INVALID_URL` | The webhook URL is not a valid HTTPS URL. |
| 401 | `INVALID_API_KEY` | Missing, invalid, or revoked credentials. |
| 402 | `INSUFFICIENT_BALANCE` | Wallet balance is too low. Top up and retry. |
| 403 | `SENDER_ID_NOT_APPROVED` | Missing, blank, or unapproved Sender ID. |
| 403 | `COMPLIANCE_BLOCKED` | The message failed UCC compliance checks. See [Compliance](https://docs.ugatext.com/compliance.md). |
| 403 | `IP_NOT_WHITELISTED` | The request came from an IP not on the key's [whitelist](https://docs.ugatext.com/authentication.md#ip-whitelisting). |
| 404 | `NOT_FOUND` | The resource does not exist. |
| 422 | `INVALID_PHONE_NUMBER` | The phone number is not valid. |
| 422 | `NUMBER_SUPPRESSED_ON_OPT_OUT_LIST` | The recipient opted out of promotional messages. |
| 429 | `RATE_LIMIT_EXCEEDED` | More than 600 requests per minute. |

### Verify API errors

| Status | Code | Meaning |
| - | - | - |
| 400 | `MISSING_REQUIRED_FIELDS` | A required field is missing. |
| 400 | `INVALID_CODE` | The code is wrong. |
| 404 | `VERIFICATION_NOT_FOUND` | Unknown `verification_id`. |
| 410 | `CODE_EXPIRED` | The code has expired. |
| 410 | `SESSION_ALREADY_VERIFIED` | The session was already verified. |
| 429 | `COOLDOWN_ACTIVE` | Wait before starting another verification. |
| 429 | `MAX_ATTEMPTS_EXCEEDED` | Too many incorrect attempts. |

See [OTP verification](https://docs.ugatext.com/otp-verification.md#verify-errors) for the full flow.

### Sender ID request errors

| Endpoint | Status | Meaning |
| - | - | - |
| `POST /sender-ids/requests` | 400 | Invalid name or missing purpose. |
| `POST /sender-ids/requests` | 409 | Sender ID already registered by another account. |
| `GET /sender-ids/requests/{id}` | 404 | Request not found. |
| `POST /sender-ids/requests/{id}/documents` | 400 | Invalid file format or file over 5 MB. |
| `POST /sender-ids/requests/{id}/pay` | 400 | Required documents missing, or already paid. |
| `POST /sender-ids/requests/{id}/pay` | 402 | `INSUFFICIENT_BALANCE` in wallet. |

## Errors by endpoint

| Endpoint | Possible errors |
| - | - |
| `GET /auth/verify` | 401 |
| `POST /sms/send` | 400, 401, 402, 403, 422, 429 |
| `POST /sms/bulk` | 400, 401, 402, 403 |
| `GET /sms/balance` | 401 |
| `GET /sms/status/{id}` | 401, 404 |
| `GET /sms/history` | 401 |
| `POST /sms/schedule` | 401 |
| `POST /sms/schedule/{id}/cancel` | 401, 404 |
| `POST /sms/schedule/{id}/reschedule` | 401, 404 |
| `GET /sender-ids` | 401 |
| `POST /webhooks` | 400 `INVALID_URL`, 401 |
| `GET /webhooks` | 401 |
| `DELETE /webhooks/{id}` | 401 |
| `POST /auth/api-keys`, `GET /auth/api-keys` | 401 |
| `POST /auth/api-keys/{id}/revoke` | 401, 404 |
| `POST /verify/start` | 400, 401, 429 |
| `POST /verify/check` | 400, 401, 404, 410, 429 |

## Message statuses

| Status | Meaning |
| - | - |
| `queued` | Accepted and waiting for dispatch. This is the initial state. |
| `sent` | Handed to the carrier. |
| `delivered` | Delivered to the handset. |
| `failed` | Not delivered. Check `failure_reason` on `GET /sms/status/{id}`. |

Responses also include a `mode` field: `live` for billable carrier sends, `sandbox` for [sandbox](https://docs.ugatext.com/sandbox.md) sends, and `mixed` on bulk batches that contain both.

## Bulk results

`POST /sms/bulk` returns one result per recipient. A failed recipient has `success: false`, a `null` `message_id`, and a description in `error`. Use `dispatched_count` and `failed_count` to check the batch totals.

## Rate limits

The API allows **600 requests per minute** (10 requests per second). Requests above the limit return `429 RATE_LIMIT_EXCEEDED`.

## Idempotency

Add an `Idempotency-Key` header to `POST /sms/send` to retry safely without sending or billing twice.

```http theme={null}
Idempotency-Key: 8f14e45f-ceea-467f-a8f1-4e45fceea67f
```

* Use a unique value up to 128 characters, such as a UUID v4.
* Keys are cached for 24 hours per account.
* Replayed requests return the header `X-Cache-Lookup: HIT (Idempotent)`.

## Compliance pre-check

Call `POST /compliance/inspect` to run the 21-rule UCC compliance and segmentation check before sending. It flags prohibited spam keywords, deceptive financial text, and length issues. See [Compliance](https://docs.ugatext.com/compliance.md#pre-flight-compliance-check).


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