# Errors

> The error format of the Tentary API, all error codes with their HTTP status, and how to retry safely after an error.

Source: https://tentary.com/en/help/api/errors/
Last updated: 2026-10-28

---

Every error of the Tentary API has the same format. The message tells you what to do.

## Format

Every error comes as JSON with a matching HTTP status:

```json
{
  "error": {
    "code": "...",
    "message": "...",
    "request_id": "..."
  }
}
```

- `code` is a fixed value from the table below. Your code can react to it.
- `message` says in words what is wrong and what you can do.
- `request_id` identifies the request. Send it to support if you need help.

Two answers have a shorter body: an unexpected server error answers `{"error": "server_error", "request_id": "..."}`, and a path outside `/v1/` answers `{"error": "not_found"}`.

## Error codes

| HTTP | `code` | Meaning |
| --- | --- | --- |
| 400 | `bad_input` | A parameter is missing, unknown or wrong. The message names it. |
| 400 | `bad_cursor` | The cursor is not valid. Start again without a cursor. |
| 400 | `bad_image` | The file or the URL is not a PNG, JPG, GIF or WebP image. |
| 400 | `fetch_failed` | Tentary could not copy the file from the URL, for example because the file is too large or the URL does not answer with HTTP 200. The message says why. |
| 400, 404 | `legacy_course` | The product has an old course. Open it once in the Tentary app first. The message has the link. |
| 400 | `not_possible` | The action is not possible for this object, for example a refund of an old order. |
| 400, 403 | `setup_missing` | The account setup is not finished, for example there is no payment provider. See [`GET /v1/setup`](https://tentary.com/en/help/api/get-setup-status/). |
| 401 | `unauthorized`, `invalid_token` | No key, a wrong key or a revoked key. The answer has the header `WWW-Authenticate: Bearer`. |
| 403 | `scope_missing` | The key does not have the permission. Create a key with the permission. |
| 403 | `owner_only`, `blocked`, `plan_limit`, `not_possible` | Not allowed for this account, or not possible through the API. The message says why. |
| 404 | `not_found` | No such object in this account, or no such path. |
| 405 | `method_not_allowed` | Wrong HTTP method. The `Allow` header names the right one. |
| 409 | `busy` | The same write is still running, or Tentary still copies this product. Wait a minute, check, then retry. |
| 409 | `not_uploaded` | The file is not in the storage yet. Send the bytes with PUT to the `upload_url` first, then finish the upload again. See [File uploads](https://tentary.com/en/help/api/file-uploads/). |
| 429 | `rate_limited` | Too many calls. The `Retry-After` header gives the seconds to wait. See [Rate limits](https://tentary.com/en/help/api/rate-limits/). |
| 429 | `busy` | Too many calls of this account run at the same time, or Tentary copies another file or image of this account at the moment. See [Rate limits](https://tentary.com/en/help/api/rate-limits/#calls-at-the-same-time). Wait the `Retry-After` seconds, then retry. |
| 500 | `server_error` | An error on the Tentary side. Send the `request_id` to support. |
| 503 | `timeout` | The call took too long for this account. Ask for a shorter period, one product (`product_id`) or fewer items per page. |
| 503 | `unavailable` | The help center search is not available now. Try again later. |

## Retry safely

- **After a 429**, wait the `Retry-After` seconds. Do not retry at once in a loop: a call refused by the per-minute or per-day limit still costs 1 point.
- **After a timeout or a 500 on a write**, first read the object or the list to see if the write happened. Only then retry.
- **Create calls** (and `POST /v1/files`) have a short protection: the same create with the same values within 60 seconds returns the first result and does not create a second object. This is not a general guarantee. Updates and calls after 60 seconds run again.
