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

# Errors and limits

> How the Symphony API reports failures, what each error code means, when to retry, and which usage limits exist.

## What it is

This page explains how the Symphony API reports failures, what each error code means, when it's worth retrying, and which usage limits exist. Use it to build your integration's error handling.

## 1. How to read an error

Every error response has an HTTP `4xx` or `5xx` status and a JSON body. The stable error code can come in **two different places**, depending on the endpoint.

<Tabs>
  <Tab title="Format A: code in error">
    ```json theme={null}
    {
      "detail": "File not found",
      "error": {
        "code": "RESOURCE_NOT_FOUND",
        "message": "The content may have been deleted or moved.",
        "action": "Refresh the page. If the problem continues, contact support.",
        "retryable": false,
        "request_id": "req_a1b2c3"
      }
    }
    ```
  </Tab>

  <Tab title="Format B: code in detail">
    Common in chat, for example unavailable model and usage limit.

    ```json theme={null}
    {
      "detail": {
        "code": "CONSUMPTION_LIMIT_REACHED",
        "message": "I can't answer this request. You will be able to use AI again on 01/11/2026.",
        "action": "If you need access before that date, contact an administrator to increase your limit.",
        "retryable": false,
        "request_id": "req_d4e5f6"
      }
    }
    ```
  </Tab>
</Tabs>

**Special cases, with no code:**

| Situation | Body |
| - | - |
| Request limit per IP | `{ "detail": "Too many requests. Please try again.", "retry_after": 60 }` |
| Company token quota | `{ "detail": { "error": "quota_exceeded", "reset_at": "...", "used": 0, "limit": 0 } }` |
| Unexpected server failure | May arrive with no JSON body. Treat it as `UNKNOWN_ERROR` |

### Reading the code safely

```python theme={null}
def error_code(response):
    try:
        body = response.json()
    except ValueError:
        return "UNKNOWN_ERROR"

    detail = body.get("detail")
    if isinstance(body.get("error"), dict):
        return body["error"].get("code", "UNKNOWN_ERROR")
    if isinstance(detail, dict) and "code" in detail:
        return detail["code"]
    if isinstance(detail, dict) and detail.get("error") == "quota_exceeded":
        return "QUOTA_EXCEEDED"
    if response.status_code == 429:
        return "RATE_LIMITED"
    return "UNKNOWN_ERROR"
```

### Rules for coding against errors

* **Use the `code`, never the text.** The `code` is stable. `message` and `action` can be rewritten at any time.
* **Messages are always in English.** The API doesn't translate. If you show the error to your user, translate it from the `code`.
* **`retryable`** tells you whether retrying has a chance of working.
* **Store the `request_id`** in your logs. That's how the Symphony team finds what happened.

## 2. HTTP status codes

| Status | What happened | Retry? |
| -: | - | - |
| `400` | Invalid body, a parameter the model doesn't accept, or content too large | No. Fix the request |
| `401` | Key invalid, revoked, or replaced. Also shows up when you lack permission to create something | No. Generate the key again or request the permission |
| `402` | Your usage limit ran out, or your access was blocked | No. Only the administrator can fix it, or the start of the next month |
| `403` | No permission for the action, or API key disabled in the environment | No |
| `404` | Resource doesn't exist, or you don't have access to it. Includes a model outside the catalog | No. Check the ID and `X-Enterprise-Id` |
| `409` | The agent exists, but its base model was removed | No. The agent must be fixed in the interface |
| `422` | Required field missing or with the wrong type. The body lists the fields in `detail` | No |
| `429` | Too many requests, or company quota used up | Yes, after waiting |
| `5xx` | Temporary failure of Symphony or the AI provider | Yes, with increasing waits |

<Note>
  For security, a resource that exists but that you can't see responds `404`, not `403`. That way the API doesn't reveal the existence of other people's data.
</Note>

## 3. Error code catalog

### Model and agent

| `code` | Meaning | Retry? | What to do |
| - | - | :-: | - |
| `MODEL_UNAVAILABLE` | The `model` doesn't exist in your company's catalog or you don't have access | No | Send the correct `X-Enterprise-Id` and copy the `id` from `GET /api/models` |
| `AGENT_BASE_MODEL_UNAVAILABLE` | The agent lost its base model | No | Ask whoever manages the agent to choose another base model |
| `MODEL_TEMPORARILY_UNAVAILABLE` | The model didn't respond this time | Yes | Try again or use another model |
| `INVALID_PARAMETER` | A parameter (like `temperature`, `max_tokens`, or `stop`) is outside what the model accepts. The `message` says which | No | Adjust or remove the parameter |
| `INVALID_CONFIGURATION` | Information is missing from the agent or connection configuration | No | Contact the administrator |
| `API_CREDENTIALS_INVALID` | The credentials Symphony uses with the AI provider have a problem | No | Contact the administrator or use another model |
| `CONTEXT_LIMIT_EXCEEDED` | The question, history, and files together exceeded what the model can handle | No | Reduce the content, summarize the history, or use a model with a larger window |
| `EMPTY_RESPONSE` | The model finished without generating content | Yes | Try again |

### Availability and network

| `code` | Meaning | Retry? | What to do |
| - | - | :-: | - |
| `RATE_LIMITED` | Too many requests in a short time, on Symphony or the AI provider | Yes | Wait for `Retry-After` and try again |
| `TIMEOUT` | The model took longer than expected | Yes | Try again in a moment |
| `NETWORK_ERROR` | The connection dropped while sending | Yes | Try again |
| `SERVICE_UNAVAILABLE` | The service couldn't respond right now | Yes | Try again with increasing waits |
| `STREAM_INTERRUPTED` | The stream ended before the response was complete | Yes | Generate the response again |

### Usage and quota

| `code` | Meaning | Retry? | What to do |
| - | - | :-: | - |
| `CONSUMPTION_LIMIT_REACHED` | Your monthly usage limit ran out. The `message` includes the renewal date | No | Ask the administrator to raise the limit, or wait for renewal |
| `CONSUMPTION_BLOCKED` | Your AI access was blocked by the administrator | No | Contact the administrator |
| `QUOTA_EXCEEDED` | Your company's token quota ran out | Not until renewal | Wait for the date in `reset_at` or contact the administrator |

### Access

| `code` | Meaning | Retry? | What to do |
| - | - | :-: | - |
| `AUTHENTICATION_REQUIRED` | The key is invalid, was revoked, or was replaced | No | Check the key and the `Authorization: Bearer sk-...` header |
| `PERMISSION_DENIED` | Your account doesn't have permission for this action | No | Ask the administrator for the permission |
| `RESOURCE_NOT_FOUND` | The resource doesn't exist, was deleted, or you don't have access | No | Check the ID and `X-Enterprise-Id` |

### Files and documents

| `code` | Meaning | Retry? | What to do |
| - | - | :-: | - |
| `FILE_TOO_LARGE` | The file exceeded the accepted size | No | Shrink the file or split it into parts |
| `FILE_FORMAT_UNSUPPORTED` | Unsupported format | No | Convert to a supported format (PDF, DOCX, TXT, for example) |
| `FILE_CORRUPTED` | The file is incomplete or won't open | No | Send another copy |
| `FILE_PROCESSING_FAILED` | The file's content couldn't be read | Yes | Try again or send the file in another format |
| `RETRIEVAL_FAILED` | The attached content couldn't be read at response time | Yes | Try again |

### Other

| `code` | Meaning | Retry? | What to do |
| - | - | :-: | - |
| `UNKNOWN_ERROR` | Unexpected error | Yes | Try again. If it continues, send the `request_id` to support |

## 4. When to retry

**Retry** when `retryable` is `true`, or when the status is `429` or `5xx`:

* Wait a time that **grows with each attempt** (1 s, 2 s, 4 s, 8 s...) and add a small random variation.
* If the response includes the `Retry-After` header, wait **at least** that many seconds.
* Limit the number of attempts (for example, 5) and log the final failure with the `request_id`.

<Warning>
  **Never retry** on `400`, `401`, `402`, `403`, `404`, `409`, and `422`. None of them resolves on its own, and retrying only uses up your request limit.
</Warning>

```python theme={null}
import random
import time

def call_with_retry(make_request, attempts=5):
    for attempt in range(attempts):
        response = make_request()
        if response.ok:
            return response
        if response.status_code not in (429, 500, 502, 503, 504):
            return response
        wait = float(response.headers.get("Retry-After", 2 ** attempt))
        time.sleep(wait + random.uniform(0, 1))
    return response
```

## 5. Usage limits

These limits are **independent**. Knowing which one you hit avoids pointless retries.

| Limit | Current value | Counted per | Response |
| - | - | - | - |
| API requests | 400 per minute | Source IP address | `429`, with a 60 s block |
| Flow runs | 30 per minute | Company | `429` |
| Audio transcription | 30 per minute | Company | `429` |
| Text-to-speech | 30 per minute | Company | `429` |
| Token quota | Set in the company contract | Company | `429` with `quota_exceeded` |
| Usage limit | Set by the administrator, per month | User | `402` |

Values may be adjusted per environment.

### Request limit per IP

Every response includes headers so you can track the limit:

| Header | Meaning |
| - | - |
| `X-RateLimit-Limit` | Total allowed in the window |
| `X-RateLimit-Remaining` | How much is left |
| `X-RateLimit-Reset` | When the window restarts |
| `Retry-After` | Only on `429`: how many seconds to wait |

<Warning>
  **The limit is per IP, not per key.** Several integrations leaving from the same network (same server, same NAT, same CI runner) share the same quota.
</Warning>

`GET /health` and `GET /api/version` don't count toward this limit, and neither does `GET /api/models`.

### User usage limit

This is the monthly spending cap for the account that owns the key. It's checked **before** any call to the model, so a blocked request doesn't cost anything. Track it with `GET /api/v1/consumption/me` (see the [API reference](/en/desenvolvedores/api/referencia-da-api)).

### Response time and size

* **Set generous timeouts.** Responses from large models, with lots of context or files, can take a few minutes. We recommend at least **300 seconds** for the complete response. If you need fast responses, use streaming.
* **Files:** the maximum upload size is set by your company's configuration. Ask the administrator if you need to upload large files.
* **Audio:** files over 25 MB are compressed automatically before transcription.

## 6. Errors during streaming

With `"stream": true`, the `200 OK` status is sent **before** the model starts responding. If something goes wrong midway, the failure arrives **inside the stream**, as a JSON event with an `error` object, not as an HTTP error status.

The error event has the same content as format B:

```
data: {"error": {"code": "STREAM_INTERRUPTED", "message": "...", "action": "...", "retryable": true, "request_id": "req_..."}}

data: [DONE]
```

When reading the stream:

1. For each `data: {...}` line, check whether the JSON has an `error` key before reading `choices`.
2. A `data: [DONE]` after an error does **not** mean success. Treat the response as a failure.
3. If the connection closes without `data: [DONE]`, treat it as `STREAM_INTERRUPTED`.
4. Ignore blank lines.

## 7. How to get help with an error

When you open a ticket, send:

* The **`request_id`** from the error response.
* The **endpoint** and the approximate **time** (with time zone).
* The **HTTP status** and the **`code`**.
* The request body, **without the API key**.

The support channels are in [Environments and support](/en/desenvolvedores/api/ambientes-e-suporte).

## Related documents

* [Connect via API](/en/desenvolvedores/api/conectar-api)
* [Building projects with the API](/en/desenvolvedores/api/projetos-com-a-api)
* [API reference](/en/desenvolvedores/api/referencia-da-api)
* [Environments and support](/en/desenvolvedores/api/ambientes-e-suporte)


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