Errors
problem+json with a stable code — the full catalogue, generated from the OpenAPI specification.
Errors are application/problem+json (RFC 9457) with the members your code should rely on:
{
"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"
}| Member | Meaning |
|---|---|
code | stable machine code from the catalogue below — branch on it |
message | text for people: Russian by default, Kazakh with Accept-Language: kk; may change |
details[] | per-field problems: field (path like recipient.phone), its own code and message |
request_id | the id of the request — the same as the X-Request-Id header; quote it to support |
| extra members | some codes add data, e.g. order_id and order_url for duplicate_external_id |
New codes may appear within v1 — treat an unknown code by its HTTP status.
Validation
400 validation_failed lists every invalid field:
{
"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"
}Isolation
Orders, quotes and webhook endpoints of other organizations — and of the other environment of your own organization
— are always 404 not_found, never 403. Nobody can probe whether an id exists.
Error catalogue
Generated from the x-error-catalogue of the OpenAPI specification; the platform's tests keep it identical to the
codes the API really returns.
| HTTP | Code | Meaning | Retry |
|---|---|---|---|
| 400 | validation_failed | One or more fields are invalid. Each entry of details[] names the field and its own code.message: «Проверьте поля запроса» | no |
| 400 | malformed_request | The body is not valid JSON or a value has the wrong type.message: «Тело запроса не удалось прочитать» | no |
| 400 | bad_request | A parameter has an invalid value (for example a date that cannot be parsed).message: «Некорректный запрос» | no |
| 400 | invalid_cursor | The cursor was not taken from a previous next_cursor or was changed.message: «Некорректный курсор пагинации» | no |
| 400 | idempotency_key_invalid | The Idempotency-Key header must be 1–255 printable ASCII characters without spaces.message: «Некорректный Idempotency-Key» | no |
| 400 | batch_too_large | A batch carries more than 100 orders. Split it.message: «Не больше 100 заказов за запрос» | no |
| 400 | webhook_url_not_allowed | Webhook URLs must use https and resolve to a public host.message: «Адрес вебхука: только https и публичный хост» | no |
| 401 | unauthorized | No Authorization: Bearer header, an unknown key or a revoked key. A revoked key stops working within 60 seconds on every replica. Platform user tokens are not accepted by the Partner API.message: «Требуется вход» | no |
| 403 | forbidden | The key lacks the scope this operation needs (orders.read, orders.write, points.read, finance.read, webhooks.manage).message: «Недостаточно прав» | no |
| 403 | api_key_ip_not_allowed | The key has an IP allowlist and the request came from another address.message: «Запрос с адреса вне allowlist ключа» | no |
| 403 | feature_disabled | The Partner API is not enabled for your organization yet. Contact your account manager.message: «Функция недоступна» | no |
| 403 | organization_not_active | Live orders need an active organization with a signed contract. Test keys keep working.message: «Организация не активна: новые заказы и аренда не принимаются» | no |
| 403 | live_keys_not_enabled | Live keys are issued after the back-office enables live mode for your organization.message: «Боевые ключи включает аккаунт-менеджер после чек-листа готовности интеграции» | no |
| 403 | delivery_not_connected | Delivery is not part of your organization's contract.message: «Модуль «Доставка» не подключён организации» | no |
| 404 | not_found | No such resource for your organization and the key's environment. Orders of other organizations are always 404, never 403.message: «Не найдено» | no |
| 404 | quote_not_found | The quote_id does not exist for your organization and environment.message: «Расчёт не найден» | no |
| 409 | duplicate_external_id | An order with this external_id already exists in this environment. The problem carries order_id and order_url of the existing order — fetch it instead of creating a new one.message: «Заказ с таким внешним номером уже есть» | no |
| 409 | idempotency_conflict | The same Idempotency-Key was used within 24 hours with a different body. Use a new key for a new request.message: «Idempotency-Key уже использован с другим запросом» | no |
| 409 | idempotency_in_progress | The first request with this key is still being processed. Retry in a second.message: «Запрос с этим Idempotency-Key ещё выполняется» | yes |
| 409 | slot_full | The chosen delivery slot has no capacity left. Pick another slot from GET /v1/slots.message: «Слот заполнен» | no |
| 409 | order_transition_invalid | The order is in a status where this action is not possible (for example ready on a delivered order).message: «Недопустимый переход статуса заказа» | no |
| 409 | api_key_limit_reached | At most two active keys per environment (for rotation). Revoke one in the cabinet first.message: «Уже есть два активных ключа этой среды: отзовите один» | no |
| 409 | webhook_endpoint_limit_reached | At most 10 webhook endpoints per environment.message: «Не больше 10 адресов вебхуков на среду» | no |
| 422 | out_of_zone | The pickup or drop-off is outside the delivery zones.message: «Адрес вне зоны доставки» | no |
| 422 | quote_expired | The quote is older than 10 minutes. Request a new quote; its price may differ.message: «Срок действия расчёта истёк, запросите новый» | no |
| 422 | order_not_editable | After picked_up only comment and recipient_phone can change. details[].field lists the refused fields; the problem carries order_status and the still editable fields.message: «Этот заказ уже нельзя изменить таким образом» | no |
| 422 | order_not_cancellable | The order is final (delivered, returned, cancelled) or already failed / returning. The problem carries order_status.message: «Заказ уже нельзя отменить» | no |
| 422 | pickup_point_unavailable | Live orders need the point_id of an active pickup point of your organization.message: «Точка забора неактивна или недоступна» | no |
| 422 | address_ambiguous | The address text could not be geocoded precisely. Send latitude and longitude.message: «Адрес не найден точно — укажите точку на карте» | no |
| 422 | cargo_too_large | The cargo is too heavy or large for every available vehicle.message: «Груз не помещается ни в один транспорт» | no |
| 422 | cod_not_allowed | Cash on delivery is not enabled in your contract.message: «Оплата при получении не разрешена договором» | no |
| 422 | cod_limit_exceeded | The cash-on-delivery amount is above the limit of your contract.message: «Сумма к оплате выше лимита договора» | no |
| 422 | window_invalid | scheduled_at is missing, in the past or outside the working hours for a scheduled or slot order.message: «Окно времени не подходит для типа заказа» | no |
| 429 | rate_limited | More than 20 requests per second with one key (the limit can be raised for your organization), or /track was called more often than once per 10 seconds for one order. Wait Retry-After seconds.message: «Слишком много запросов, повторите позже» | yes |
| 500 | internal_error | Unexpected error on our side. Retry with the same Idempotency-Key; quote request_id to support.message: «Внутренняя ошибка, мы уже разбираемся» | yes |
| 503 | partner_live_orders_unavailable | Live orders are temporarily unavailable. The sandbox keeps working.message: «Боевые заказы через API станут доступны с запуском модуля заказов (Д1); песочница работает» | yes |
Reason codes
reason_code of cancellations, failed deliveries and reassignments (in history[], order.status_changed,
order.failed). Unknown codes you send in POST /cancel are stored as other.
| Code | Kind | Meaning |
|---|---|---|
client_changed_mind | cancel | The client changed their mind. |
duplicate | cancel | Duplicate order. |
out_of_stock | cancel | The goods are out of stock. |
no_courier | cancel | No courier was found (no fee). |
dispatcher_decision | cancel | Cancelled by a Sapa Moto dispatcher. |
other | cancel | Any other reason. Unknown codes sent by you are stored as other with your code in the comment. |
battery | reassign | The courier's battery ran low; the order goes back to searching. |
no_connection | reassign | The courier lost connection; the order goes back to searching. |
better_option | reassign | A closer courier was found. |
not_ready | reassign | The order was not ready at the pickup point in time. |
recipient_unreachable | failure | The recipient did not answer; the order returns to the pickup point. |
wrong_address | failure | The address is wrong. |
refused | failure | The recipient refused the order. |
no_adult_document | failure | No ID for an 18+ order. |
no_payment | failure | Cash on delivery was not paid. |
damaged | failure | The goods were damaged. |