# Errors

> The error format, status codes and how to handle them.

The API uses ordinary HTTP status codes. A response in the 200s means success. Anything else, including `401` and `500`, carries a JSON body that explains what went wrong.

## Error format

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Must be one of: reply, note.",
    "fields": { "type": "Must be one of: reply, note." },
    "request_id": "req_3f9c0a1b22d4e5f6"
  }
}
```

| Field | Meaning |
|---|---|
| `code` | A short, stable word for the problem. Branch on this, not on the message. |
| `message` | A sentence for people. It can change, so do not match on it. |
| `fields` | Present on validation errors. Maps each field to what is wrong with it. |
| `request_id` | Identifies the request. It is also in the `X-Request-Id` response header. Include it when you write to support. |

## Status codes

| Status | Code | What it means |
|---|---|---|
| 400 | `invalid_json` | The body is not valid JSON. |
| 401 | `missing_api_key` | There is no `Authorization` header. |
| 401 | `invalid_api_key` | The key is unknown, revoked or expired. A `WWW-Authenticate: Bearer` header is sent. |
| 403 | `insufficient_scope` | The key is valid but lacks the scope. The message names it. See [permissions](https://tirver.com/help/api/permissions.md). |
| 404 | `not_found` | No such endpoint or record. Ids from another workspace also give this. |
| 404 | `module_disabled` | The workspace has switched that module off. |
| 405 | `method_not_allowed` | The path exists but not for this method. The `Allow` header lists the methods that work. |
| 409 | `conflict` | A duplicate slug, or the conversation already has a feedback post. |
| 413 | `payload_too_large` | The body is over 256 KB. |
| 422 | `validation_failed` | A query parameter or body field is wrong. See `fields`. Unknown body fields are rejected. Unknown query parameters are ignored. |
| 429 | `rate_limited` | Too many requests, or the AI agent is busy. Wait for the time in `Retry-After`. See [rate limits](https://tirver.com/help/api/rate-limits.md). |
| 500 | `internal_error` | Something broke on our side. Retry with backoff. |
| 502 | `upstream_error` | The AI backend could not answer an `ask` request. Retry with backoff. |

## Handling errors

Retry only what can succeed on a second try: `429`, `500` and `502`. Do not retry other `4xx` errors, because the same request will fail the same way.

**JavaScript**

```js
async function tirver(path, init = {}) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`https://app.tirver.com/api/v1${path}`, {
      ...init,
      headers: { Authorization: `Bearer ${process.env.TIRVER_API_KEY}`, 'Content-Type': 'application/json', ...init.headers },
    });
    if (res.ok) return res.json();
    const retryable = res.status === 429 || res.status >= 500;
    if (!retryable || attempt === 4) {
      const { error } = await res.json().catch(() => ({ error: { code: 'unknown', message: res.statusText } }));
      throw Object.assign(new Error(error.message), { status: res.status, code: error.code, requestId: error.request_id });
    }
    const wait = Number(res.headers.get('Retry-After')) || 2 ** attempt;
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
}

const open = await tirver('/conversations?status=open');
```

**Python**

```python
import os, time, requests

def tirver(method, path, **kwargs):
    for attempt in range(5):
        res = requests.request(
            method,
            f"https://app.tirver.com/api/v1{path}",
            headers={"Authorization": f"Bearer {os.environ['TIRVER_API_KEY']}"},
            timeout=10,
            **kwargs,
        )
        if res.ok:
            return res.json()
        retryable = res.status_code == 429 or res.status_code >= 500
        if not retryable or attempt == 4:
            res.raise_for_status()
        time.sleep(float(res.headers.get("Retry-After", 2 ** attempt)))

open_conversations = tirver("GET", "/conversations", params={"status": "open"})
```

When you contact support about an error, include the `request_id`, the time of the request and the path. Never include your key.

Source: https://tirver.com/help/api/errors
