M cenaly.ru
🔌 Кассовый сервер: чеки по API

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

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

Документация

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

Дальше: реквизиты чека · сценарии магазина · ошибки.