C cenaly.ru
Start free

API кассового сервера: справочник

Протокол, совместимый с АТОЛ Онлайн v5/v4: getToken, регистрация документа, report, callback, идемпотентность, лимиты и коды ошибок

9 min read Open this screen in the demo
On this page 14

Cash Server API: reference

The protocol is compatible with ATOL Online: the same paths, the same field names, the same error codes. If you already have a module built for ATOL Online, changing the base URL and the access credentials is usually enough.

Base URL (FFD 1.2) https://54fz.cenaly.ru/possystem/v5/
Base URL (FFD 1.05) https://54fz.cenaly.ru/possystem/v4/
Format JSON, UTF-8, Content-Type: application/json
Authentication a 24-hour token, the Token header
Liveness check GET /possystem/health (no token needed)

A product overview and the requirements for the cash register — Cash Server.


1. Authentication: getToken#

curl -X POST https://54fz.cenaly.ru/possystem/v5/getToken \
  -H 'Content-Type: application/json' \
  -d '{"login":"u9f3c1a7d40b2","pass":"••••••••"}'
{ "error": null, "token": "e826899dc378…063086", "timestamp": "04.08.2026 13:37:40" }
  • The login and group_code are issued upon creating a POS server in the admin panel's "POS API" section and look like u<12 hex> and kkt-<12 hex> — custom descriptive values cannot be chosen.
  • The token is valid for 24 hours; there is no need to request it for every receipt — cache it.
  • The token is passed via the Token: <token> header or the ?token=<token> parameter.
  • GET /possystem/v5/getToken?login=…&pass=… is also supported — mirroring ATOL.
  • Invalid credentials → HTTP 401, error code 12.

2. Registering a document#

POST /possystem/{v5|v4}/{group_code}/{operation}
Operation What document it is v5 v4
sell Sale (income) ✅ ✅
sell_refund Refund of a sale ✅ ✅
buy Expense ✅ ✅
buy_refund Refund of an expense ✅ ✅
sell_correction Correction of a sale ✅ ✅
buy_correction Correction of an expense ✅ ✅
sell_refund_correction Correction of a sale refund ✅ —
buy_refund_correction Correction of an expense refund ✅ —

Request body#

{
  "timestamp": "04.08.2026 14:40:00",
  "external_id": "order-10421",
  "service": { "callback_url": "https://shop.example/api/fiscal-callback" },
  "receipt": {
    "client": { "email": "buyer@example.com" },
    "company": {
      "email": "shop@example.com",
      "sno": "usn_income",
      "inn": "7736570901",
      "payment_address": "https://shop.example"
    },
    "items": [
      {
        "name": "Coffee beans, 1 kg",
        "price": 1250.00,
        "quantity": 2,
        "sum": 2500.00,
        "measure": 0,
        "payment_method": "full_payment",
        "payment_object": 1,
        "vat": { "type": "vat20" }
      }
    ],
    "payments": [ { "type": 1, "sum": 2500.00 } ],
    "total": 2500.00
  }
}
Field Required Description
timestamp yes The moment of settlement, format dd.mm.yyyy HH:MM:SS
external_id yes Your document identifier, ≤ 128 characters. The idempotency key (see §5)
service.callback_url no The HTTP(S) address for delivering the result, ≤ 256 characters
receipt yes The receipt body for ordinary operations
correction + correction_info yes The body and the grounds for *_correction operations

The contents of receipt and all value reference tables — Receipt fields.

Response#

{ "uuid": "47b30214-b145-4c3f-abe3-89076225ac4f",
  "timestamp": "04.08.2026 13:39:15", "status": "wait", "error": null }

uuid is the identifier of the document in our queue; the result is requested by it. The wait status means "accepted, waiting for a cash register" — that is the normal response to a successful registration.


3. Result: report#

GET /possystem/{v5|v4}/{group_code}/report/{uuid}
curl "https://54fz.cenaly.ru/possystem/v5/kkt-1f4c9a20b73e/report/47b30214-…" \
  -H "Token: $TOKEN"
{
  "uuid": "47b30214-b145-4c3f-abe3-89076225ac4f",
  "timestamp": "04.08.2026 13:39:28",
  "callback_url": "https://shop.example/api/fiscal-callback",
  "status": "done",
  "group_code": "kkt-1f4c9a20b73e",
  "daemon_code": "cenaly-54fz",
  "device_code": "kkt-1",
  "external_id": "order-10421",
  "error": null,
  "warnings": null,
  "payload": {
    "fiscal_receipt_number": 101,
    "shift_number": 9,
    "receipt_datetime": "04.08.2026 13:39:15",
    "total": 2500.0,
    "fn_number": "9999078902005478",
    "ecr_registration_number": "0000000001056264",
    "fiscal_document_number": 6789,
    "fiscal_document_attribute": 2745081395,
    "fns_site": "www.nalog.gov.ru",
    "ofd_receipt_url": ""
  }
}

Statuses#

status Meaning What to do
wait Queued, POS has not responded yet Wait for callback or poll
done Document issued, payload populated Save details in your order
fail Document not issued, error populated Parse the error code, see Errors

Payload fields#

Field FFD tag Description
fiscal_receipt_number 1042 Receipt number per shift
shift_number 1038 Shift number
receipt_datetime 1012 Document time according to POS clock
total 1020 Receipt total in rubles
fn_number 1041 Factory serial number of the fiscal storage (FN)
ecr_registration_number 1037 Registration number of the POS/ECR (RN KKT)
fiscal_document_number 1040 Fiscal document number (FD)
fiscal_document_attribute 1077 Fiscal document sign/attribute (FPD)
fns_site 1060 FTS website for receipt verification
ofd_receipt_url — Link to the receipt in OFD; currently always empty (see below)

The four parameters FD + FPD + FN + RN KKT are what you need to save in the order: they allow locating the receipt in the OFD personal dashboard and in the FTS "Receipt Check" mobile app. The ofd_receipt_url field is reserved by the protocol, but POS drivers do not return the link, so it arrives empty.

Certain POS units may not return some attributes (such as the shift number). In this case, the field returns zero or empty — we deliberately never substitute calculated values into fiscal attributes.

Storage and limits#

  • The result is available for at least 32 days; the document is archived after 60 days.
  • Polling is rate-limited: 1 request per second per POS in the group. Exceeding it returns HTTP 429.
  • Unknown uuid → error 30; document still in queue → HTTP 200 with status: wait.

4. Callback#

If the request contained service.callback_url, then after the document moves to done or fail we send it a POST with the same body the report returns.

  • The request timeout is 10 seconds, success is any 2xx response.
  • Retries: immediately, after 30 seconds, after 120 seconds (3 attempts in total).
  • An invalid or overly long URL does not break the receipt: it is discarded and warnings.callback_url appears in the report.

The handler on your side must be idempotent — key it on uuid or external_id. The callback does not replace polling: if no notification arrived within 300 seconds, request the report yourself.


5. Idempotency#

external_id is unique within a group. Registering the same external_id again returns HTTP 400 and error 33 — together with the original uuid:

{ "uuid": "47b30214-…", "status": "wait",
  "error": { "code": 33, "text": "a document with this external_id has already been accepted in this group", "type": "system" } }

That is the regular answer to a retry, not an emergency: take the uuid from the response and request the report for it.

Idempotency is end-to-end — it reaches the cash register itself. The bridge receives an operation key built from the group and external_id, and the bridge keeps an operation journal on disk: a repeat of the same operation returns the first result and survives a reboot of the cash register PC. If the previous attempt broke off with an unknown outcome (the connection dropped exactly at the closing of the receipt), the repeat is rejected with a demand to verify the last document on the cash register — a second tax document is never generated blindly.

The practical rule: one external_id per payment, and repeat the request with the same value as many times as you like. Create a new external_id only for a new settlement.


6. Limits#

Limit Value
Document registration 10 requests per second per group (soft; above that — HTTP 429)
Report polling 1 rps × the number of cash registers in the group
Request body size 256 KB
Items in a receipt 100
Length of an item's name 128 characters
Length of external_id 128 characters
Length of callback_url 256 characters
Token lifetime 24 hours
Waiting for a free cash register 300 seconds, then fail / timeout

7. Error codes#

The error object has the same format everywhere:

{ "error": { "error_id": "f546e8d0-…", "code": 33, "text": "…", "type": "system" },
  "status": "fail", "timestamp": "04.08.2026 13:39:15" }

type is one of system, driver, agent, timeout, unknown.

Code HTTP Meaning
0 / 1 400 Unknown error / processing failure
10 401 Token not provided
11 401 Token expired or unknown
12 401 Incorrect login or password
13 400 Request validation error
20 401 Token does not match the group
21 401 Group does not use this protocol version
30 400 Unknown uuid
31 400 Unsupported operation
32 400 Receipt JSON validation error (the message names the field)
33 400 external_id already accepted (the response contains the original uuid)
34 200 Document not yet processed (status: wait)
40 400 Invalid request
41 415 Unsupported Content-Type
50 500 Service configuration error

Codes 2001–2003 mean no free register / no suitable register / INN mismatch:

  • no register became available within 300 seconds (all registers in the group are offline or busy with other receipts) → status: fail, type: "timeout", code 2001. If a register PICKED UP the receipt but did not respond within 300 seconds, you receive code 1 with the same type: "timeout" — the code distinguishes “no register was available” from “the register is not responding”;
  • the receipt INN does not match the group INN → the document is rejected immediately on registration: HTTP 400, code 2003, with both INNs named in the message; it never enters the queue;
  • the register cannot issue this kind of receipt (product marking, electronic receipt, extended buyer details) → status: fail, code 2002, type: "driver", and error.text contains the register's rejection verbatim.

Driver errors appear in the document result, not in the registration response — they occur after the document has been accepted into the queue. For details and what to do in each case, see Errors and diagnostics.


8. Differences between v5 and v4#

v5 (FFD 1.2) v4 (FFD 1.05)
Unit of measurement measure — numeric code (tag 2108), required measurement_unit — string ("pcs", "kg")
Subject of calculation payment_object — number 1…33, required payment_object — string
Payment method payment_method is required Optional; if not passed, tag 1214 is not sent to the POS and it applies its default flag
Marking code mark_code — object with a single format field nomenclature_code — hex string
Return corrections Yes No

They cannot be mixed: measurement_unit in v5 and measure in v4 are rejected by validation with a clear message. v5 is recommended for new integrations — marked goods require FFD 1.2.


9. Quick Start: Full Cycle with curl#

BASE=https://54fz.cenaly.ru/possystem/v5
GROUP=kkt-1f4c9a20b73e

TOKEN=$(curl -s -X POST $BASE/getToken -H 'Content-Type: application/json' \
  -d '{"login":"u9f3c1a7d40b2","pass":"••••"}' | jq -r .token)

UUID=$(curl -s -X POST $BASE/$GROUP/sell -H "Token: $TOKEN" \
  -H 'Content-Type: application/json' -d @receipt.json | jq -r .uuid)

curl -s "$BASE/$GROUP/report/$UUID" -H "Token: $TOKEN" | jq .payload

Next: receipt details · store scenarios · errors.

Was this article helpful?