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

# Error reference

> Comprehensive catalog of HTTP status codes and error types across all API endpoints.

All Yotel API errors return a consistent JSON shape. This page catalogs
every error type, including domain-specific errors from voice agents and
AI sessions.

## Error response format

```json theme={null}
{
  "detail": "Human-readable error description"
}
```

For validation errors (422), Pydantic returns a list:

```json theme={null}
{
  "detail": [
    {
      "loc": ["body", "predictive_target_abandon"],
      "msg": "ensure this value is less than or equal to 0.03",
      "type": "value_error.number.not_le"
    }
  ]
}
```

***

## Standard HTTP status codes

| Status | Meaning               | When                                                              |
| ------ | --------------------- | ----------------------------------------------------------------- |
| `200`  | OK                    | Successful read or update                                         |
| `201`  | Created               | Resource created (campaigns, leads, voice agents, keys, webhooks) |
| `204`  | No Content            | Successful delete                                                 |
| `400`  | Bad Request           | Malformed request or query param out of range                     |
| `401`  | Unauthorized          | Missing, malformed, or expired auth token                         |
| `403`  | Forbidden             | Valid token but insufficient scope or permission                  |
| `404`  | Not Found             | Resource doesn't exist **or** belongs to another tenant           |
| `409`  | Conflict              | State-based conflict (see domain-specific errors below)           |
| `422`  | Validation Error      | Request body fails Pydantic validation                            |
| `429`  | Too Many Requests     | Rate limit exceeded — check `Retry-After` header                  |
| `500`  | Internal Server Error | Unexpected server error                                           |

<Note>
  Cross-tenant access always returns `404`, never `403`. This prevents
  attackers from confirming whether a resource exists in another tenant.
</Note>

***

## Authentication errors

| Error         | Status | `detail`                                         | Cause                                          |
| ------------- | ------ | ------------------------------------------------ | ---------------------------------------------- |
| Malformed key | `401`  | `"API key must start with yt_live_ or yt_test_"` | Key doesn't match expected format              |
| Invalid key   | `401`  | `"Invalid API key"`                              | Key not found or revoked                       |
| Scope denied  | `403`  | `"Scope 'X' required"`                           | Key lacks the required scope for this endpoint |
| Rate limited  | `429`  | `"Rate limit exceeded"`                          | Per-key request budget exhausted               |

***

## Campaign errors

| Error                   | Status | Cause                                                          |
| ----------------------- | ------ | -------------------------------------------------------------- |
| `InvalidCampaignState`  | `409`  | Invalid state transition (e.g., starting a completed campaign) |
| Abandon rate validation | `422`  | `predictive_target_abandon` exceeds 0.03 (TRAI hard cap)       |
| Predictive ratio range  | `422`  | `predictive_max_ratio < predictive_min_ratio`                  |
| Delete while running    | `409`  | Cannot delete a running campaign — pause or stop first         |

***

## Voice agent errors

| Error                    | Status | `detail`                            | Cause                                                                                                                                                              |
| ------------------------ | ------ | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `NoVoiceAgentConfigured` | `424`  | `"No voice agent resolved"`         | The override hierarchy (flow → campaign → tenant default) resolved no agent. Set a [tenant default](/api-reference/voice-agents/defaults) or pin one per campaign. |
| `VoiceAgentInUse`        | `409`  | `"Voice agent has active sessions"` | Cannot hard-delete a voice agent with in-progress calls. Use [archive](/api-reference/voice-agents/crud#archive-a-voice-agent) instead.                            |
| `MaxConcurrentExceeded`  | `429`  | `"Max concurrent sessions reached"` | The voice agent's `max_concurrent` limit is hit. Wait for active sessions to end or increase the limit.                                                            |

***

## AI session errors

| Error                          | Status | `detail`                                   | Cause                                                                                                                 |
| ------------------------------ | ------ | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `AISessionTerminated`          | `410`  | `"AI session already ended"`               | Sending a control verb to a call that has already hung up.                                                            |
| `ConferenceMembershipRequired` | `403`  | `"Whisper requires conference membership"` | `whisper`, `barge`, or `monitor` verbs require the call to be in an active conference. Call `conference_start` first. |
| Token mismatch                 | `403`  | `"Token call_id mismatch"`                 | The callback token's embedded `call_id` doesn't match the URL path.                                                   |
| Cross-tenant                   | `404`  | `"Call not found"`                         | The call belongs to a different tenant than the auth context.                                                         |

***

## Recording errors

| Error             | Status | Cause                                                                                                                                   |
| ----------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| Not yet available | `409`  | Recording upload pipeline still processing. Wait for the [`call.recording_ready`](/webhooks/event-catalog#callrecording_ready) webhook. |
| Call not found    | `404`  | Call doesn't exist or belongs to another tenant.                                                                                        |

***

## Webhook management errors

| Error           | Status | Cause                                                                                       |
| --------------- | ------ | ------------------------------------------------------------------------------------------- |
| URL not HTTPS   | `422`  | Webhook URL must use `https://`                                                             |
| Test tenant URL | `422`  | Test tenants can only register `localhost`, `127.0.0.1`, `*.ngrok.io`, or `*.test.yotel.in` |

***

## Validation patterns

Common validation rules enforced across the API:

| Rule                 | Endpoints    | `detail`                                        |
| -------------------- | ------------ | ----------------------------------------------- |
| Indian phone format  | Leads        | 10 digits, optional 0/91 prefix; 5–20 chars raw |
| WebSocket URL format | Voice agents | Must start with `ws://` or `wss://`             |
| TRAI abandon cap     | Campaigns    | `predictive_target_abandon ≤ 0.03`              |
| Rate limit range     | API keys     | `rate_limit_per_min` must be 1–100,000          |
| Stats window range   | Agents       | `window_hours` must be 1–720                    |
| Bulk lead limit      | Leads        | Max 500 leads per `:bulk` request               |

***

## Rate limit headers

Successful responses include:

| Header                  | Description                    |
| ----------------------- | ------------------------------ |
| `X-RateLimit-Limit`     | Per-minute budget              |
| `X-RateLimit-Remaining` | Remaining requests this window |

On `429` responses:

| Header        | Description                            |
| ------------- | -------------------------------------- |
| `Retry-After` | Seconds until the next minute boundary |

Both SDKs auto-retry on 429 with exponential backoff (configurable).
