# Ошибки

Успешный ответ всегда имеет статус 200. Ошибка приходит с кодом 4xx или 5xx и телом в формате RFC 9457.

## Формат

Ошибка возвращается с заголовком `Content-Type: application/problem+json` и телом из восьми полей:

```json
{
  "type": "urn:meliora:problem:invalid_parameter",
  "title": "Invalid parameter",
  "status": 400,
  "detail": "Проверьте значение параметра",
  "instance": "urn:request:0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20",
  "code": "invalid_parameter",
  "param": "limit",
  "request_id": "0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20"
}
```

| Поле | Описание |
|---|---|
| `type` | URI типа ошибки: `urn:meliora:problem:` и код. |
| `title` | Короткое название на английском. |
| `status` | HTTP-статус, тот же, что у ответа. |
| `detail` | Пояснение для человека. Язык зависит от заголовка `Accept-Language`. |
| `instance` | URI запроса: `urn:request:` и идентификатор запроса. |
| `code` | Машинный код ошибки. |
| `param` | Параметр, к которому относится ошибка, или `null`. |
| `request_id` | Идентификатор запроса, тот же, что в заголовке `Request-Id`. |

## Отказы до проверки ключа

Часть запросов отклоняется еще до проверки ключа: превышен лимит с одного IP-адреса, неизвестный путь, тело в запросе, перегрузка. У таких ответов те же поля `status`, `code`, `param` и `request_id`, но `type` равен `about:blank`, а `title` и `detail` совпадают с `code`:

```json
{
  "type": "about:blank",
  "title": "rate_limit_exceeded",
  "status": 429,
  "code": "rate_limit_exceeded",
  "detail": "rate_limit_exceeded",
  "instance": "urn:request:0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20",
  "request_id": "0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20",
  "param": null
}
```

Поэтому опирайтесь на `status` и `code`, а не на `type` и `title`.

## Как обрабатывать

- Ветвите логику по `status` и `code`. Текст `detail` может меняться, не разбирайте его в коде.
- Если пришел незнакомый `code`, обработайте ошибку по HTTP-статусу: 4xx означает проблему в запросе или доступе, 5xx временную проблему на нашей стороне.
- При ошибке в параметре `param` содержит его имя: `limit`, `status`, `cursor`, `include_text` и так далее.
- Ошибка никогда не продвигает курсор. После повтора запрос с тем же курсором вернет те же данные.

## Язык пояснения

По умолчанию `detail` на русском. Чтобы получать английский текст, передайте заголовок `Accept-Language: en`. Поля `code` и `title` от языка не зависят.

## Request-Id

Каждый ответ, включая ошибки, содержит заголовок `Request-Id` с UUID запроса. Записывайте его в журнал рядом с маршрутом и временем. В обращении в [поддержку](https://docs.melioralab.io/support/) достаточно `Request-Id`: по нему мы найдем запрос. Сам ключ не присылайте.

## Коды ошибок

| Статус | Код | Что случилось |
|---|---|---|
| 400 | `invalid_parameter` | Значение параметра не подходит по формату, диапазону или сочетанию. Имя параметра в `param`. |
| 400 | `unknown_parameter` | Маршрут не принимает такой параметр. Ключ API в адресе тоже дает эту ошибку. |
| 400 | `cursor_mismatch` | Курсор поврежден, выдан для другого маршрута или фильтров, либо права и охват ключа изменились. |
| 401 | `invalid_api_key` | Ключа нет, он неверен, отозван или истек его срок. |
| 403 | `scope_required` | Ключ не имеет права на это чтение. Для текстов нужен также включенный «Текст сообщений». |
| 403 | `api_disabled` | Доступ по API выключен в настройках кабинета. |
| 403 | `api_not_available` | Тариф кабинета не включает API. Чтение доступно с тарифа Pro. |
| 403 | `subscription_inactive` | Подписка кабинета не действует. |
| 403 | `module_not_available` | В кабинете недоступен модуль рассылок. |
| 403 | `organization_suspended` | Доступ кабинета приостановлен. |
| 403 | `ip_not_allowed` | Запрос пришел с IP-адреса, которого нет в списке разрешенных у ключа. |
| 404 | `not_found` | Объект не найден или находится вне охвата ключа. Неизвестный путь тоже дает 404. |
| 405 | `method_not_allowed` | Разрешен только метод GET. Внешний адрес отвечает на другие методы 404 без тела, поэтому этот код снаружи не встречается. |
| 410 | `cursor_expired` | Срок курсора истек: 15 минут для списков, 30 суток для ленты событий. |
| 429 | `rate_limit_exceeded` | Исчерпан лимит запросов ключа, организации или IP-адреса. Повторите через `Retry-After` секунд. |
| 503 | `service_unavailable` | Данные временно не прочитаны. Повторите позже. |
| 503 | `server_busy` | Сервис перегружен. Повторите через `Retry-After` секунд. |
| 503 | `integration_initializing` | Новый ключ еще готовится. Повторите позже. |

## Что повторять

| Статус | Повторять? | Как |
|---|---|---|
| 400 | Нет | Исправьте параметр из `param`. Для `cursor_mismatch` начните список заново. |
| 401, 403 | Нет | Проверьте ключ, права и настройки кабинета. |
| 404 | Нет | Объект не существует или вне охвата ключа. |
| 410 | Нет | Курсор истек: начните список заново или [восстановите ленту](https://docs.melioralab.io/events-polling/#vosstanovlenie). |
| 429 | Да | Подождите `Retry-After` секунд, добавьте случайный разброс. |
| 503 | Да | Подождите `Retry-After` секунд, при повторных отказах увеличивайте паузу. |

Подробнее о паузах между повторами в руководстве [Лимиты и повторы](https://docs.melioralab.io/rate-limits/).

## Особые случаи

- **Неизвестный путь** и объект с идентификатором не в формате UUID дают 404 `not_found`. Путь пишется точно как в справочнике: завершающий `/` или закодированные символы в пути тоже дают 404.
- **Методы кроме GET**, включая `HEAD` и `OPTIONS`, и адреса вне `/v1/` получают 404 без тела. Код `method_not_allowed` снаружи не встречается.
- **Тело в GET-запросе**, адрес длиннее 8 КиБ или заголовки больше 8 КиБ дают 400 `invalid_parameter` с `param` равным `body`, `uri` или `headers`.
- **Повторенный параметр** (`?status=new&status=replied`) и **пустое значение** (`?status=`) дают 400 `invalid_parameter`.
- **Долгий запрос** прерывается через 10 секунд с ответом 503 `service_unavailable`.
