API кассового сервера: справочник
Протокол совместим с АТОЛ Онлайн: те же пути, те же имена полей, те же коды ошибок. Если у вас есть готовый модуль под АТОЛ Онлайн, чаще всего достаточно поменять базовый URL и реквизиты доступа.
| Базовый URL (ФФД 1.2) | https://54fz.cenaly.ru/possystem/v5/ |
| Базовый URL (ФФД 1.05) | https://54fz.cenaly.ru/possystem/v4/ |
| Формат | JSON, UTF-8, Content-Type: application/json |
| Аутентификация | токен на 24 часа, заголовок Token |
| Проверка живости | GET /possystem/health (без токена) |
Обзор продукта и требования к кассе — Кассовый сервер.
1. Аутентификация: 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" }
- Логин и
group_codeвыдаются при создании кассового сервера в разделе админки «API кассы» и выглядят какu<12 hex>иkkt-<12 hex>— выбрать свои «говорящие» значения нельзя. - Токен живёт 24 часа; получать его на каждый чек не нужно — кэшируйте.
- Токен передаётся заголовком
Token: <токен>либо параметром?token=<токен>. - Поддерживается и
GET /possystem/v5/getToken?login=…&pass=…— зеркально АТОЛ. - Неверные реквизиты → HTTP 401, код ошибки 12.
Один логин обслуживает одну кассовую группу. Пароль хранится у нас только в виде хеша argon2id — восстановить его нельзя, можно только перевыпустить: раздел «API кассы» → «Сменить пароль». Ранее выданные токены при этом продолжают работать до истечения своих 24 часов.
2. Регистрация документа#
POST /possystem/{v5|v4}/{group_code}/{operation}
| Операция | Что за документ | v5 | v4 |
|---|---|---|---|
sell |
Приход (продажа) | ✅ | ✅ |
sell_refund |
Возврат прихода | ✅ | ✅ |
buy |
Расход | ✅ | ✅ |
buy_refund |
Возврат расхода | ✅ | ✅ |
sell_correction |
Коррекция прихода | ✅ | ✅ |
buy_correction |
Коррекция расхода | ✅ | ✅ |
sell_refund_correction |
Коррекция возврата прихода | ✅ | — |
buy_refund_correction |
Коррекция возврата расхода | ✅ | — |
Тело запроса#
{
"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": "Кофе в зёрнах, 1 кг",
"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
}
}
| Поле | Обяз. | Описание |
|---|---|---|
timestamp |
да | Момент расчёта, формат dd.mm.yyyy HH:MM:SS |
external_id |
да | Ваш идентификатор документа, ≤ 128 символов. Ключ идемпотентности (см. §5) |
service.callback_url |
нет | HTTP(S)-адрес для доставки результата, ≤ 256 символов |
receipt |
да | Тело чека для обычных операций |
correction + correction_info |
да | Тело и основание для операций *_correction |
Состав receipt и все справочники значений — Реквизиты чека.
Ответ#
{ "uuid": "47b30214-b145-4c3f-abe3-89076225ac4f",
"timestamp": "04.08.2026 13:39:15", "status": "wait", "error": null }
uuid — идентификатор документа в нашей очереди; по нему запрашивается результат. Статус wait означает «принят, ждёт кассу», это нормальный ответ успешной регистрации.
Важно. Ответ
waitещё не означает, что чек пробит. Признавать оплату фискализированной можно только поdoneв report или в callback.
3. Результат: 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": ""
}
}
Статусы#
status |
Смысл | Что делать |
|---|---|---|
wait |
В очереди, касса ещё не ответила | Ждать callback либо поллить |
done |
Документ пробит, payload заполнен |
Сохранить реквизиты у себя в заказе |
fail |
Документ не пробит, заполнен error |
Разобрать код ошибки, см. Ошибки |
Поля payload#
| Поле | Тег ФФД | Что это |
|---|---|---|
fiscal_receipt_number |
1042 | Номер чека за смену |
shift_number |
1038 | Номер смены |
receipt_datetime |
1012 | Время документа по часам кассы |
total |
1020 | Итог чека в рублях |
fn_number |
1041 | Заводской номер фискального накопителя |
ecr_registration_number |
1037 | Регистрационный номер ККТ (РН ККТ) |
fiscal_document_number |
1040 | Номер фискального документа (ФД) |
fiscal_document_attribute |
1077 | Фискальный признак документа (ФПД) |
fns_site |
1060 | Сайт ФНС для проверки чека |
ofd_receipt_url |
— | Ссылка на чек в ОФД; сейчас всегда пустая (см. ниже) |
Четвёрка ФД + ФПД + ФН + РН ККТ — это то, что нужно сохранить в заказе: по ней чек находится в личном кабинете ОФД и в приложении «Проверка чека» ФНС. Поле ofd_receipt_url зарезервировано протоколом, но кассовые драйверы ссылку не возвращают, поэтому оно приходит пустым.
Отдельные кассы могут не отдавать часть реквизитов (например, номер смены). Тогда поле приходит нулевым или пустым — мы принципиально не подставляем вычисленные значения в фискальные реквизиты.
Хранение и лимиты#
- Результат доступен не менее 32 суток, документ архивируется через 60 дней.
- Поллинг ограничен: 1 запрос в секунду на каждую кассу группы. Превышение — HTTP 429.
- Неизвестный
uuid→ ошибка 30; документ ещё в очереди → HTTP 200 соstatus: wait.
4. Callback#
Если в запросе был service.callback_url, после перехода в done или fail мы отправляем на него POST с тем же телом, что отдаёт report.
- Таймаут запроса — 10 секунд, успех — любой ответ 2xx.
- Повторы: сразу, через 30 секунд, через 120 секунд (всего 3 попытки).
- Невалидный или слишком длинный URL не валит чек: он отбрасывается, а в report появляется
warnings.callback_url.
Обработчик на вашей стороне должен быть идемпотентным — ориентируйтесь на uuid или external_id. Callback не заменяет поллинг: если за 300 секунд уведомления не было, запросите report сами.
5. Идемпотентность#
external_id уникален в пределах группы. Повторная регистрация того же external_id возвращает HTTP 400 и ошибку 33 — вместе с прежним uuid:
{ "uuid": "47b30214-…", "status": "wait",
"error": { "code": 33, "text": "документ с таким external_id уже принят в этой группе", "type": "system" } }
Это штатный ответ на ретрай, а не аварийная ситуация: возьмите uuid из ответа и запросите по нему report.
Идемпотентность сквозная — она доходит до самой кассы. Мосту передаётся ключ операции, построенный из группы и external_id, а мост ведёт журнал операций на диске: повтор той же операции возвращает первый результат и переживает перезагрузку кассового ПК. Если предыдущая попытка оборвалась с неизвестным исходом (обрыв связи ровно на закрытии чека), повтор будет отклонён с требованием сверить последний документ на кассе — второй налоговый документ вслепую не формируется.
Практическое правило: на одну оплату — один external_id, и повторяйте запрос с тем же значением сколько угодно раз. Новый external_id создавайте только для нового расчёта.
6. Лимиты#
| Лимит | Значение |
|---|---|
| Регистрация документов | 10 запросов в секунду на группу (мягкий, сверх — HTTP 429) |
| Поллинг report | 1 rps × число касс в группе |
| Размер тела запроса | 256 КБ |
| Позиций в чеке | 100 |
Длина name позиции |
128 символов |
Длина external_id |
128 символов |
Длина callback_url |
256 символов |
| Время жизни токена | 24 часа |
| Ожидание свободной кассы | 300 секунд, дальше fail / timeout |
7. Коды ошибок#
Формат объекта ошибки одинаков везде:
{ "error": { "error_id": "f546e8d0-…", "code": 33, "text": "…", "type": "system" },
"status": "fail", "timestamp": "04.08.2026 13:39:15" }
type — один из system, driver, agent, timeout, unknown.
| Код | HTTP | Значение |
|---|---|---|
| 0 / 1 | 400 | Неизвестная ошибка / сбой обработки |
| 10 | 401 | Токен не передан |
| 11 | 401 | Токен истёк или неизвестен |
| 12 | 401 | Неверный логин или пароль |
| 13 | 400 | Ошибка валидации запроса |
| 20 | 401 | Токен не соответствует группе |
| 21 | 401 | Группа не работает по этой версии протокола |
| 30 | 400 | Неизвестный uuid |
| 31 | 400 | Неподдерживаемая операция |
| 32 | 400 | Ошибка валидации JSON чека (текст называет поле) |
| 33 | 400 | external_id уже принят (в ответе прежний uuid) |
| 34 | 200 | Документ ещё не обработан (status: wait) |
| 40 | 400 | Некорректный запрос |
| 41 | 415 | Неподдерживаемый Content-Type |
| 50 | 500 | Ошибка конфигурации сервиса |
Коды 2001–2003 — нет свободной кассы / нет подходящей кассы / несовпадение ИНН:
- свободной кассы не нашлось за 300 секунд (все кассы группы офлайн либо заняты чужими чеками) →
status: fail,type: "timeout", код 2001. Если касса чек ВЗЯЛА, но за 300 с не ответила, приходит код 1 с тем жеtype: "timeout"— по коду вы отличаете «касс не было» от «касса молчит»; - ИНН чека не совпадает с ИНН группы → документ отклоняется сразу при регистрации: HTTP 400, код 2003, текст называет оба ИНН; в очередь он не попадает;
- касса не умеет такой чек (маркировка, электронный чек, расширенные реквизиты покупателя) →
status: fail, код 2002,type: "driver", а вerror.text— дословный отказ кассы.
Ошибки драйвера приходят не в ответе на регистрацию, а в результате документа — они возникают, когда он уже принят в очередь. Подробный разбор и что делать по каждому — Ошибки и диагностика.
8. Различия v5 и v4#
| v5 (ФФД 1.2) | v4 (ФФД 1.05) | |
|---|---|---|
| Единица измерения | measure — числовой код (тег 2108), обязателен |
measurement_unit — строка («шт», «кг») |
| Предмет расчёта | payment_object — число 1…33, обязателен |
payment_object — строка |
| Способ расчёта | payment_method обязателен |
Необязателен; если не передан, тег 1214 в кассу не уходит и она подставляет свой умолчательный признак |
| Код маркировки | mark_code — объект с одним полем-форматом |
nomenclature_code — hex-строка |
| Коррекции возврата | Есть | Нет |
Смешивать нельзя: measurement_unit в v5 и measure в v4 отклоняются валидацией с понятным текстом. Новым интеграциям рекомендуется v5 — маркированные товары требуют ФФД 1.2.
9. Быстрый старт: полный цикл на 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
Дальше: реквизиты чека · сценарии магазина · ошибки.