Перейти к содержимому

Коды ответов и ошибки

Код Когда возвращается Что делать
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 проверяется первым.

Причина различается по паре 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 с описанием причины.

Метод существует, токен валиден, но пара «метод + шаблон маршрута» не входит в список, разрешённый ключу.

Тем же кодом отвечают ещё два состояния: компания заблокирована ("error": "company is blocked") и ключ не активирован ("error": "user hasn't been activated").

Общий лимит — 2 запроса в секунду на IP. Сверх него собственные ограничители стоят на отдельных тяжёлых маршрутах: поисковых и выгрузках отчётов в Excel (5 запросов в секунду). Эти счётчики общие для всех клиентов платформы.

Возникает, когда результат асинхронной операции не пришёл за 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 из ответа записывается в лог — по нему поддержка находит запрос в трассировке.