Ошибки
problem+json со стабильным кодом — полный справочник, собранный из спецификации OpenAPI.
Ошибки приходят как application/problem+json (RFC 9457) с полями, на которые стоит опираться в коде:
{
"type": "urn:sapa:problem:order_not_editable",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Этот заказ уже нельзя изменить таким образом",
"code": "order_not_editable",
"message": "Этот заказ уже нельзя изменить таким образом",
"details": [
{ "field": "dropoff", "code": "order_not_editable", "message": "Адрес уже нельзя изменить" }
],
"order_status": "picked_up",
"editable": ["comment", "recipient_phone"],
"request_id": "0192f1c3-9e8d-7f6a-b5c4-d3e2f1a0b9c8"
}| Поле | Что это |
|---|---|
code | стабильный машинный код из справочника ниже — ветвитесь по нему |
message | текст для людей: по умолчанию на русском, на казахском — с Accept-Language: kk; может меняться |
details[] | ошибки по полям: field (путь вида recipient.phone), свой code и message |
request_id | id запроса — то же, что заголовок X-Request-Id; сообщайте его поддержке |
| дополнительные поля | некоторые коды добавляют данные, например order_id и order_url у duplicate_external_id |
Внутри v1 могут появляться новые коды — незнакомый код обрабатывайте по HTTP-статусу.
Проверка полей
400 validation_failed перечисляет все неверные поля:
{
"status": 400,
"code": "validation_failed",
"message": "Проверьте поля запроса",
"details": [
{ "field": "recipient.phone", "code": "invalid_phone", "message": "Телефон получателя: +7XXXXXXXXXX" },
{ "field": "external_id", "code": "not_blank", "message": "не должно быть пустым" }
],
"request_id": "0192f1c3-9e8d-7f6a-b5c4-d3e2f1a0b9c8"
}Изоляция
Заказы, расчёты и адреса вебхуков других организаций — и другой среды вашей организации — всегда 404 not_found,
никогда не 403. Узнать, существует ли чужой id, нельзя.
Справочник ошибок
Собран из x-error-catalogue спецификации OpenAPI; тесты платформы следят, чтобы он совпадал с кодами, которые API
возвращает на самом деле.
| HTTP | Код | Что значит | Повторять |
|---|---|---|---|
| 400 | validation_failed | Одно или несколько полей неверны. Каждый элемент details[] называет поле field и свой code.message: «Проверьте поля запроса» | нет |
| 400 | malformed_request | Тело — не корректный JSON, или у значения неверный тип.message: «Тело запроса не удалось прочитать» | нет |
| 400 | bad_request | У параметра неверное значение (например, дату не удалось разобрать).message: «Некорректный запрос» | нет |
| 400 | invalid_cursor | cursor взят не из предыдущего next_cursor или изменён.message: «Некорректный курсор пагинации» | нет |
| 400 | idempotency_key_invalid | Заголовок Idempotency-Key — 1–255 печатных символов ASCII без пробелов.message: «Некорректный Idempotency-Key» | нет |
| 400 | batch_too_large | В пакете больше 100 заказов. Разделите его.message: «Не больше 100 заказов за запрос» | нет |
| 400 | webhook_url_not_allowed | Адрес вебхука — только https и публичный хост.message: «Адрес вебхука: только https и публичный хост» | нет |
| 401 | unauthorized | Нет заголовка Authorization: Bearer, ключ неизвестен или отозван. Отозванный ключ перестаёт работать на всех серверах не позже чем через 60 секунд. Токены пользователей платформы Partner API не принимает.message: «Требуется вход» | нет |
| 403 | forbidden | У ключа нет нужной области доступа (orders.read, orders.write, points.read, finance.read, webhooks.manage).message: «Недостаточно прав» | нет |
| 403 | api_key_ip_not_allowed | У ключа есть allowlist IP, а запрос пришёл с другого адреса.message: «Запрос с адреса вне allowlist ключа» | нет |
| 403 | feature_disabled | Partner API ещё не включён для вашей организации. Обратитесь к аккаунт-менеджеру.message: «Функция недоступна» | нет |
| 403 | organization_not_active | Для боевых заказов нужна активная организация с подписанным договором. Тестовые ключи продолжают работать.message: «Организация не активна: новые заказы и аренда не принимаются» | нет |
| 403 | live_keys_not_enabled | Боевые ключи выпускаются после того, как бэк-офис включит боевой режим вашей организации.message: «Боевые ключи включает аккаунт-менеджер после чек-листа готовности интеграции» | нет |
| 403 | delivery_not_connected | Доставка не входит в договор вашей организации.message: «Модуль «Доставка» не подключён организации» | нет |
| 404 | not_found | Такого ресурса нет у вашей организации в среде ключа. Заказы других организаций — всегда 404, никогда 403.message: «Не найдено» | нет |
| 404 | quote_not_found | Такого quote_id нет у вашей организации в этой среде.message: «Расчёт не найден» | нет |
| 409 | duplicate_external_id | Заказ с таким external_id уже есть в этой среде. В ошибке — order_id и order_url существующего заказа: запросите его вместо создания нового.message: «Заказ с таким внешним номером уже есть» | нет |
| 409 | idempotency_conflict | Этот Idempotency-Key уже использован в течение 24 часов с другим телом. Для нового запроса — новый ключ.message: «Idempotency-Key уже использован с другим запросом» | нет |
| 409 | idempotency_in_progress | Первый запрос с этим ключом ещё выполняется. Повторите через секунду.message: «Запрос с этим Idempotency-Key ещё выполняется» | да |
| 409 | slot_full | В выбранном слоте не осталось мест. Выберите другой слот из GET /v1/slots.message: «Слот заполнен» | нет |
| 409 | order_transition_invalid | В текущем статусе заказа это действие невозможно (например, ready для вручённого заказа).message: «Недопустимый переход статуса заказа» | нет |
| 409 | api_key_limit_reached | Не больше двух активных ключей на среду (для ротации). Сначала отзовите один в кабинете.message: «Уже есть два активных ключа этой среды: отзовите один» | нет |
| 409 | webhook_endpoint_limit_reached | Не больше 10 адресов вебхуков на среду.message: «Не больше 10 адресов вебхуков на среду» | нет |
| 422 | out_of_zone | Адрес забора или доставки вне зон доставки.message: «Адрес вне зоны доставки» | нет |
| 422 | quote_expired | Расчёту больше 10 минут. Запросите новый; цена может отличаться.message: «Срок действия расчёта истёк, запросите новый» | нет |
| 422 | order_not_editable | После picked_up можно менять только comment и recipient_phone. В details[].field — отклонённые поля; в ошибке — order_status и ещё доступные для изменения поля editable.message: «Этот заказ уже нельзя изменить таким образом» | нет |
| 422 | order_not_cancellable | Заказ завершён (вручён, возвращён, отменён) или уже не доставлен / возвращается. В ошибке — order_status.message: «Заказ уже нельзя отменить» | нет |
| 422 | pickup_point_unavailable | Для боевых заказов нужен point_id активной точки забора вашей организации.message: «Точка забора неактивна или недоступна» | нет |
| 422 | address_ambiguous | Адрес текстом не удалось точно найти на карте. Передайте latitude и longitude.message: «Адрес не найден точно — укажите точку на карте» | нет |
| 422 | cargo_too_large | Груз слишком тяжёлый или крупный для любого доступного транспорта.message: «Груз не помещается ни в один транспорт» | нет |
| 422 | cod_not_allowed | Наложенный платёж не включён в вашем договоре.message: «Оплата при получении не разрешена договором» | нет |
| 422 | cod_limit_exceeded | Сумма наложенного платежа выше лимита договора.message: «Сумма к оплате выше лимита договора» | нет |
| 422 | window_invalid | Для заказа scheduled или slot нет scheduled_at, оно в прошлом или вне часов работы.message: «Окно времени не подходит для типа заказа» | нет |
| 429 | rate_limited | Больше 20 запросов в секунду с одного ключа (лимит можно повысить для вашей организации) или /track вызван чаще раза в 10 секунд для одного заказа. Подождите Retry-After секунд.message: «Слишком много запросов, повторите позже» | да |
| 500 | internal_error | Непредвиденная ошибка на нашей стороне. Повторите с тем же Idempotency-Key; сообщите поддержке request_id.message: «Внутренняя ошибка, мы уже разбираемся» | да |
| 503 | partner_live_orders_unavailable | Боевые заказы временно недоступны. Песочница работает.message: «Боевые заказы через API станут доступны с запуском модуля заказов (Д1); песочница работает» | да |
Коды причин
reason_code отмен, недоставок и переназначений (в history[], order.status_changed, order.failed). Незнакомые
коды, которые вы передаёте в POST /cancel, сохраняются как other.
| Код | Вид | Что значит |
|---|---|---|
client_changed_mind | отмена | Клиент передумал. |
duplicate | отмена | Дубль заказа. |
out_of_stock | отмена | Товара нет в наличии. |
no_courier | отмена | Курьер не найден (без платы). |
dispatcher_decision | отмена | Отменено диспетчером Sapa Moto. |
other | отмена | Другая причина. Незнакомые коды, которые вы передаёте, сохраняются как other с вашим кодом в комментарии. |
battery | переназначение | У курьера села батарея; заказ возвращается в searching. |
no_connection | переназначение | Пропала связь с курьером; заказ возвращается в searching. |
better_option | переназначение | Найден курьер ближе. |
not_ready | переназначение | Заказ не был готов на точке забора вовремя. |
recipient_unreachable | недоставка | Получатель не отвечает; заказ возвращается на точку забора. |
wrong_address | недоставка | Неверный адрес. |
refused | недоставка | Получатель отказался от заказа. |
no_adult_document | недоставка | Нет документа для заказа 18+. |
no_payment | недоставка | Наложенный платёж не оплачен. |
damaged | недоставка | Товар повреждён. |