Формат ошибок

Как выглядит отказ API, как его разбирать и какие отказы возможны сегодня.

Отказ, который вернуло приложение, всегда одной формы, на любой ручке и при любом статусе HTTP.

{
  "error": {
    "code": "unknown_filter",
    "message": "Неизвестные параметры: verbose",
    "details": {
      "unknown": [
        "verbose"
      ],
      "allowed": [
        "page",
        "perPage",
        "sort",
        "dir"
      ]
    }
  }
}
ПолеВидВ ответеЧто это
errorобъектвсегдаСам отказ. В успешном ответе этого поля нет никогда.
error.codeстрокаunauthorized cookie_auth_not_allowed forbidden unknown_filter bad_body too_large not_found too_many_requests not_implemented internalвсегдаКод отказа. По нему клиент выбирает, что делать. Все коды перечислены в каталоге ниже.
error.messageстрокавсегдаОбъяснение для человека. Текст может меняться, разбирать его кодом нельзя.
error.detailsлюбое значениене всегдаПодробности, если они есть. Состав зависит от кода; у отказов входа здесь лежит rule.

Как разбирать отказ

  1. Статус HTTP говорит, чья это ошибка. Статус 4xx: запрос надо исправить, повтор без правки даст тот же ответ. Статус 5xx: сбой на нашей стороне.
  2. error.code говорит, что именно не так.
  3. error.details.rule, если он есть, называет точную причину. Отказы входа различаются именно им.

Незнакомый код обрабатывайте по статусу HTTP: новые коды и правила могут появляться, о них пишет журнал изменений.

Если приложение недоступно целиком, ответ 502 или 504 отдаёт прокси перед ним, и тела в этой форме у такого ответа нет. Не разбирайте его как JSON, обращайтесь с ним как с любым 5xx.

Номер запроса

У каждого ответа, успешного или нет, есть заголовок X-Request-Id. Сохраняйте его вместе с отказом в своём журнале и называйте поддержке: по нему запрос находят сразу.

Каталог отказов

Отказы общие для всех ручек. Под каждой строкой раскрывается пример ответа с работающего сервера.

Вход

HTTPКод и правилоКогда приходит и что делать
401unauthorized

Заголовка Authorization нет, ключ не по форме, такой открытой части не существует или секрет не сошёлся. Какой из четырёх случаев произошёл, ответ намеренно не уточняет.

Проверить, что заголовок передан и ключ скопирован целиком, без пробелов и переводов строки.

Пример ответа
GET /api/v1/me

HTTP/1.1 401

{
  "error": {
    "code": "unauthorized",
    "message": "Ключ API не принят"
  }
}
401unauthorizedapi_key_revoked

Ключ верный, но отозван или заменён перегенерацией. Отзыв окончателен.

Получить у администратора новый ключ.

Пример ответа
HTTP/1.1 401

{
  "error": {
    "code": "unauthorized",
    "message": "Ключ API отозван: получите новый",
    "details": {
      "rule": "api_key_revoked"
    }
  }
}
401unauthorizedapi_key_expired

Ключ верный, но его срок истёк.

Получить у администратора новый ключ.

Пример ответа
HTTP/1.1 401

{
  "error": {
    "code": "unauthorized",
    "message": "Срок ключа API истёк: получите новый",
    "details": {
      "rule": "api_key_expired"
    }
  }
}
401unauthorizedapi_key_disabled

Ключ верный, но выключен администратором. Выключенный ключ можно включить обратно.

Попросить администратора включить ключ.

Пример ответа
HTTP/1.1 401

{
  "error": {
    "code": "unauthorized",
    "message": "Ключ API выключен",
    "details": {
      "rule": "api_key_disabled"
    }
  }
}
401cookie_auth_not_allowed

Вместе с запросом пришла кука сессии консоли оператора. Так бывает, когда запрос скопирован из панели браузера.

Убрать куку и передать ключ API заголовком Authorization.

Пример ответа
HTTP/1.1 401

{
  "error": {
    "code": "cookie_auth_not_allowed",
    "message": "API мерчанта не принимает сессию консоли: нужен ключ API"
  }
}
403forbiddenmerchant_not_active

Ключ верный, но мерчант не в работе: ещё не активирован, выключен или в архиве.

Уточнить у администратора статус мерчанта.

Пример ответа
HTTP/1.1 403

{
  "error": {
    "code": "forbidden",
    "message": "Мерчант не в работе: запросы не принимаются",
    "details": {
      "rule": "merchant_not_active"
    }
  }
}
403forbiddenenvironment_closed

Ключ верный, мерчант в работе, но контур этого ключа мерчанту закрыт.

Работать ключом другого контура либо попросить администратора открыть контур.

Пример ответа
HTTP/1.1 403

{
  "error": {
    "code": "forbidden",
    "message": "Боевой контур мерчанту закрыт",
    "details": {
      "rule": "environment_closed"
    }
  }
}

Запрос

HTTPКод и правилоКогда приходит и что делать
400unknown_filter

В адресе есть параметр, которого у ручки нет. Незнакомый параметр не пропускается молча: details.unknown называет его.

Убрать параметр или исправить его имя.

Пример ответа
GET /api/v1/me?verbose=1

HTTP/1.1 400

{
  "error": {
    "code": "unknown_filter",
    "message": "Неизвестные параметры: verbose",
    "details": {
      "unknown": [
        "verbose"
      ],
      "allowed": [
        "page",
        "perPage",
        "sort",
        "dir"
      ]
    }
  }
}
400bad_body

Тело запроса не разобралось как JSON.

Исправить тело запроса.

Пример ответа
HTTP/1.1 400

{
  "error": {
    "code": "bad_body",
    "message": "Тело запроса не разобралось"
  }
}
413too_large

Тело запроса больше допустимого размера.

Уменьшить тело запроса.

Пример ответа
HTTP/1.1 413

{
  "error": {
    "code": "too_large",
    "message": "Тело запроса больше разрешённого"
  }
}
404not_found

Такого адреса нет: опечатка в пути или не тот метод HTTP.

Сверить адрес и метод со справочником.

Пример ответа
GET /api/v1/unknown

HTTP/1.1 404

{
  "error": {
    "code": "not_found",
    "message": "Адрес не найден"
  }
}

Предел частоты

HTTPКод и правилоКогда приходит и что делать
429too_many_requests

Превышен предел частоты запросов с одного адреса.

Повторить позже: заголовок RateLimit-Reset называет, через сколько секунд счёт начнётся заново.

Пример ответа
HTTP/1.1 429

{
  "error": {
    "code": "too_many_requests",
    "message": "Слишком часто. Повторите позже.",
    "details": {
      "limit": 300,
      "windowSeconds": 60
    }
  }
}

Наша сторона

HTTPКод и правилоКогда приходит и что делать
501not_implemented

Адрес зарезервирован под ручку, которая ещё не выпущена.

Не использовать адрес, пока он не появится в справочнике и в журнале изменений.

Пример ответа
POST /api/v1/payments

HTTP/1.1 501

{
  "error": {
    "code": "not_implemented",
    "message": "Не реализовано: модуль merchant-api, фаза 1",
    "details": {
      "module": "merchant-api",
      "phase": "фаза 1",
      "why": "Создание платежа требует движка роутинга и адаптера провайдера, и ни того, ни другого нет. Ответ-заглушка с идентификатором платежа хуже отказа: касса казино сочтёт платёж принятым и будет ждать уведомления, которого никто не пришлёт."
    }
  }
}
500internal

Сбой на нашей стороне. Причина наружу не выдаётся, в details.requestId лежит номер запроса.

Повторить запрос чтения позже. Если повторяется, сообщить поддержке номер запроса.

Пример ответа
HTTP/1.1 500

{
  "error": {
    "code": "internal",
    "message": "Внутренняя ошибка",
    "details": {
      "requestId": "00000000-0000-4000-8000-000000000000"
    }
  }
}

API v1 · собрано 12.11.2026 · выпуск cd63633

Справочник методов собирается из кода сервера: он не описывает адрес, которого нет.