Skip to main content

Errors

Errors arrive in the same response envelope as successes — what differs is accept: false and a data of null:

{
"data": null,
"message": "Shartnoma topilmadi.",
"error": null,
"accept": false,
"errors": [],
"requestId": null
}
FieldMeaning
messageHuman-readable text, usually in Uzbek. Do not branch on it — the wording can change
error{ messageCode, args } when present — but most errors leave it null (section 3)
errors[]Field-level validation failures (section 5)
datanull on error — a few 400s echo the underlying parser/validator message here instead

:::warning Branch on the HTTP status first error.messageCode is the stable identifier, but today it is only filled in for a handful of generic errors. The reliable signal is the HTTP status code — read section 3 before you build error handling around messageCode. :::

On this page:


1. Where an error comes from​

Two different layers can reject your request, and they answer in different shapes.

1.1 The authentication layer — 401 and 429​

Token checks and rate limiting happen in a servlet filter, before your request reaches any endpoint. Its body is a reduced envelope: no error, no requestId, and errors is null rather than [].

401 — missing, invalid, expired or revoked token; also a blocked company
{
"message": "Invalid or expired Open API token",
"accept": false,
"errors": null,
"data": null
}
429 — more than 60 requests per minute on this token
{
"message": "Rate limit exceeded. Max 60 requests per minute.",
"accept": false,
"errors": null,
"data": null
}

Both messages are English and fixed, and neither carries a messageCode. Every reason for a 401 — no header, unknown token, expired token, revoked token, revoked connection, blocked company — produces the same body, so the status is all you get. Use GET /v1/open-api/token/info to find out which one it is.

1.2 The application layer — everything else​

Once authentication passes, errors come from the endpoint itself and use the full envelope: message, errors[], and sometimes error.


2. HTTP status codes​

StatusWhenWhat to do
200Success. Also the answer to an accepted async write (requestId, data: null)—
400Malformed JSON, bean validation (page < 1, size over the endpoint's maximum), unknown enum value, business validation (e.g. talk time greater than call duration), OAuth request errorsFix the request; retrying won't help
401Token problem or a blocked company (1.1). OAuth: unknown client_id / wrong client_secretCheck the token, then refresh or reissue it
403Valid token without the required grantAsk the company to grant the missing (permission, scope)
404An unknown requestId, or a request-status entry that has expiredDon't poll a requestId older than 24 hours
406A token-state error surfaced by an endpoint rather than the filter — rareTreat it like 401
409Also how most "not found" cases are reported (section 4), plus genuine conflicts such as a duplicate valueRe-read the current state; on a not-found, stop
429Per-minute limit exceeded (1.1)Back off — see Rate limits
500Our fault — and today also the answer for a few missing records (section 4)Retry with backoff; on the paths listed in section 4, treat it as "not found"

3. When messageCode is present​

error.messageCode is filled in only by the generic handlers:

messageCodeStatusWhen
3701403Access denied — the token lacks the required grant
3704500Unhandled server error
3705400Malformed / unparsable JSON body

Everything else — a missing lead, an invalid call duration, an OAuth failure, a rejected token — comes back with error: null and only a message. The domain codes below exist inside the platform and appear in logs and support tickets, so they are worth knowing, but do not build client logic that waits for them in a response body:

CodeMeaningHow you actually see it
6201 / 6202 / 6204Token not found / expired / invalid401 from the filter, no code in the body
6205Rate limit exceeded429 from the filter, no code in the body
6206requestId not found404, message only
2715Company blocked (licence expired)401 from the filter
1020Contract not found500 today (section 4)
1310Flat not found409, message only
1719Lead not found409, message only
6400 / 6401Invalid call duration / talk time above total duration400, message only
6900–6909OAuth errors400 (or 401 for invalid client), message only

:::note If your client already branches on messageCode Keep that code path — it is where the API is heading — but make it fall through to status-based handling when error is null. That way nothing breaks when the field starts being populated. :::


4. How "not found" is reported​

This is the most surprising part of the API today: a missing record is usually a 409, not a 404 — and on a few paths still a 500.

ResourceMissing record returnsBody
Lead (GET/DELETE /v1/open-api/lead/{leadId}, notes, tasks, lead history)409message only
Flat (GET /v1/open-api/flat/{id})409message only
Contract (GET /v1/open-api/contract/{id}, its payments and schedule)500generic system-error message, messageCode: 3704
Payment (GET /v1/open-api/payment/{id})500generic system-error message, messageCode: 3704
Request status (GET /v1/open-api/request/{requestId})404message only
An id inside an async write (unknown leadId, employeeId, call uuid)200 at accept timethe failure shows up as status: FAILED on the requestId

So: treat 409 on a read as "this record does not exist or is not visible to your company", and on the two paths above treat 500 the same way rather than retrying it forever. A 409 on a write is a genuine conflict (for example a duplicate value) — those are distinguishable by which endpoint you called, not by the body.

:::tip Never-existed and no-longer-visible look the same An id belonging to another company, a deleted record and an id that never existed all produce the same response. That is deliberate — it keeps ids from leaking across tenants. :::


5. Validation failures — errors[]​

When the request fails validation, the 400 response lists the offending fields. Note that error is null here — the detail lives in errors[]:

{
"data": null,
"message": "So'rovda xatolik",
"error": null,
"accept": false,
"errors": [
{ "field": "size", "reason": "MAX" },
{ "field": "page", "reason": "MIN" }
]
}

The same shape covers body fields and path/query parameters (for example a uuid longer than 128 characters, or a size above the endpoint's maximum).

reasonMeaning
NOT_NULLThe field is required
NOT_BLANKMust not be an empty string
NOT_EMPTY / NOT_NULL_COLLECTIONThe list must not be empty or null
MIN / MAXNumeric bound violated
DECIMAL_MIN / DECIMAL_MAXDecimal bound violated
SIZE / LENGTHLength or element count out of range
NOT_FOUNDNo record matches the given id
CONFLICTThe value conflicts with the current state
VALID_PHONEInvalid phone number format
VALID_EPOCH_SECThe timestamp is not in epoch seconds (milliseconds were probably sent)
NULLABLE_NOT_EMPTYMay be null, but must not be empty when present

A malformed JSON body is different: it is the one 400 that does carry a code (error.messageCode: 3705) and comes with an empty errors[].


6. Retry policy​

ErrorRetry?
429✅ with exponential backoff and jitter
5xx, network timeout✅ with backoff, 3–5 attempts — except the "not found" paths in section 4, where retrying can never succeed
401⚠️ only after refreshing the token (OAuth refresh_token); otherwise don't repeat
400, 403, 409❌ pointless until the request or the permissions change

:::tip Be careful with writes Write endpoints are asynchronous: once you have a 200 plus a requestId, resending may create a second record. If the connection dropped before you saw the response, look the record up first, then retry. POST /v1/open-api/call-history is the exception — it is idempotent on your uuid, so resending it is safe. :::


7. Known deviations​

The docs describe reality — these behave as follows today:

DeviationNote
⚠️ Missing contract or payment returns 500, not 404Being fixed; for now treat 500 on those paths as "not found" too
⚠️ Missing lead / flat / note / task / history item returns 409, not 404Section 4
⚠️ error.messageCode is absent from most error bodiesSection 3 — branch on the status
401 and 429 bodies use a reduced envelope (errors: null, no error)Section 1.1
The contrac-field path typoThe canonical path is contract-field; the old spelling still works as an alias
No X-RateLimit-* or Retry-After headersTrack your own usage
No last flag in PageableDataUse currentPage >= totalPages
A successful response's message can be a token-expiry warning instead of "Success"Within 7 days of expiry — see Authentication