> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-parschat.parstechai.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-parschat.parstechai.com/_mcp/server.

# Errors

> Every status and code we return, and what each one means you should do.

## Shape

```json
{
  "error": {
    "code": "plan_inactive",
    "message": "Your plan is not active.",
    "request_id": "req_8f2Kq9mR"
  }
}
```

**Always log `request_id`.** It is what lets us find your exact request when you contact support.

## Codes

| Status | `code`                      | Meaning                                                             | What to do                                                                                                                       |
| ------ | --------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `bad_request`               | A parameter is missing, unparseable, or out of range                | Fix the query string                                                                                                             |
| `401`  | `invalid_key`               | Key missing, wrong, revoked or expired                              | Check the header; if the key was revoked or expired, create a new one in the panel                                               |
| `403`  | `plan_inactive`             | Plan lapsed                                                         | Not transient. See [When a plan lapses](/documentation/guides/when-a-plan-lapses)                                                |
| `403`  | `capacity_exhausted`        | Robot or channel capacity used up                                   | Delete one, or ask sales to raise it                                                                                             |
| `403`  | `channel_not_in_plan`       | Plan does not include this channel type                             | Contact sales                                                                                                                    |
| `403`  | `attachment_not_in_plan`    | Plan does not include this kind of file                             | Contact sales                                                                                                                    |
| `403`  | `ai_not_in_plan`            | Plan does not include AI messages, so AI settings cannot be changed | Leave `settings.ai` and `settings.ai_operator` out, or contact sales. See [Robot settings](/documentation/guides/robot-settings) |
| `403`  | `forbidden`                 | The required role is not on your account                            | Contact your account manager                                                                                                     |
| `404`  | `not_found`                 | No such resource, **or not yours**                                  | Check the id                                                                                                                     |
| `409`  | `conflict`                  | Duplicate `external_id`, or channel type already present            | Use a different value                                                                                                            |
| `409`  | `handshake_pending`         | An unexpired Instagram handshake exists                             | Wait for it to expire, or reuse it                                                                                               |
| `410`  | `handshake_expired`         | The page owner took too long                                        | Start a new one                                                                                                                  |
| `422`  | `validation_failed`         | Malformed request                                                   | Fix the payload                                                                                                                  |
| `422`  | `return_url_not_configured` | No return URL configured for you                                    | Contact us before connecting                                                                                                     |
| `429`  | `rate_limited`              | Too many requests                                                   | Back off, then retry. `details` names your limit. See [Rate limits](/documentation/guides/rate-limits)                           |
| `500`  | `internal_error`            | Something failed on our side                                        | Retry with backoff; quote `request_id` if it persists                                                                            |
| `503`  | `upstream_unavailable`      | A channel provider, or our AI service, is down                      | Retry with backoff                                                                                                               |

## `400` and `422` are different failures

`400` means the request never got as far as validation: a required parameter is missing, a
timestamp will not parse, a `limit` is out of range. `422` means the request was well formed and
its contents were rejected, such as a comment rule with no reply and no DM.

```json
{
  "error": {
    "code": "bad_request",
    "message": "`from` must be earlier than `to`.",
    "request_id": "req_8f2Kq9mR",
    "details": { "from": "2026-09-30T00:00:00Z", "to": "2026-09-01T00:00:00Z" }
  }
}
```

## One code for every bad key

A missing, wrong, revoked or expired key all answer `401 invalid_key`. Telling an expired key
from an unknown one would confirm to whoever holds a leaked key that it was once real. Your key's
expiry date is in the key table in the panel (**Profile** → **Access keys & webhook**). Rotate the key
before that date.

## `404`, not `403`, for someone else's resources

If you request a resource that exists but belongs to another partner, you receive **`404`, not
`403`**. A `403` would confirm the id exists, which is information you should not be able to probe
for.

## `details`

Some errors carry a `details` object where a number helps you act:

```json
{
  "error": {
    "code": "capacity_exhausted",
    "message": "You have reached the maximum number of channels your plan allows.",
    "request_id": "req_8f2Kq9mR",
    "details": { "occupied": 10, "requested": 1, "capacity": 10 }
  }
}
```

It is **never** present on `401` or `404`, where extra detail would confirm something you should
not learn.

## Which errors are worth retrying

| Retry                       | Do not retry                                       |
| --------------------------- | -------------------------------------------------- |
| `429` (after backing off)   | `400`. The request is malformed                    |
|                             | `401`. The key will not fix itself                 |
| `500`, `503` (with backoff) | `403`. A plan or role state, not a transient fault |
|                             | `422`. The payload is wrong                        |
|                             | `409`. The conflict is real                        |

> **Info**
>
> We never name an internal host or service in an error. If you need to know *why* something
> upstream failed, send your `request_id` to your account manager.