Skip to content

Response codes and errors

Code When it is returned What to do
200 success —
201 object created —
400 input validation error fix the request, do not retry
401 token missing, expired or invalid refresh the token pair and retry once
403 no permission for the route, company blocked or key not activated grant the permission; check the company and key status
404 route or object not found check the path and the identifier
408 waiting for the asynchronous response timed out (600 s; for report export to Excel — 100 s) check the object state with a separate request
422 business validation error fix the data
429 the rate limiter triggered on a heavy route retry with exponential backoff
500 internal error 1–2 retries, then contact support with the correlation_id

Classic envelope — this is how most methods and the access check respond

{ "code": 401, "message": "token expired", "error": "token has been expired", "data": null }

Error object

{ "error": { "code": "BAD_REQUEST", "message": "shop_id is required" } }

Asynchronous response envelope

{
"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"
}

In this case the final HTTP code is taken from the status_code field, and the ID of the created or modified object — from id.

Empty envelope fields are omitted from the response (omitempty); data in an error envelope is usually absent.

Timeout body — the same channel, but shorter: it has no status_code field.

{ "correlation_id": "...", "error": { "code": "TIMEOUT", "message": "Timeout waiting for response" } }

error.code is checked first.

The cause is distinguished by the message + error pair:

message error What happened
token error JWT parsing text the token is truncated, corrupted or signed with a different key
token expired token has been expired expired — a refresh is required
unauthorized refresh token used as access a refresh token was passed in Authorization
unauthorized session not found / session revoked the session is closed or revoked — a new sign-in is required
unauthorized mismatch platform-id with token info platform-id differs from the one the token was issued with
unauthorized user is not active / user has been expired the key is disabled or has expired

The token pair is refreshed via POST /v2/auth/refresh (the platform-id header is required). If the refresh token has also expired, a new token is issued via POST /v1/auth/login. Errors of the refresh itself come with code 400 and a description of the cause.

The method exists and the token is valid, but the “method + route template” pair is not in the list allowed for the key.

Two more states respond with the same code: the company is blocked ("error": "company is blocked") and the key is not activated ("error": "user hasn't been activated").

Overall limit — 2 requests per second per IP. On top of it, dedicated limiters stand on individual heavy routes: search routes and report exports to Excel (5 requests per second). These counters are shared by all clients of the platform.

Occurs when the result of an asynchronous operation has not arrived within 600 seconds; for report export to Excel the wait is shorter — 100 seconds. The operation may still have completed — do not retry a write blindly.

The error / error.code field carries string codes: BAD_REQUEST, TIMEOUT, TOO_MANY_REQUESTS, rate_limited, and token refresh responses use the RFC 6749 §5.2 codes: invalid_request, invalid_grant, unauthorized_client, server_error.

Sale methods respond with 400; the exact cause is a number in 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 here is an empty string.

The most common codes in an online store integration:

Code Meaning What to do
20084 payment is possible only for a recalculated cart the items were added with the wrong method — use POST /v2/order-product/:order_id
20082 payment with the shift closed open a shift in the shop of the sale
20010 the user has no current cash register open a shift; the system picks the cash register, passing it in the body is useless
20009 the cash register’s shop and the sale’s shop differ the sale was created in the wrong shop
20015 the sale has no items add products before payment
20016 insufficient stock check the stock in the shop of the sale
20017 the draft has been deleted create a new one
20020 wrong payment amount the sum of payments must match the receipt total
20033 shift not found open a shift
20036 product not found check product_id
20037 the product is archived an archived product cannot be sold
20063 the sale was not sent to the fiscal data operator (OFD) removed with the skip_ofd flag
20085 the seller has no access to the shop check seller_id

The primary reference is the HTTP code; the numeric and string codes serve for precise diagnostics and logs.

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 from the response is written to the log — support uses it to find the request in the trace.