> ## 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.

# Errors

> Error shape, status codes, and what each means.

All errors follow a consistent JSON shape:

```json theme={null}
{
  "detail": "Human-readable description of what went wrong"
}
```

Some errors carry structured details:

```json theme={null}
{
  "detail": [
    {
      "loc": ["body", "predictive_target_abandon"],
      "msg": "must be between 0 and 0.030 (TRAI cap)",
      "type": "value_error"
    }
  ]
}
```

That's Pydantic's 422 format — field-level validation errors.

## Status codes

| Status      | Meaning                                                             |
| ----------- | ------------------------------------------------------------------- |
| 200         | Success                                                             |
| 201         | Created                                                             |
| 204         | Success, no body                                                    |
| 400         | Malformed request (rare — most validation uses 422)                 |
| 401         | Missing or bad API key                                              |
| 403         | Key lacks the required scope (future)                               |
| 404         | Resource doesn't exist OR belongs to another tenant                 |
| 409         | State conflict (e.g. `/predictive/pause` on a non-running campaign) |
| 422         | Validation error — body failed a Pydantic constraint                |
| 429         | Rate-limited                                                        |
| 500         | Our bug. Email support with the request timestamp.                  |
| 502/503/504 | Upstream momentarily unavailable. Retry with backoff.               |

## Tenant-scoped 404s

When you GET a resource that exists but belongs to another tenant,
you get a **404**, not a 403. We never want to confirm existence
across tenant boundaries (otherwise an attacker could enumerate
other customers' campaign IDs).

## Validation errors you'll see often

### `predictive_target_abandon > 0.030`

```json theme={null}
{
  "detail": [{
    "loc": ["body", "predictive_target_abandon"],
    "msg": "Input should be less than or equal to 0.03",
    "type": "less_than_equal"
  }]
}
```

TRAI caps abandon rate at 3.0%. We can't accept a higher target.

### Inverted ratio range

```json theme={null}
{
  "detail": "predictive_max_dial_ratio (1.5) must be >= predictive_min_dial_ratio (3.0)"
}
```

When you PATCH both ratio fields in the same payload, they must
satisfy `max >= min`. PATCHing just one is always allowed (we defer
to the DB CHECK).

### Invalid phone

```json theme={null}
{"detail": "Invalid phone number"}
```

We only accept Indian numbers (10 digits, optional 0/91 prefix). For
non-Indian use cases, contact us — cross-border dialing has its own
compliance track.

## Abandon rate exceeded (business-level)

Not an HTTP error — it's a lifecycle event.

When a predictive campaign's abandon rate crosses its target for 60s
sustained, the backend auto-pauses the campaign. You'll see:

* The campaign's `status` flip to `paused`
* A `campaign.paused` webhook with `data.source = "abandon_rate_exceeded"`

Re-enable via `POST /api/campaigns/{id}/predictive/resume` (dashboard
JWT auth, not the v1 API key). See the operator runbook in your
dashboard for guidance.
