Errors, diagnostics and reliability
How to read the errors of the Cash Server API, what to do in each case, and how to build an integration that does not duplicate tax documents.
1. Where each error appears#
Errors appear in two places, representing different classes of problems:
| Where | Class | Example |
|---|---|---|
| In the registration response (HTTP 4xx) | Request not accepted: authentication, receipt schema, INN mismatch, duplicate | 10, 11, 12, 32, 33 |
In the document result (report, document already accepted) |
Receipt could not be issued: register offline, register rejection | 2001, 2002, type: "timeout" with code 1, driver errors |
The first class is fixed in the integration code. The second is addressed on the register and equipment side.
2. Request Errors#
10 · 11 · 12 — Authorization#
| Code | Cause | Solution |
|---|---|---|
| 10 | Token not passed | Header Token: <token> or ?token= |
| 11 | Token expired (valid for 24 hours) or unknown | Get a new one via getToken |
| 12 | Invalid login or password | Check group credentials |
| 20 | Token issued for another group | Check group_code in path |
| 21 | Group does not support this protocol version | Check v5 / v4 in path |
Best practice: cache the token for 23 hours and refresh it upon error 11, rather than on a schedule.
32 — Receipt Validation#
The most common error at the start of integration. The text always specifies the exact field, for example:
{ "error": { "code": 32, "type": "system",
"text": "items[0].measure is required (v5, tag 2108)" } }
Common causes:
| Error text | What is wrong |
|---|---|
receipt.client: at least one of email|phone is required |
Nowhere to send the electronic receipt |
receipt.company.inn: 10 or 12 digits expected |
TIN contains spaces, hyphens, or is invalid |
items[N].payment_object is required (v5) |
Calculation subject attribute not passed |
items[N].measure is required (v5, tag 2108) |
Unit of measurement is a string instead of a code |
items[N].measurement_unit is not supported in v5 |
v4 and v5 mixed in a single receipt |
receipt.total (…) does not equal the sum of items (…) |
Discount deducted from total, but not from items |
payment sum (…) does not equal receipt.total (…) |
Rounding discrepancy in payment |
items[N].supplier_info with valid inn is required when agent_info is present |
Agent item without supplier details |
receipt.company.inn (…) does not match the group INN (…) |
Receipt TIN does not match the one registered to the group's POS fiscal drive |
Check the receipt structure against the attributes reference guide — it lists what is required in v5 and what is in v4.
The last row — regarding TIN — is processed before queueing: a receipt with another company's TIN is rejected immediately (HTTP 400, code 32), the document is not created, and the external_id is not consumed. Check the TIN against the POS registration card and the details specified when creating the cash server.
33 — Duplicate external_id#
This is not an integration error, but a standard response to a retry. The response body contains the uuid of the first document:
{ "uuid": "47b30214-…", "status": "wait",
"error": { "code": 33, "text": "a document with this external_id has already been accepted in this group" } }
The correct action: take the uuid and request a report for it. This only becomes an error if you accidentally reused the external_id for another transaction.
30 · 34 — Working with report#
- 30 — unknown
uuid: typo, another group, or document older than 60 days (archive). - 34 — document has not been processed yet. Returned with HTTP 200 and
status: wait; simply continue waiting.
429 — Rate Limit Exceeded#
Registration is limited to 10 requests per second per group; polling is limited to 1 request per second for each POS in the group. Retry with a delay; for polling, a reasonable interval is 2–3 seconds, or better yet, subscribe to callback_url.
3. Errors in report: document accepted but receipt not issued#
Code 2001 (type: "timeout") — 300 seconds waiting for a register#
The document spent 5 minutes in the queue without reaching an online register in the group: the POS computer is off, there is no internet connection, the bridge is not running — or all registers in the group remained busy with other documents beyond the allotted time. Error text: “queue: no free register found in the group within 300 s (all registers offline or busy)”.
Code 1 with the same type: "timeout" is a separate case: a register PICKED UP the receipt but did not report the result within 300 seconds (it froze or the connection dropped during printing). Unlike code 2001, the document may have reached the fiscal storage device — check the latest receipt on the register itself before retrying the transaction.
What to do:
- Check that the POS computer is on and the bridge is running (the bridge status page on the computer itself); each register's connection is also shown in the admin panel under “Cash Register API”;
- Check the computer's internet connection;
- If the computer and bridge are working but timeouts recur at peak times, add a second register to the group, including a backup: the queue processes documents in arrival order;
- Retry the transaction with a new
external_id— the old document has already been finalized asfail.
Cash register driver errors#
Anything the register reports itself arrives in its own wording: “out of paper”, “register locked”, “unfinished receipt open”, “fiscal storage full”, “shift exceeded 24 hours — run a Z report” (see Shift longer than 24 hours). Common codes are covered in ATOL errors and SHTRIKH-M errors.
This also includes a receipt incompatible with the register model — such a rejection arrives with code 2002 and the driver's message:
- the register cannot issue an electronic receipt without printing — required for online payments (currently reliably supported by ATOL registers);
- the receipt includes marked products, but the register does not support FFD 1.2;
- extended buyer details or industry-specific details that the model does not recognize.
What to do: check the register model and FFD version; add a suitable device to the group if necessary. The queue does not select registers “by capability” — the document goes to any online register in the group, so keep devices with similar capabilities in the same group.
4. Protection against a double receipt#
A double tax document for a single payment is the worst possible mistake in a fiscal integration: it has to be reversed with a correction receipt and explained to the FTS. Three lines of defence work against it in the service:
external_idat the intake. A repeat within the group returns the originaluuid(error 33) and never creates a second document.- The operation key all the way to the cash register. The bridge receives a key built from the group and
external_idand keeps an operation journal on disk. A repeat returns the first result — even if the cash register PC managed to reboot in between. - An honest refusal on an unknown outcome. If the connection dropped exactly at the moment the receipt was being closed (the document may or may not have reached the fiscal drive), a repeat of that operation is rejected with a demand to verify the last document on the cash register. A second receipt is never registered blindly.
What is required from your side:
- one settlement — one
external_id, stable across retries (order-10421, say, rather than a random UUID per attempt); - never change the
external_id"so that it goes through": if a document was not registered, first find out why; - an idempotent callback handler: the same notification may arrive more than once.
5. What to do if the cash register is offline#
The cash register PC is off or has no Internet — the receipt will not be registered: the cash register is physically yours, and the cloud cannot stand in for it. The service behaves as follows:
- the document waits in the queue for up to 300 seconds;
- if the cash register appears within that time, the receipt is registered normally;
- if not, the document moves to
failwith thetimeouttype.
Recommendations for stores that take orders around the clock:
- Keep the cash register PC on (turn off power saving and sleep);
- Add a second cash register to the group as a backup;
- Keep a queue of unsent receipts in your own system and repeat them with a new
external_idonce the cash register is back online — bearing in mind that by law the receipt must be generated no later than the next business day; - Set up monitoring: a
failwithtimeoutis an incident a human should learn about.
6. Checklist before going live#
- the KKT is registered with the "payments on the Internet" attribute, and tag 1187 carries the website address;
- the TIN in
receipt.company.innmatches the TIN the fiscal drive is registered to; - the taxation system in the receipt matches your actual taxation system;
-
external_idis deterministic and survives retries; - response 33 is handled (take the
uuidfrom the response, do not treat it as a failure); - both polling and
callback_urlare implemented — the latter does not replace the former; - a payment is recognized as fiscalized only on
status: done; - the FD, FPD, fiscal drive number and KKT registration number are saved with the order;
-
failandtimeoutreach monitoring, not just the log; - the behaviour with the cash register PC switched off has been tested;
- for marked goods, the code is verified before the receipt is sent;
- the VAT rates have been checked on the cash register's actual firmware (especially 22% and the derived rates).
7. Where to look when investigating an incident#
| Question | Where to find the answer |
|---|---|
| What exactly did we send? | Your request log + external_id |
| What did the service return? | report by uuid (retained for at least 32 days) |
| Did it reach a register? | device_code in report — the code of the register the document was sent to |
| What did the register say? | The error field in report; receipt log and last error in “Cash Register API” |
| Is the receipt in fiscal storage? | Fiscal document number from payload + your fiscal data operator account |
| Was there a duplicate? | Error 33 in your logs and a matching uuid |
If a receipt was issued but your system did not learn about it (you lost the callback and did not poll), the document still exists: find it by external_id through report and save the missing details — do not issue the receipt again.
Next: API reference · receipt fields · store scenarios.