Обзор локального API
Кассовая программа отправляет чеки в приложение по HTTP на том же компьютере. Приложение проверяет чек, пробивает его через фискальный модуль и возвращает фискальный признак.
Адрес и ключ
Заголовок раздела «Адрес и ключ»- Адрес:
http://127.0.0.1:5600(порт меняется в настройках приложения). - Ключ: заголовок
X-Api-Key. Ключ показан в приложении, раздел «API». - API доступен только с этого компьютера. Запросы из браузера со сторонних сайтов отклоняются (
BRIDGE_FORBIDDEN_ORIGIN). - Полное описание в формате OpenAPI:
GET /v1/openapi.json(без ключа).
Деньги и количество
Заголовок раздела «Деньги и количество»- Все суммы — целые тийины (1 сум = 100 тийин):
12 500,50 сум→1250050. - Количество — целые тысячные доли: 1 шт →
1000, 0,25 кг →250. - НДС приложение считает само по ставке
vatPercent.
Пробить чек
Заголовок раздела «Пробить чек»POST /v1/receipts
{ "externalId": "POS-2026-000123", "type": "sale", "items": [ { "name": "Хлеб", "spic": "10901001001000000", "packageCode": "1500213", "quantity": 2000, "unitPrice": 500000, "discount": 0, "vatPercent": 12 } ], "discount": 0, "payments": { "cash": 600000, "card": 400000, "cardType": "personal" }}| Поле | Обязательно | Что это |
|---|---|---|
externalId |
да | Номер чека в вашей программе, до 64 символов. Уникален: см. «Повторы» ниже |
type |
да | sale — продажа, refund — возврат |
items[].name |
да | Название позиции |
items[].spic |
да | ИКПУ (МХИК) из каталога tasnif.soliq.uz |
items[].packageCode |
да | Код упаковки из того же каталога |
items[].quantity |
да | Количество в тысячных |
items[].unitPrice |
да | Цена за единицу в тийинах |
items[].discount |
нет | Скидка на позицию в тийинах |
items[].vatPercent |
да | Ставка НДС, % |
items[].barcode |
нет | Штрихкод |
items[].markingCode |
нет | Код маркировки с упаковки |
items[].commissionTin |
нет | ИНН комитента (9 цифр) |
discount |
нет | Общая скидка на чек в тийинах, включая скидки позиций; остаток разносится по позициям |
payments.cash, payments.card |
да | Оплата наличными и картой в тийинах |
payments.cardType |
нет | personal или corporate |
refund |
для возврата | Данные исходного чека: terminalId, receiptSeq, fiscalSign, dateTime — как их вернул ответ на продажу |
Сумма оплат должна точно равняться сумме позиций за вычетом скидок, иначе — BRIDGE_PAYMENT_MISMATCH. Позиции с нулевой стоимостью в ФМ не отправляются.
Ответ 200:
{ "externalId": "POS-2026-000123", "duplicate": false, "result": { "terminalId": "UZ170703100236", "receiptSeq": "1532", "fiscalSign": "287265419807", "dateTime": "2026-10-05 14:12:03", "qrCodeUrl": "https://ofd.soliq.uz/check?…" }}Повторы
Заголовок раздела «Повторы»Повторный запрос с тем же externalId и тем же содержимым не пробивает чек второй раз, а возвращает прежний результат с duplicate: true. Поэтому после обрыва связи запрос можно повторить.
| Ситуация | Ответ |
|---|---|
| Чек уже пробит | 200, duplicate: true |
| Тот же номер, другое содержимое | 409 BRIDGE_EXTERNAL_ID_CONFLICT |
| Чек пробивается прямо сейчас | 409 BRIDGE_RECEIPT_IN_PROGRESS — повторите через несколько секунд |
| ФМ отказал | 422 OFD1_* / OFD2_* — чек не пробит; тот же чек можно повторить после устранения причины |
| ФМ не ответил вовремя | 504 BRIDGE_FISCAL_DRIVE_TIMEOUT, повтор вернёт 409 BRIDGE_RECEIPT_STATE_UNKNOWN |
Если ФМ не ответил, чек мог пробиться. Приложение не даст пробить его ещё раз, пока кассир не проверит чек и не нажмёт «Не пробит» в разделе «Чеки». Узнать состояние: GET /v1/receipts/{externalId}.
Любая ошибка приходит в одном формате; title и action — на языке из Accept-Language (ru, uz-latn, uz-cyrl) или на языке приложения:
{ "error": { "code": "BRIDGE_PAYMENT_MISMATCH", "title": "Сумма оплаты не совпадает с суммой чека", "action": "…", "message": "Оплата 999999 тийин не равна сумме к оплате по позициям 1000000 тийин" }}Покажите кассиру title и action, а code запишите в лог. Все коды — на странице «Коды ошибок».
| HTTP | Когда |
|---|---|
400 |
Неверный чек |
401 |
Нет или неверный X-Api-Key |
402 |
Касса отключена: см. «Подписка» ниже (LICENSE_*) |
403 |
Запрос из браузера со стороннего сайта |
404 |
Чека нет в журнале |
409 |
Номер чека занят, чек пробивается или исход неизвестен |
422 |
Фискальный модуль отказал |
502 |
FiscalDriveAPI или сервис маркировки ответил непонятно |
503 |
Приложение не настроено, FiscalDriveAPI не запущен или нет связи с сервером (CLOUD_*) |
504 |
ФМ не ответил вовремя |
Смена и отправка чеков
Заголовок раздела «Смена и отправка чеков»| Запрос | Что делает |
|---|---|
POST /v1/shift/open |
Открыть смену |
POST /v1/shift/close |
Закрыть смену |
POST /v1/queue/send |
Отправить накопленные в ФМ чеки в ОФД; ответ — сколько осталось (remaining, у v2 — null) |
GET /v1/status |
Версия приложения, настройка, состояние ФМ и подписки (license) |
Операции с ФМ выполняются по одной: если кассовая программа шлёт несколько запросов сразу, они встают в очередь.
Проверка маркировки
Заголовок раздела «Проверка маркировки»POST /v1/marking/check — проверить код маркировки с упаковки перед продажей. Приложение спрашивает сервер, а тот — tasnif.soliq.uz, поэтому кассе нужен интернет.
{ "markingCode": "0104780069000130215Ab…", "spic": "10901001001000000", "packageCode": "1500213", "commissionTin": "123456789"}commissionTin — только для товара комитента. Ответ 200: { "valid": true, "message": null }; если код не годится, valid: false, а в message — причина от tasnif.
Если касса не вошла на сервер или сервер недоступен — 503 с кодом CLOUD_*; если tasnif не ответил — 502 CLOUD_MARKING_UNAVAILABLE. Пробивать чек можно и без проверки маркировки: это решает кассовая программа.
Связь с сервером
Заголовок раздела «Связь с сервером»Пробитие чека не требует связи с сервером: приложение пишет чек в журнал на кассе и отправляет журнал на сервер каждые 30 секунд и сразу после каждого чека. Пока связи нет, чеки ждут на кассе и уходят, как только она появится. Время смены приложение берёт с сервера, а не с часов компьютера.
Подписка
Заголовок раздела «Подписка»Касса работает по ежемесячной подписке. Если касса отключена, на продажу и возврат приложение отвечает 402, а сам чек в журнал не попадает: после оплаты его можно пробить с тем же externalId.
| Код | Когда |
|---|---|
LICENSE_EXPIRED |
Подписка не оплачена и льготные дни кончились |
LICENSE_BLOCKED |
Кассу отключил администратор |
LICENSE_OFFLINE |
Касса больше суток без связи с сервером |
LICENSE_MISSING |
Касса не вошла на сервер или ещё не получила лицензию |
Что работает у отключённой кассы:
- повтор уже пробитого чека —
200сduplicate: true; - закрытие смены (
POST /v1/shift/close) и отправка чеков в ОФД (POST /v1/queue/send); GET /v1/statusиGET /v1/receipts/{externalId}.
Состояние подписки — в GET /v1/status, поле license: state (active, expiring — осталось 5 дней и меньше, grace — льготные дни, blocked), code, paidThrough (последний оплаченный день) и disableAt (когда касса отключится). Кассовая программа может заранее предупредить кассира, пока state — expiring или grace.