Коды ответов и ошибки
HTTP-коды
Заголовок раздела «HTTP-коды»| Код | Когда возвращается | Что делать |
|---|---|---|
200 |
успех | — |
201 |
объект создан | — |
400 |
ошибка валидации входных данных | исправить запрос, не повторять |
401 |
токен отсутствует, истёк или недействителен | обновить пару токенов и повторить один раз |
403 |
нет прав на маршрут, компания заблокирована или ключ не активирован | выдать права; проверить статус компании и ключа |
404 |
маршрут или объект не найден | проверить путь и идентификатор |
408 |
истекло ожидание асинхронного ответа (600 с; у экспорта отчётов в Excel — 100 с) | проверить состояние объекта отдельным запросом |
422 |
ошибка бизнес-валидации | исправить данные |
429 |
сработал ограничитель на тяжёлом маршруте | повторить с экспоненциальной задержкой |
500 |
внутренняя ошибка | 1–2 повтора, затем в поддержку с correlation_id |
Форматы тела
Заголовок раздела «Форматы тела»Классический конверт — так отвечает большинство методов и проверка доступа
{ "code": 401, "message": "token expired", "error": "token has been expired", "data": null }Объект ошибки
{ "error": { "code": "BAD_REQUEST", "message": "shop_id is required" } }Конверт асинхронного ответа
{ "session_id": "00000000-0000-0000-0000-000000000000", "company_id": "00000000-0000-0000-0000-000000000000", "id": "00000000-0000-0000-0000-000000000000", "status_code": 400, "error": { "code": "BAD_REQUEST", "message": "..." }, "correlation_id": "00000000-0000-0000-0000-000000000000"}Итоговый HTTP-код в этом случае берётся из поля status_code, а ID созданного
или изменённого объекта — из id.
Пустые поля конверта в ответ не попадают (omitempty); data в конверте ошибки
обычно отсутствует.
Тело таймаута — тот же канал, но короче: поля status_code в нём нет.
{ "correlation_id": "...", "error": { "code": "TIMEOUT", "message": "Timeout waiting for response" } }error.code проверяется первым.
Семантика ключевых кодов
Заголовок раздела «Семантика ключевых кодов»401 — токен
Заголовок раздела «401 — токен»Причина различается по паре message + error:
message |
error |
Что случилось |
|---|---|---|
token error |
текст разбора JWT | токен обрезан, испорчен или подписан не тем ключом |
token expired |
token has been expired |
истёк — требуется обновление |
unauthorized |
refresh token used as access |
в Authorization подставлен refresh-токен |
unauthorized |
session not found / session revoked |
сессия закрыта или отозвана — нужен новый логин |
unauthorized |
mismatch platform-id with token info |
platform-id не тот, с которым выдан токен |
unauthorized |
user is not active / user has been expired |
ключ отключён или у него вышел срок |
Пара токенов обновляется через POST /v2/auth/refresh (нужен заголовок platform-id).
Если refresh-токен тоже просрочен, новый токен выпускается через POST /v1/auth/login.
Ошибки самого обновления приходят кодом 400 с описанием причины.
403 — права
Заголовок раздела «403 — права»Метод существует, токен валиден, но пара «метод + шаблон маршрута» не входит в список, разрешённый ключу.
Тем же кодом отвечают ещё два состояния: компания заблокирована
("error": "company is blocked") и ключ не активирован
("error": "user hasn't been activated").
429 — частота
Заголовок раздела «429 — частота»Общий лимит — 2 запроса в секунду на IP. Сверх него собственные ограничители стоят на отдельных тяжёлых маршрутах: поисковых и выгрузках отчётов в Excel (5 запросов в секунду). Эти счётчики общие для всех клиентов платформы.
408 — таймаут асинхронного ответа
Заголовок раздела «408 — таймаут асинхронного ответа»Возникает, когда результат асинхронной операции не пришёл за 600 секунд; у экспорта отчётов в Excel ожидание короче — 100 секунд. Операция при этом могла выполниться — повторять запись вслепую нельзя.
Бизнес-коды
Заголовок раздела «Бизнес-коды»Строковые
Заголовок раздела «Строковые»В поле error / error.code встречаются строковые коды: BAD_REQUEST, TIMEOUT,
TOO_MANY_REQUESTS, rate_limited, а в ответах обновления токена — коды RFC 6749 §5.2:
invalid_request, invalid_grant, unauthorized_client, server_error.
Числовые — у продаж
Заголовок раздела «Числовые — у продаж»Методы продаж отвечают 400, точная причина — числом в data.error_code:
{ "status_code": 400, "error": { "code": "", "message": "status: 400 - errors: Bad request - causes: ..." }, "data": { "error_code": 20084, "message": "payment is possible only for calculated order", "data": null }}error.code здесь — пустая строка.
Самые частые коды при интеграции интернет-магазина:
| Код | Смысл | Что делать |
|---|---|---|
20084 |
оплата возможна только для пересчитанной корзины | позиции добавлены не тем методом — нужен POST /v2/order-product/:order_id |
20082 |
оплата при закрытой смене | открыть смену в магазине продажи |
20010 |
у пользователя нет текущей кассы | открыть смену; кассу выбирает система, в теле её передавать бесполезно |
20009 |
магазин кассы и магазин продажи разные | продажа создана не в том магазине |
20015 |
в продаже нет позиций | добавить товары до оплаты |
20016 |
недостаточно товара | проверить остаток в магазине продажи |
20017 |
черновик удалён | создать новый |
20020 |
неверная сумма оплаты | сумма платежей должна сходиться с суммой чека |
20033 |
смена не найдена | открыть смену |
20036 |
товар не найден | проверить product_id |
20037 |
товар в архиве | архивный товар продать нельзя |
20063 |
продажа не отправлена в ОФД | снимается флагом skip_ofd |
20085 |
у продавца нет доступа к магазину | проверить seller_id |
Основной ориентир — HTTP-код; числовой и строковый коды служат для точной диагностики и логов.
Разбор тела ошибки
Заголовок раздела «Разбор тела ошибки»def parse_error(resp): try: body = resp.json() except ValueError: return {"code": resp.status_code, "message": resp.text[:300]} err = body.get("error") if isinstance(err, dict): return {"code": body.get("status_code", resp.status_code), "message": err.get("message") or err.get("code")} return {"code": body.get("code", resp.status_code), "message": body.get("message") or (err if isinstance(err, str) else "unknown")}correlation_id из ответа записывается в лог — по нему поддержка находит запрос
в трассировке.