Mark GreenEngine — входящий API программы. Руководство интегратора

Документ MGE-API-DOCS-001, редакция 1 от 01.11.2026; статус — подготовлен к проверке, не опубликован
Версия программы Mark GreenEngine 2.2.0, сборка MarkGreenEngine-2.2.0-78ca5a8-win-x64.zip (SHA-256 305b39c63ef4876ff35e2e303d4a07ca84bc624f0d8bd951239ad7c6db05c0f8)
Исходный код RTitovich/GE-TrueAPI, commit сборки 78ca5a874c6e8094da14024f2094857960bdcd54; код API в src/ не менялся до 87eed5740374e13d1639a8d410618efbfd238ae6
Версия API /api/v1 (единственная)
Материалы api/README.md: реестр методов, OpenAPI, примеры cURL/PowerShell/1С/.http, отчёт проверки

Руководство описывает HTTP API, который программа Mark GreenEngine открывает на компьютере пользователя для учётной системы, кассового ПО, панели или собственного сервиса. Все поля, коды и тексты ниже взяты из кода сборки 2.2.0, а примеры ответов — из прогона этой сборки на изолированном стенде (раздел 12).

Уровни проверки, которые указаны в карточках:

Оглавление

  1. Карта взаимодействий
  2. Назначение и совместимость
  3. Подготовка программы
  4. Быстрый старт
  5. Общий контракт
  6. Авторизация, роли, организации и лицензия
  7. Каталог методов
  8. Карточки методов
  9. Практические сценарии
  10. Длительные операции и повторы
  11. Ошибки и устранение проблем
  12. Совместимость, ограничения и найденные расхождения
  13. Как проверялся этот документ

0. Карта взаимодействий

Контур Что это В этом руководстве
Входящий API программы (https://<компьютер>:<порт>/api/v1) HTTP JSON API, который поднимает сама программа, пока она запущена Полный каталог и карточки — разделы 6–7
Сервер Mark GreenEngine (mark.ge39.ru): координация копий, лицензирование, кабинет Программа сама обращается к нему; ключ подключения программы вводится в окне «Лицензия» Не является API для интегратора. Описано только влияние лицензии на ответы входящего API — 5.4
ГИС МТ «Честный знак» (True API), ЭДО Лайт Программа обращается от имени организаций по их сертификатам Описаны только те результаты, которые возвращают методы входящего API (/tokens, /kegs, /codes/check); API ГИС МТ здесь не перепечатывается
ЕГАИС / УТМ Программа работает с УТМ сама Методов входящего API для ЕГАИС в 2.2.0 нет
Файловый обмен ЭДО (папки ЭДО\Исходящие, ЭДО\Входящие\<организация>) Выгрузка УПД из 1С в папку и получение входящих документов файлами Это не HTTP API; порядок работы — в README.txt дистрибутива, раздел «ЭДО Лайт»
Интеграция с PanelPD (исходящие вызовы программы в api.php панели) Очередь подключения кег, остатки, коды 1С — по настройке Panel Не входит во входящий API; роль ключа panel названа по этой панели, но доступна любому интегратору
API приёма чеков (сервер Mark) Запланирован, не выпущен Не описывается; страница /docs/receipts-api на сайте отсутствует
Кабинет, оплата (ЮKassa), обмен с 1С:Бухгалтерией Сервисы сайта Не входят в API программы

Протокол входящего API — набор HTTP-маршрутов под общим префиксом /api/v1 с JSON-телами. Отдельного поля «команда» или JSON-RPC нет: операция определяется HTTP-методом и путём.

1. Назначение и совместимость

Через входящий API внешняя система может:

Компонент Требование
Программа Mark GreenEngine 2.2.0 для Windows x64, запущенная (API работает, пока работает программа)
Включение Server:Enabled = true — в новой установке 2.2.0 выключено (2.1)
Сеть TCP-порт Server:Port (по умолчанию 8443) доступен клиенту; рекомендуемая среда — локальная сеть или VPN
Редакции Одна сборка для всех пользователей: набор методов не зависит от редакции. Интеграция с PanelPD — отдельная настройка, на входящий API не влияет
Лицензия Методы, которые обращаются в ГИС МТ, работают только для организаций с действующим правом в центре лицензирования (5.4)

История контракта /api/v1 по коду (новые поля и методы только добавлялись):

Версия (commit) Изменение
GE TrueAPI, первые версии (a47d9b0, e824520, 29.09.2026) /health, /tokens*, /kegs, /kegs/{cis}, /kegs/connect, /kegs/track, /documents/{doc_id}
4f2b119, 18.10.2026 (дерево 1.11.1) GET /kegs/rests/fias
688fc49, 20.10.2026 (дерево 1.17.0) GET/POST /codes/check
1.19.0 /codes/check: поля статуса продажи sale_*
2.0.0 Программа переименована в Mark GreenEngine; контракт /api/v1 не менялся
2.2.0 Ошибка 403 license_denied (LicenseGate: право организации из центра лицензирования)

2. Подготовка программы

2.1. Включить API

В новой установке 2.2.0 API выключен: при сборке дистрибутива в appsettings.json записывается "Server": { "Enabled": false }. Включите одним из способов:

  1. В программе: «⋯» → «Настройки» → «Входящий API» → «Принимать запросы от панели и магазинов», сохранить, перезапустить программу (настройки раздела действуют после перезапуска, в окне они помечены знаком ⟳).
  2. Или в файле appsettings.Local.json рядом с MarkGreenEngine.exe (образец — appsettings.Local.example.json):

json { "Server": { "Enabled": true } }

и перезапустить программу. Значения из appsettings.Local.json перекрывают appsettings.json.

Состояние сервера видно на вкладке «API-журнал»: адрес вида https://<имя компьютера>:8443/api/v1 или причина, по которой API не запущен (например, «порт 8443 уже занят другой программой»). Если Server:Enabled = false, там написано «Server:Enabled = false в appsettings.json».

2.2. Настройки раздела Server

Параметр По умолчанию Смысл
Server:Enabled false в дистрибутиве 2.2.0 Запускать входящий API
Server:Port 8443 TCP-порт. Сервер слушает его на всех адресах компьютера (0.0.0.0 и [::])
Server:UseHttps true HTTPS. При false — обычный HTTP: только за VPN или обратным прокси с собственным TLS
Server:PfxPath server.pfx Файл сертификата (относительный путь — от папки программы)
Server:PfxPassword пусто Пароль своего PFX; для созданного программой не задаётся
Server:AutoCertificate true Если PFX нет — создать самоподписанный сертификат
Server:ExtraHostNames [] Дополнительные DNS-имена и IP для сертификата (2.3)
Server:PanelRequestsPerMinute 120 Лимит запросов в минуту на один ключ роли panel
Server:RetailRequestsPerMinute 60 Лимит на ключ роли retail; он же — лимит GET /health с одного IP
Server:AuthFailuresPerMinute 30 Неудачных попыток ключа с одного адреса в минуту до блокировки адреса
Server:MaxRequestBodyMegabytes 8 Наибольший размер тела запроса, МБ (1–100 в окне настроек)

Переменные окружения (ASPNETCORE_URLS и т. п.) и секция Kestrel на входящий API не влияют. Активные значения видны в окне «Настройки», фактический адрес и порт — на вкладке «API-журнал».

Одновременно на одном порту может работать только одна копия программы с включённым API, даже у разных пользователей Windows. Второй копии задайте другой порт.

2.3. Сертификат и закрепление

При первом запуске API с AutoCertificate = true программа создаёт самоподписанный сертификат CN=GE-TrueAPI (RSA, SHA-256, срок 5 лет) в файле server.pfx (пароль — в server.pfx.key, зашифрован DPAPI пользователя Windows). В сертификат записываются имена localhost, имя компьютера, 127.0.0.1, ::1 и значения Server:ExtraHostNames.

Сертификат не выдан доверенным центром, поэтому клиент не может проверить цепочку обычным способом и должен проверять, что отвечает именно этот сервер:

Pin, отпечаток, адрес API, команда брандмауэра и готовая команда curl показаны в окне «⋯» → «Сертификат и подключение API…» (также кнопка на вкладке «API-журнал»).

2.4. Брандмауэр и сеть

Для доступа с другого компьютера откройте порт в брандмауэре Windows (командная строка от имени администратора; та же команда с кнопкой «Копировать» есть в окне сертификата):

netsh advfirewall firewall add rule name="Mark GreenEngine 8443" dir=in action=allow protocol=TCP localport=8443

Не публикуйте порт API в интернет и не отключайте ключи ради упрощения: API даёт доступ к токенам ГИС МТ и действиям от имени организаций. Для удалённых точек используйте VPN; дополнительно ограничьте ключ адресами (allowed_ips, 5.2).

2.5. Ключи доступа

Ключи хранятся в api_keys.json рядом с MarkGreenEngine.exe и редактируются в «Настройки» → «Ключи API» (кнопки «Добавить», «Новый ключ», «Организации…»). Если файла нет, при первом открытии окна ключей он создаётся из api_keys.example.json со случайными ключами и без ограничений по адресам. Файл перечитывается автоматически через ~0,5 с после сохранения — перезапуск не нужен. Формат — 5.2.

3. Быстрый старт

Пример — на компьютере с программой (localhost) и порте 8443. В других случаях подставьте адрес из вкладки «API-журнал».

Шаг 1. Проверьте доступность (ключ не нужен). Pin возьмите из окна «Сертификат и подключение API». Команда для cmd.exe и PowerShell одинакова (в PowerShell пишите именно curl.exe, а не curl — это другая команда):

curl.exe --insecure --pinnedpubkey "sha256//<pin из окна программы>" https://localhost:8443/api/v1/health

Ожидаемый ответ (version — версия и commit вашей сборки, environment — среда ГИС МТ из настройки TrueApi:Environment):

{
  "status": "ok",
  "version": "2.2.0+78ca5a874c6e8094da14024f2094857960bdcd54",
  "uptime_seconds": 0,
  "environment": "prod"
}

--insecure выключает только проверку цепочки, --pinnedpubkey при этом обязателен: без него соединение не защищено от подмены сервера.

Шаг 2. Получите ключ. В «Настройки» → «Ключи API» добавьте ключ, выберите роль (panel — все методы, retail — чтение кег и проверка кодов) и организации. Сохраните значение ключа в файл, а не в командную строку: файл mge-api-key.txt с одной строкой

X-Api-Key: <значение ключа>

и закройте к нему доступ другим пользователям (icacls mge-api-key.txt /inheritance:r /grant:r "%USERNAME%:R").

Шаг 3. Первый запрос с ключом — список организаций и состояние их токенов (роль panel). -H @файл читает заголовок из файла, значение ключа не попадает в командную строку и историю:

curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt https://localhost:8443/api/v1/tokens
[
  {
    "client": "ООО «Пример»",
    "inn": "0100000001",
    "enabled": true,
    "status": "active",
    "expire_date": "2026-11-01T19:00:00+00:00",
    "next_rotation": "2026-11-01T18:00:00+00:00"
  },
  {
    "client": "ИП Образцов О. О.",
    "inn": "010000000001",
    "enabled": true,
    "status": "active",
    "expire_date": "2026-11-01T19:00:00+00:00",
    "next_rotation": "2026-11-01T18:00:00+00:00"
  },
  {
    "client": "ООО «Без права»",
    "inn": "0100000003",
    "enabled": true,
    "status": "not_received",
    "next_rotation": "2026-11-01T09:00:00+00:00"
  },
  {
    "client": "ООО «Нет связи»",
    "inn": "0100000004",
    "enabled": true,
    "status": "error",
    "next_rotation": "2026-11-01T09:01:00+00:00",
    "last_error": "ГИС МТ: сервис временно недоступен (HTTP 503)"
  },
  {
    "client": "ООО «Отключено»",
    "inn": "0100000005",
    "enabled": false,
    "status": "disabled",
    "next_rotation": "2026-11-01T09:00:00+00:00"
  },
  {
    "client": "ООО «Нет сертификата»",
    "inn": "0100000006",
    "enabled": true,
    "status": "cert_error",
    "last_error": "Сертификат с серийным номером STAND06 не найден в хранилище «Личное»"
  }
]

Значение client из этого ответа — имя организации, которое передаётся в остальные методы.

Шаг 4. Типичные причины неуспеха.

Признак Причина и действие
curl: (7) Failed to connect API выключен или программа не запущена; порт закрыт брандмауэром; на порту другая программа — см. вкладку «API-журнал»
curl: (90) SSL: public key does not match pinned public key Отвечает другой сервер или сертификат пересоздан — сверьте pin в окне программы
401 unauthorized Нет заголовка X-Api-Key, ключ неверен, короче 24 символов или api_keys.json не найден
403 forbidden «Доступ с адреса … запрещён» Адрес клиента не входит в allowed_ips ключа
429 rate_limited сразу на всё С адреса было 30 неудачных попыток (неверный ключ или адрес не из allowed_ips) — подождите Retry-After секунд

Готовые сценарии на PowerShell и 1С — api/examples.

4. Общий контракт

4.1. Транспорт

Свойство Значение
Базовый адрес https://<имя или IP компьютера>:<Server:Port>/api/v1
Протоколы HTTP/1.1 и HTTP/2 (TLS 1.2 / 1.3). При UseHttps = false — HTTP/1.1 без TLS
Кодировка UTF-8 в запросах и ответах
Ответы Content-Type: application/json; charset=utf-8 — у всех ответов программы, включая ошибки. Исключение — ошибки протокола, на которые отвечает сам Kestrel до программы (заголовки больше 16 КБ — 431, заголовки не пришли за 15 с — 408, некорректная строка запроса — 400): ответ без JSON-тела. Заголовка Server нет
Тело запроса JSON-объект для POST /kegs/connect, POST /kegs/track, POST /codes/check. Заголовок Content-Type сервер не проверяет, но передавайте application/json
Размер тела До Server:MaxRequestBodyMegabytes (8 МБ). Больше — 413 payload_too_large; сервер может закрыть соединение раньше, чем клиент допишет тело, и тогда клиент видит обрыв соединения вместо ответа
Заголовки До 16 КБ суммарно; заголовки должны прийти за 15 с. Соединений одновременно — до 256, потоков HTTP/2 на соединение — до 32
Неизвестный путь 404 not_found «Метод API не найден» — после проверки ключа (без ключа — 401)
Неподдерживаемый HTTP-метод на существующем пути Тоже 404 not_found «Метод API не найден» (не 405)
Регистр пути Не учитывается
Таймаут ответа Общего нет. GET /tokens/current и POST /tokens/rotate ждут токен не дольше 60 с; остальные методы ждут ответа ГИС МТ столько, сколько длятся запросы программы в ГИС МТ (каждый — до TrueApi:TimeoutSeconds, по умолчанию 30 с; метод может сделать несколько запросов: токен, сведения, документ). POST /kegs/connect дополнительно ждёт подпись — до Tokens:SignTimeoutSeconds (120 с). Рекомендуемый таймаут клиента — 180 с для connect, 120 с для остальных

4.2. JSON

4.3. Даты, время, количества

Вид Формат Пример
Дата Строго yyyy-MM-dd, в запросе и в ответе. Другой формат в запросе — 400 "2026-11-01"
Дата-время в ответе ISO 8601 со смещением, от 0 до 7 знаков дробной части секунды: yyyy-MM-ddTHH:mm:ss[.fffffff]±hh:mm. Смещение зависит от источника (обычно +00:00; у generated_at — +03:00). Сравнивайте как момент времени, а не как строку "2026-11-01T09:00:10+00:00"
«Сегодня» По московскому времени (UTC+3), независимо от часового пояса компьютера и клиента —
Объём кеги в /kegs Целое, миллилитры (inner_unit_count, sold_unit_count, rest) 30000
Литры в /kegs/rests/fias, /codes/check Десятичное число JSON, литры, до 3 знаков после запятой (мл ÷ 1000, округление от нуля) 17.5
Проценты Целое 0–100, округление от нуля 58

4.4. Коды маркировки в запросе

Методы кег (/kegs*, connect, track) приводят код к виду КИ «01» + GTIN (14 цифр) + «21» + серийный номер (1–20 символов ASCII с кодами 33–122, то есть печатные символы, кроме {, |, }, ~) и отбрасывают криптохвост. Принимаются:

/codes/check разбирает шире: дополнительно русскую раскладку сканера, КИТУ (18 цифр, 00+18, (00)+18) и прочие коды ГИС МТ из 18–74 печатных символов.

Код в пути (/kegs/{cis}) кодируйте целиком как компонент URL (encodeURIComponent, Uri.EscapeDataString, КодироватьСтроку(…, СпособКодированияСтроки.КодировкаURL)): / → %2F, GS → %1D, % → %25, ? → %3F, # → %23, + → %2B. Код в query (/codes/check?cis=) кодируется так же: незакодированный + превратится в пробел. Коды со спецсимволами удобнее передавать в теле POST.

4.5. Формат ошибки

Любой ответ с кодом 4xx/5xx, кроме оборванных соединений, — объект:

Поле Тип Смысл
error строка, всегда Машиночитаемый код — по нему строится логика клиента
message строка, всегда Текст на русском для журнала и пользователя; формулировки могут меняться между версиями
details массив строк, не всегда Пояснения: ошибки по полям и кодам (validation_failed) или причина отказа лицензии (license_denied)
{
  "error": "validation_failed",
  "message": "Ошибка проверки данных",
  "details": [
    "Некорректный код маркировки: «12345» (проверьте раскладку клавиатуры сканера: латиница, без CapsLock)",
    "Для юрлица обязателен КПП торговой точки (9 цифр, в 5-6 позиции допустимы буквы A-Z по формату ФНС)",
    "Для юрлица ФИАС не указывается",
    "КИ 010460000000001021FoR0001: дата подключения 05.11.2026 позже сегодняшней"
  ]
}
error HTTP Когда
validation_failed 400 Нет обязательного параметра, неверный формат, некорректный JSON, ошибки проверки документа CONNECT_TAP. Также ошибка чтения тела (обрыв и т. п.) — «Некорректный HTTP-запрос»
unauthorized 401 Нет X-Api-Key, он передан больше одного раза или не совпал ни с одним ключом
forbidden 403 Адрес клиента не входит в allowed_ips; роль ключа не подходит методу; нет доступа к указанной организации
license_denied 403 У организации нет действующего права в центре лицензирования, или копия программы не подтверждена (5.4)
not_found 404 Метод не найден; организация не настроена или выключена; документ, кега не найдены (в том числе принадлежат организации, недоступной ключу)
payload_too_large 413 Тело больше Server:MaxRequestBodyMegabytes
rate_limited 429 Превышен лимит запросов ключа или адреса; есть заголовок Retry-After (секунды)
internal_error 500 Необработанная ошибка программы, message — «Внутренняя ошибка». Подробности — в журнале программы
gis_error 502 Ошибка обращения к ГИС МТ: сеть, отказ сервиса, бизнес-ошибка. message — текст ГИС МТ или клиента ГИС МТ
token_unavailable 503 Не удалось получить токен ГИС МТ организации (в /tokens/* и /codes/check)
signing_failed 503 Ошибка электронной подписи: сертификат не найден, нет закрытого ключа, PIN, носитель, КриптоПро
conflict 409 Объявлен в коде, ни один метод 2.2.0 его не возвращает

Клиент закрыл соединение до ответа — в журнале программы запрос записывается с кодом 499; клиент ответа не получает.

4.6. Лимиты частоты

Окно — фиксированная минута (не скользящая), очереди нет: лишний запрос сразу получает 429.

Что считается Лимит по умолчанию Ключ раздела
Запросы одного ключа роли panel 120 в минуту key:Panel:<имя ключа>
Запросы одного ключа роли retail 60 в минуту key:Retail:<имя ключа>
GET /health без ключа с одного IP 60 в минуту (значение лимита retail) ip:<адрес>
Неудачные попытки с одного адреса — неверный ключ (401) или адрес не из allowed_ips (403); IPv4 — адрес, IPv6 — сеть /64; loopback, link-local и ULA — точный адрес 30 за минуту от первой неудачи —

Когда с адреса набралось 30 неудачных попыток, до конца минуты, отсчитанной от первой из них, адрес получает 429 на любой запрос (начиная с 31-го), даже с верным ключом. Такие отказы не пишутся в журнал запросов, в журнал программы — одно предупреждение. GET /health эта блокировка не затрагивает.

{
  "error": "rate_limited",
  "message": "Превышен лимит запросов, повторите позже"
}

Заголовок Retry-After — целое число секунд до конца окна (в примере 60).

4.7. Журнал

Программа хранит последние 500 запросов (время, имя ключа, IP, метод, путь без query-строки, код ответа, длительность) — вкладка «API-журнал», и пишет их в файл logs/api-ГГГГММДД.log. Значения ключей, токены, query-строки и тела запросов в журнал не попадают. Имя организации из пути (/kegs/{cis} содержит только код) не записывается; query-строка с client не пишется.

5. Авторизация, роли, организации и лицензия

5.1. Какие ключи бывают — не путайте

Что Где задаётся Для чего Передаётся во входящий API
Ключ входящего API (X-Api-Key) api_keys.json этой копии программы Доступ вашей системы к API этой копии Да, в каждом запросе, кроме /health
Ключ подключения программы к кабинету Окно «Лицензия» программы Связь копии с аккаунтом на сайте Mark GreenEngine и получение прав организаций Нет
Сертификат УКЭП / МЧД организации Профиль организации в программе Получение токена ГИС МТ, подпись документов Нет; используется программой
Токен ГИС МТ (uuid_token) Выдаёт ГИС МТ, хранит программа Запросы в True API Возвращается методами /tokens/current, /tokens/rotate
Ключ разрешительного режима Профиль организации («Ключ разрешительного режима») Статус продажи в /codes/check Нет; в ответы и журналы не попадает
Сессия кабинета на сайте Браузер Кабинет, оплата Нет

5.2. Файл api_keys.json

Массив объектов (допускаются комментарии // и завершающие запятые):

[
  { "name": "accounting", "key": "<случайная строка от 32 символов>", "role": "panel", "clients": ["*"], "allowed_ips": ["192.168.10.5"] },
  { "name": "shop-1", "key": "<случайная строка от 32 символов>", "role": "retail", "clients": ["ИП Образцов О. О."] }
]
Поле Тип Обязательно Смысл
name строка да Имя ключа: показывается в журнале и в сообщениях об отказе; уникально без учёта регистра
key строка да Секрет для X-Api-Key, не короче 24 символов (рекомендуется 32+ случайных); пробелы по краям обрезаются. Значения ключей не должны совпадать
role "panel" | "retail" да Роль (5.3)
clients массив строк нет Организации — их имена в профилях программы (без учёта регистра); "*" — все. Нет поля: для panel — все, для retail — ни одной
allowed_ips массив строк нет IPv4/IPv6-адреса и сети CIDR (10.0.0.0/8), с которых разрешён ключ; без ведущих нулей в октетах. Пусто или нет поля — с любого адреса. ::ffff:a.b.c.d равнозначен IPv4

Некорректный элемент пропускается (ошибка — в журнале программы, значение ключа в ошибку не попадает); остальные ключи действуют. Если файл целиком не разобран, остаются прежние ключи. Если файла нет — все запросы, кроме /health, получают 401.

Сервер хранит только SHA-256 ключа и сравнивает его за постоянное время. Отзыв ключа — удалить элемент или сменить key и сохранить файл: изменение действует через ~0,5 с.

5.3. Роли и организации

Роль Методы
panel Все методы, включая изменяющие и выдачу токенов
retail Только чтение и проверка: GET /kegs, GET /kegs/{cis}, GET /kegs/rests/fias, GET /codes/check, POST /codes/check

Порядок проверок: блокировка адреса за неудачные попытки (429) → ключ (401) → адрес из allowed_ips (403) → лимит ключа (429) → роль метода (403 «Метод доступен только ключам с ролью panel») → разбор параметров и тела (400) → организация (403 «Нет доступа к организации …», 404) → проверки данных метода (400) → лицензия организации (403 license_denied, только у методов, которые обращаются в ГИС МТ). Поэтому, например, POST /kegs/connect с ошибками в данных для организации без права получит 400, а не 403 license_denied: лицензия проверяется непосредственно перед обращением в ГИС МТ.

Параметр client — имя организации, как оно задано в профиле программы (поле client в GET /tokens), сравнивается без учёта регистра. ИНН вместо имени не принимается. Методы, которые читают данные нескольких организаций (GET /kegs, GET /kegs/rests/fias, GET /tokens), без client возвращают только организации, доступные ключу. Данные организаций, к которым у ключа нет доступа, в ответах скрыты: кега или документ такой организации даёт 404, как будто их нет. Исключение — POST /kegs/track (11.3, Д-2).

5.4. Лицензия (LicenseGate) и несколько организаций

С версии 2.2.0 каждая операция, которая обращается в ГИС МТ от имени организации, разрешается программой только при действующем праве этой организации — его выдаёт центр лицензирования по ключу подключения программы. Право — у каждой организации (ИНН) своё. Один ключ подключения и одна подписка аккаунта не означают права для всех организаций копии: у организации без пробного периода или оплаты операции запрещены, даже если у соседней организации всё оплачено.

Методы Лицензия
GET /tokens/current, POST /tokens/rotate, POST /kegs/connect, GET /codes/check, POST /codes/check, GET /kegs/{cis} с запросом в ГИС МТ (кега не на учёте с client, или refresh=true) Проверяется для организации запроса. Нет права — 403 license_denied
POST /kegs/track Проверяется, но отказ возвращается внутри ответа 200: каждый ещё не стоящий на учёте код получает outcome: "failed" с текстом причины (11.3, Д-1)
GET /health, GET /tokens, GET /kegs, GET /kegs/{cis} без запроса в ГИС МТ, GET /kegs/rests/fias, GET /documents/{doc_id}, DELETE /kegs/{cis} Не проверяется: чтение и изменение сохранённых в программе данных

Ответ license_denied: message — причина по-русски, details[0] — код причины программы, details[1] — код причины центра (может быть пустой строкой):

{
  "error": "license_denied",
  "message": "Нет действующего права организации (ЮЛ ИНН 0100000003): нет пробного периода или оплаты. Операция: запрос в ГИС МТ / ЭДО Лайт.",
  "details": [
    "RightDenied",
    "no_subscription"
  ]
}
details[0] Что значит Как восстановить
KeyMissing В программе не задан ключ подключения Ввести ключ: «⋯» → «Лицензия»
TrustUnconfigured Сборка без ключей центра лицензирования Установить официальный дистрибутив
NotVerified Программа ещё не получила ответ центра (первый запуск, кэш удалён) Проверить связь с сервером Mark, «Проверить сейчас» в окне «Лицензия»
KeyChanged Ключ подключения изменён — права запрашиваются заново Подождать проверку и повторить
CenterDenied Центр запретил копию: ключ отозван, аккаунт заблокирован, копия отключена в кабинете Кабинет на сайте
ClockUnreliable Часы компьютера переведены назад или состояние недостоверно Синхронизировать время, дождаться проверки центром
OfflineExpired Более 72 часов без успешной проверки в центре (или истёк offline-срок права) Восстановить связь с сервером Mark
SubjectUnknown / SubjectMismatch Не удалось определить ИНН организации по сертификату/МЧД или ИНН профиля и сертификата не совпадают Проверить профиль и сертификат организации
NotRequested Права новой организации ещё не получены — проверка уже запущена Повторить через минуту
RightDenied Центр не дал право: см. details[1] Кабинет: пробный период, оплата, подтверждение организации
RightExpired Срок права организации закончился Продлить в кабинете

Коды центра в details[1]: no_subscription (нет пробного периода или оплаты), expired, subscription_blocked, organization_unverified (организация не подтверждена УКЭП в кабинете), organization_archived, not_in_account (организация не добавлена в аккаунт), license_required, license_invalid, license_revoked, account_blocked, install_disabled, installation_key_mismatch, installation_revoked; может прийти и новый код — показывайте message.

Ответ license_denied — окончательный для этой минуты: повторять запрос имеет смысл только после устранения причины (для NotRequested и KeyChanged — через минуту). Недоступность центра сама по себе не запрещает операции: решает последний проверенный ответ центра и его сроки (до 72 часов без связи).

Серверная настройка центра licensing.enforce_api к этому механизму программы не относится: она управляет проверкой на стороне сайта для старых копий программы. По последнему известному состоянию (отчёт MGE-WEB-007B, 01.11.2026) она выключена; на работу LicenseGate программы 2.2.0 это не влияет.

6. Каталог методов

Все пути — от /api/v1. «Роль» — минимальная роль: метод с ролью retail доступен и ключам panel.

Метод и путь Роль Назначение Изменяет ГИС МТ Лицензия Карточка
GET /health, HEAD /health без ключа Доступность, версия, среда нет нет нет 7.1
GET /tokens panel Организации и состояние токенов (без токенов) нет нет нет 7.2
GET /tokens/current panel Действующий токен организации может получить новый токен да да 7.3
POST /tokens/rotate panel Перевыпуск токена да (новый токен) да да 7.4
POST /kegs/connect panel Подключение кег к оборудованию (CONNECT_TAP) да, необратимо в ГИС МТ да да 7.5
GET /documents/{doc_id} panel Статус документа CONNECT_TAP нет нет нет 7.6
GET /kegs retail Кеги на учёте нет нет нет 7.7
GET /kegs/{cis} retail Одна кега; сведения ГИС МТ о коде при refresh=true или живом запросе обновляет данные при необходимости при обращении в ГИС МТ 7.8
GET /kegs/rests/fias retail Остатки разливного по адресам ФИАС нет нет нет 7.9
POST /kegs/track panel Поставить подключённые кеги на учёт да (учёт программы) да да, в ответе 200 7.10
DELETE /kegs/{cis} panel Снять кегу с учёта да (учёт программы) нет нет 7.11
GET /codes/check retail Проверка одного кода маркировки нет да да 7.12
POST /codes/check retail Проверка до 1000 кодов нет да да 7.13

Других маршрутов в 2.2.0 нет: нет списка документов, нет методов ЭДО, ЕГАИС и чеков. Алиасов и устаревших маршрутов нет. Машиночитаемый реестр — api/api-methods.json, формальная схема — api/openapi.yaml.

7. Карточки методов

Примеры запросов показаны для curl.exe с файлом заголовка ключа из раздела 3; <pin> — pin из окна программы. Все организации, ИНН, коды и токены в примерах вымышлены. Ответы — дословно из прогона на стенде (раздел 12), включая порядок полей.


7.1. GET /health — проверка доступности

Назначение Проверить, что программа запущена и API отвечает; узнать версию программы и среду ГИС МТ. Для мониторинга
Вызов GET /api/v1/health или HEAD /api/v1/health (только заголовки). Путь /health/ с завершающей косой чертой тоже работает
Доступ Без ключа. Блокировка адреса за неудачные попытки на него не действует. Лимит — Server:RetailRequestsPerMinute в минуту с одного IP
Параметры Нет
Последствия Нет
Повтор Безопасен
Асинхронность Нет
Проверка Код; стенд — health, health-head

Ответ 200

Поле Тип Смысл
status строка Всегда "ok"
version строка Версия программы с commit сборки (2.2.0+78ca5a87…)
uptime_seconds целое Секунды с запуска API (на стенде время остановлено, поэтому 0)
environment "prod" | "sandbox" Среда ГИС МТ (TrueApi:Environment)
curl.exe --insecure --pinnedpubkey "sha256//<pin>" https://localhost:8443/api/v1/health
{
  "status": "ok",
  "version": "2.2.0+78ca5a874c6e8094da14024f2094857960bdcd54",
  "uptime_seconds": 0,
  "environment": "prod"
}

Ошибки: 429 rate_limited — превышен лимит с адреса. Если программа не запущена или API выключен — нет TCP-соединения.


7.2. GET /tokens — организации и состояние токенов

Назначение Получить список организаций (профилей) программы и состояние их токенов ГИС МТ. Самих токенов не возвращает. Используйте, чтобы узнать точные имена client
Вызов GET /api/v1/tokens
Доступ Роль panel. Только организации, доступные ключу (clients). Лицензия не проверяется
Параметры Нет
Последствия Нет; запрос в ГИС МТ не выполняется — показывается сохранённое состояние
Повтор Безопасен
Асинхронность Нет
Проверка Код; стенд — tokens-list, tokens-list-org, tokens-list-after

Ответ 200 — массив в порядке профилей программы:

Поле Тип Смысл
client строка Имя организации — значение для параметра client
inn строка, может отсутствовать ИНН организации из профиля
enabled логическое Профиль включён
status строка Состояние токена — таблица ниже
expire_date дата-время, может отсутствовать Окончание срока токена (смещение — как вернула ГИС МТ)
next_rotation дата-время, может отсутствовать Когда программа сама попробует обновить токен
last_error строка, может отсутствовать Текст последней ошибки получения токена
status Смысл
not_received Токен ещё не получен
active Действует
expiring Истекает (меньше Tokens:ExpiringThresholdMinutes, по умолчанию 90 мин)
updating Сейчас обновляется
error Последняя попытка неуспешна (last_error)
cert_error Проблема сертификата организации (last_error)
disabled Профиль выключен
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt https://localhost:8443/api/v1/tokens
[
  {
    "client": "ООО «Пример»",
    "inn": "0100000001",
    "enabled": true,
    "status": "active",
    "expire_date": "2026-11-01T19:00:00+00:00",
    "next_rotation": "2026-11-01T18:00:00+00:00"
  },
  {
    "client": "ИП Образцов О. О.",
    "inn": "010000000001",
    "enabled": true,
    "status": "active",
    "expire_date": "2026-11-01T19:00:00+00:00",
    "next_rotation": "2026-11-01T18:00:00+00:00"
  },
  {
    "client": "ООО «Без права»",
    "inn": "0100000003",
    "enabled": true,
    "status": "not_received",
    "next_rotation": "2026-11-01T09:00:00+00:00"
  },
  {
    "client": "ООО «Нет связи»",
    "inn": "0100000004",
    "enabled": true,
    "status": "error",
    "next_rotation": "2026-11-01T09:01:00+00:00",
    "last_error": "ГИС МТ: сервис временно недоступен (HTTP 503)"
  },
  {
    "client": "ООО «Отключено»",
    "inn": "0100000005",
    "enabled": false,
    "status": "disabled",
    "next_rotation": "2026-11-01T09:00:00+00:00"
  },
  {
    "client": "ООО «Нет сертификата»",
    "inn": "0100000006",
    "enabled": true,
    "status": "cert_error",
    "last_error": "Сертификат с серийным номером STAND06 не найден в хранилище «Личное»"
  }
]

Ошибки: 401, 403 forbidden (ключ retail — «Метод доступен только ключам с ролью panel»), 429.

{
  "error": "forbidden",
  "message": "Метод доступен только ключам с ролью panel"
}

7.3. GET /tokens/current — действующий токен организации

Назначение Получить действующий UUID-токен ГИС МТ организации для собственных запросов в True API
Вызов GET /api/v1/tokens/current?client=<имя организации>
Доступ Роль panel; ключ должен иметь доступ к client; лицензия организации; нужны сертификат организации и связь с ГИС МТ, если токен надо получить
Последствия Если до окончания токена меньше 5 минут или его нет, программа до ответа получает новый токен в ГИС МТ (подпись сертификатом организации)
Повтор Безопасен: повторный вызов вернёт тот же токен, пока он действует
Асинхронность Синхронно; ожидание токена — до 60 с
Проверка Код; стенд — tokens-current* (ГИС МТ синтетическая)

Параметры

Где Имя Тип Обязательно Смысл
query client строка да Имя организации (см. GET /tokens), без учёта регистра

Ответ 200

Поле Тип Смысл
client строка Имя организации
uuid_token строка Токен ГИС МТ. Это секрет: не пишите его в журналы и не передавайте третьим лицам
expire_date дата-время, может отсутствовать Окончание срока
status строка Состояние (7.2)
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt "https://localhost:8443/api/v1/tokens/current?client=%D0%9E%D0%9E%D0%9E%20%C2%AB%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%C2%BB"

(client=ООО «Пример» в URL-кодировке.)

{
  "client": "ООО «Пример»",
  "uuid_token": "00000000-0000-4000-a000-000000000001",
  "expire_date": "2026-11-01T19:00:00+00:00",
  "status": "active"
}

Ошибки

HTTP error Условие Действие
400 validation_failed Нет client Передать параметр
403 forbidden Ключ без доступа к организации Добавить организацию в clients ключа
403 license_denied Нет права организации 5.4
404 not_found Организация не настроена Проверить имя по GET /tokens
503 token_unavailable ГИС МТ не выдала токен или не ответила за 60 с Повторить позже; last_error в GET /tokens
503 signing_failed Сертификат не найден, нет закрытого ключа, PIN, носитель Проверить сертификат в профиле организации
{
  "error": "token_unavailable",
  "message": "ГИС МТ: сервис временно недоступен (HTTP 503)"
}
{
  "error": "signing_failed",
  "message": "Сертификат с серийным номером STAND06 не найден в хранилище «Личное»"
}

7.4. POST /tokens/rotate — перевыпустить токен

Назначение Принудительно получить новый токен, не дожидаясь срока, — например, если ГИС МТ отвергла текущий
Вызов POST /api/v1/tokens/rotate?client=<имя организации>, тело не нужно
Доступ Как у 7.3
Последствия Новый вход в ГИС МТ с подписью сертификатом организации. Новый токен получают все пользователи программы (окно, фоновые задачи, панель)
Повтор Каждый вызов — новый вход в ГИС МТ. Не вызывайте в цикле: для обычной работы достаточно GET /tokens/current
Асинхронность Синхронно, до 60 с
Проверка Код; стенд — tokens-rotate (синтетическая ГИС МТ выдаёт детерминированный токен, поэтому значение совпало с прежним)

Параметры, ответ и ошибки — как у 7.3. Если ГИС МТ ответила без токена — 503 token_unavailable «ГИС МТ не вернула токен».

curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt -X POST "https://localhost:8443/api/v1/tokens/rotate?client=%D0%9E%D0%9E%D0%9E%20%C2%AB%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%C2%BB"
{
  "client": "ООО «Пример»",
  "uuid_token": "00000000-0000-4000-a000-000000000001",
  "expire_date": "2026-11-01T19:00:00+00:00",
  "status": "active"
}

7.5. POST /kegs/connect — подключить кеги к оборудованию (CONNECT_TAP)

Назначение Сформировать, подписать и отправить в ГИС МТ документ «Подключение кега к оборудованию для розлива» по одной или нескольким кегам. После успешной обработки кеги ставятся на учёт программы
Вызов POST /api/v1/kegs/connect, тело JSON
Доступ Роль panel; доступ к client; лицензия организации; сертификат организации, КриптоПро, токен ГИС МТ
Последствия Необратимая юридически значимая операция в ГИС МТ: подписанный документ отправлен от имени организации. Отменить его через API нельзя. После CHECKED_OK кеги записываются в учёт программы (источник connect_tap), панель (если включена) получает статус документа и остатки
Повтор Ключа идемпотентности нет. Защита от дубля — по кодам: пока документ с кодом обрабатывается, повтор отклоняется 400 «уже отправлен в документе , ожидается результат»; после подключения — «уже подключён». Запрос не отменяется при обрыве соединения клиентом: если документ подписан и отправлен, он будет зарегистрирован и обработан. Порядок восстановления — 9.2
Асинхронность Ответ 202 сразу после отправки; результат — GET /documents/{doc_id} (7.6)
Совместимость Только товарная группа программы (TrueApi:ProductGroup, по умолчанию beer)
Проверка Код; стенд — connect* (подпись и ГИС МТ синтетические). Реальная отправка в ГИС МТ в рамках документа не выполнялась

Параметры (тело)

Поле Тип Обязательно Ограничения Смысл
client строка да Имя организации От чьего имени подключается кега
kpp строка для ЮЛ, если нет КПП по умолчанию в профиле 9 символов: 4 цифры, 2 цифры или A–Z, 3 цифры КПП торговой точки. Для ИП передавать нельзя
fias_id строка для ИП, если нет ФИАС по умолчанию в профиле GUID Адрес точки по ФИАС. Для ЮЛ передавать нельзя. Хранится в виде GUID в нижнем регистре
codes массив да 1–100 000 элементов, без повторов Кеги
codes[].cis строка да Код маркировки в любом виде из 4.4 Код кеги; приводится к КИ
codes[].connect_date дата нет Не позже сегодняшней и не раньше 5 лет назад (по Москве) Дата подключения. Нет — сегодня по Москве
codes[].expiration_date дата нет Позже connect_date Срок годности после вскрытия

Тип организации определяется по ИНН: 10 цифр — юрлицо (нужен КПП), 12 — ИП (нужен ФИАС). Пустая строка в kpp/fias_id равнозначна отсутствию поля: берётся значение по умолчанию из профиля.

curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt -H "Content-Type: application/json" --data-binary @connect.json https://localhost:8443/api/v1/kegs/connect

connect.json (UTF-8):

{
  "client": "ООО «Пример»",
  "codes": [
    {
      "cis": "010460000000001021NeW0001",
      "connect_date": "2026-11-01",
      "expiration_date": "2026-12-31"
    }
  ]
}

Ответ 202 Accepted, заголовок Location: /api/v1/documents/<doc_id>:

Поле Тип Смысл
doc_id строка Идентификатор документа в ГИС МТ
status строка IN_PROGRESS
codes массив Принятые коды: cis (КИ без криптохвоста), connect_date (с подставленной датой), expiration_date (если был)
{
  "doc_id": "00000000-0000-4000-9000-000000000001",
  "status": "IN_PROGRESS",
  "codes": [
    {
      "cis": "010460000000001021NeW0001",
      "connect_date": "2026-11-01",
      "expiration_date": "2026-12-31"
    }
  ]
}

Ошибки

HTTP error Условие
400 validation_failed Нет client, некорректный JSON или дата; в details — все ошибки проверки документа сразу: некорректные коды, КПП/ФИАС, даты, повторы, коды уже в обработке или уже подключённые, сертификат организации не найден, не определён ИНН
403 forbidden / license_denied Нет доступа к организации / нет права организации
404 not_found Организация не настроена
502 gis_error ГИС МТ не выдала токен, не приняла документ или недоступна
503 signing_failed Ошибка подписи документа (в том числе подпись не завершилась за Tokens:SignTimeoutSeconds, по умолчанию 120 с — КриптоПро ждёт PIN)
{
  "error": "validation_failed",
  "message": "Ошибка проверки данных",
  "details": [
    "КИ 010460000000001021NeW0001: уже отправлен в документе 00000000-0000-4000-9000-000000000001, ожидается результат"
  ]
}
{
  "error": "validation_failed",
  "message": "Ошибка проверки данных",
  "details": [
    "Сертификат с серийным номером STAND06 не найден в хранилище «Личное»"
  ]
}
{
  "error": "license_denied",
  "message": "Нет действующего права организации (ЮЛ ИНН 0100000003): нет пробного периода или оплаты. Операция: запрос в ГИС МТ / ЭДО Лайт.",
  "details": [
    "RightDenied",
    "no_subscription"
  ]
}
{
  "error": "validation_failed",
  "message": "Некорректный JSON в теле запроса",
  "details": [
    "Некорректное значение поля $.codes[0].connect_date"
  ]
}

7.6. GET /documents/{doc_id} — статус документа CONNECT_TAP

Назначение Узнать результат обработки документа, отправленного POST /kegs/connect (из API, из окна программы или из очереди панели)
Вызов GET /api/v1/documents/{doc_id}
Доступ Роль panel. Документ организации, недоступной ключу, — 404. Лицензия не проверяется
Последствия Нет: ответ из сохранённого состояния. Статус в ГИС МТ опрашивает сама программа: первые 2 минуты — каждые 5 с, затем раз в минуту
Повтор Безопасен
Асинхронность Окончательное состояние — когда есть completed_at. Документ без окончательного ответа ГИС МТ за 30 минут получает UNKNOWN. Если опрос не удаётся (сбой связи, нет токена, у организации закончилось право — опрос идёт её токеном), документ остаётся IN_PROGRESS и опрашивается до 24 часов, затем получает UNKNOWN
Хранение Окончательные документы хранятся 7 суток после завершения, затем 404. Незавершённые сохраняются в pending_docs.json и переживают перезапуск программы
Проверка Код; стенд — document-new, document-polled, document-done, document-unknown

Параметры: doc_id в пути — значение из ответа connect.

Ответ 200

Поле Тип Смысл
doc_id строка Идентификатор
client строка Организация
status строка Код статуса ГИС МТ
status_text строка Статус по-русски
errors массив строк Ошибки ГИС МТ по документу; пустой — ошибок нет
codes массив Коды документа (cis, connect_date, expiration_date)
submitted_at дата-время Отправка
completed_at дата-время, может отсутствовать Завершение обработки; нет — документ ещё обрабатывается
status status_text Окончательный
IN_PROGRESS (и промежуточные WAIT_FOR_CONTINUATION, UNDEFINED) Проверяется нет
CHECKED_OK Обработан да — кеги поставлены на учёт
CHECKED_NOT_OK Обработан с ошибками да — см. errors
PARSE_ERROR Обработан с ошибками да
PROCESSING_ERROR Техническая ошибка да
CANCELLED Аннулирован да
UNKNOWN Статус не получен да — проверьте кеги через GET /kegs/{cis}?client=…
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt https://localhost:8443/api/v1/documents/00000000-0000-4000-9000-000000000001

Сразу после отправки:

{
  "doc_id": "00000000-0000-4000-9000-000000000001",
  "client": "ООО «Пример»",
  "status": "IN_PROGRESS",
  "status_text": "Проверяется",
  "errors": [],
  "codes": [
    {
      "cis": "010460000000001021NeW0001",
      "connect_date": "2026-11-01",
      "expiration_date": "2026-12-31"
    }
  ],
  "submitted_at": "2026-11-01T09:00:00+00:00"
}

После обработки:

{
  "doc_id": "00000000-0000-4000-9000-000000000001",
  "client": "ООО «Пример»",
  "status": "CHECKED_OK",
  "status_text": "Обработан",
  "errors": [],
  "codes": [
    {
      "cis": "010460000000001021NeW0001",
      "connect_date": "2026-11-01",
      "expiration_date": "2026-12-31"
    }
  ],
  "submitted_at": "2026-11-01T09:00:00+00:00",
  "completed_at": "2026-11-01T09:00:10+00:00"
}

Ошибки: 404 not_found — документ не найден, удалён после 7 суток или принадлежит организации, недоступной ключу.

{
  "error": "not_found",
  "message": "Документ 00000000-0000-4000-9000-999999999999 не найден"
}

7.7. GET /kegs — кеги на учёте

Назначение Список кег, которые программа ведёт на кранах: объём, продано, остаток, сроки
Вызов GET /api/v1/kegs[?client=<имя организации>]
Доступ Роль retail. Без client — кеги всех организаций, доступных ключу; с client — только этой (нужен доступ). Лицензия не проверяется
Последствия Нет; данные из учёта программы (обновляются ею из ГИС МТ каждые Kegs:RefreshIntervalMinutes, по умолчанию 15 мин)
Повтор Безопасен
Список Порядок — по организациям и порядку постановки на учёт; сортировки, фильтров (кроме client) и пагинации нет; возвращается весь список
Проверка Код; стенд — kegs-list, kegs-list-retail, kegs-list-retail-forbidden

Параметры: query client — необязателен. Организация с таким именем может и не существовать: тогда ответ — пустой массив.

Ответ 200 — массив объектов «кега» (поле last_info здесь не выводится):

Поле Тип Смысл
cis строка КИ кеги
client строка Организация учёта
product_name, gtin строки, могут отсутствовать Товар по данным ГИС МТ
connect_date дата, может отсутствовать Дата подключения
expiration_date дата, может отсутствовать Срок годности
expiration_from_document логическое true — срок из документа подключения или задан вручную; false — заводской срок из ГИС МТ
inner_unit_count целое, мл, может отсутствовать Объём кеги
sold_unit_count целое, мл, может отсутствовать Продано
rest целое, мл, может отсутствовать Остаток по ГИС МТ (может быть отрицательным при перепродаже)
rest_percent целое 0–100, может отсутствовать Остаток в процентах (отрицательный остаток — 0)
status, status_ex строки, могут отсутствовать Статус кода в ГИС МТ (INTRODUCED) и расширенный (CONNECT_TAP)
source строка Как кега попала в учёт: connect_tap (подключена программой), api (через POST /kegs/track), manual (вручную в окне), import (загрузка из ГИС МТ)
doc_id строка, может отсутствовать Документ подключения
updated_at дата-время, может отсутствовать Последнее обновление сведений
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt https://localhost:8443/api/v1/kegs
[
  {
    "cis": "010460000000001021AbC1234",
    "client": "ООО «Пример»",
    "product_name": "Пиво светлое фильтрованное, кега 30 л",
    "gtin": "04600000000010",
    "connect_date": "2026-10-28",
    "expiration_date": "2026-12-31",
    "expiration_from_document": true,
    "inner_unit_count": 30000,
    "sold_unit_count": 12500,
    "rest": 17500,
    "rest_percent": 58,
    "status": "INTRODUCED",
    "status_ex": "CONNECT_TAP",
    "source": "connect_tap",
    "doc_id": "00000000-0000-4000-9000-000000000100",
    "updated_at": "2026-11-01T09:00:00+00:00"
  },
  {
    "cis": "010460000000001021AbC1235",
    "client": "ООО «Пример»",
    "product_name": "Пиво светлое фильтрованное, кега 30 л",
    "gtin": "04600000000010",
    "connect_date": "2026-10-30",
    "expiration_date": "2027-01-31",
    "expiration_from_document": false,
    "inner_unit_count": 30000,
    "sold_unit_count": 0,
    "rest": 30000,
    "rest_percent": 100,
    "status": "INTRODUCED",
    "status_ex": "CONNECT_TAP",
    "source": "api",
    "updated_at": "2026-11-01T09:00:00+00:00"
  },
  {
    "cis": "010460000000002721Ip00001",
    "client": "ИП Образцов О. О.",
    "product_name": "Пиво тёмное, кега 50 л",
    "gtin": "04600000000027",
    "connect_date": "2026-10-25",
    "expiration_date": "2027-01-31",
    "expiration_from_document": false,
    "inner_unit_count": 50000,
    "sold_unit_count": 10000,
    "rest": 40000,
    "rest_percent": 80,
    "status": "INTRODUCED",
    "status_ex": "CONNECT_TAP",
    "source": "connect_tap",
    "doc_id": "00000000-0000-4000-9000-000000000101",
    "updated_at": "2026-11-01T09:00:00+00:00"
  },
  {
    "cis": "010460000000003421Ip00002",
    "client": "ИП Образцов О. О.",
    "product_name": "Сидр, кега 20 л",
    "gtin": "04600000000034",
    "connect_date": "2026-10-29",
    "expiration_date": "2027-01-31",
    "expiration_from_document": false,
    "status": "INTRODUCED",
    "status_ex": "CONNECT_TAP",
    "source": "connect_tap",
    "doc_id": "00000000-0000-4000-9000-000000000102",
    "updated_at": "2026-11-01T09:00:00+00:00"
  }
]

Ошибки: 403 forbidden — указана организация, недоступная ключу.

{
  "error": "forbidden",
  "message": "Нет доступа к организации «ООО «Пример»»"
}

7.8. GET /kegs/{cis} — одна кега или сведения ГИС МТ о коде

Назначение Получить запись кеги на учёте с последним ответом ГИС МТ; при необходимости — обновить её или запросить в ГИС МТ код, который не на учёте
Вызов GET /api/v1/kegs/{cis}[?refresh=true][&client=<имя организации>]; {cis} — URL-кодированный код (4.4)
Доступ Роль retail. Кега организации, недоступной ключу, обрабатывается как «не на учёте». Запрос в ГИС МТ — с лицензией организации
Последствия refresh=true или живой запрос — обращение в ГИС МТ; для кеги на учёте сохранённые сведения обновляются. На учёт код не ставится
Повтор Безопасен
Проверка Код; стенд — kegs-get*

Параметры

Где Имя Тип Обязательно Смысл
путь cis строка да Код маркировки, URL-кодированный
query refresh true/false/1/0 нет, false Для кеги на учёте: сначала запросить свежие сведения в ГИС МТ
query client строка для кода не на учёте От имени какой организации запросить код в ГИС МТ

Логика ответа:

  1. Код на учёте у организации, доступной ключу → объект «кега» (7.7) плюс last_info — последний ответ ГИС МТ cisInfo без изменений (поля и регистр — как у ГИС МТ, например requestedCis, partialSaleInfo). client игнорируется.
  2. Код не на учёте и нет client → 404 «КИ не на учёте; укажите client для запроса в ГИС МТ».
  3. Код не на учёте, есть client → живой запрос в ГИС МТ; ответ — объект {cis, client, tracked: false, last_info}.
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt https://localhost:8443/api/v1/kegs/010460000000001021AbC1234
{
  "cis": "010460000000001021AbC1234",
  "client": "ООО «Пример»",
  "product_name": "Пиво светлое фильтрованное, кега 30 л",
  "gtin": "04600000000010",
  "connect_date": "2026-10-28",
  "expiration_date": "2026-12-31",
  "expiration_from_document": true,
  "inner_unit_count": 30000,
  "sold_unit_count": 12500,
  "rest": 17500,
  "rest_percent": 58,
  "status": "INTRODUCED",
  "status_ex": "CONNECT_TAP",
  "source": "connect_tap",
  "doc_id": "00000000-0000-4000-9000-000000000100",
  "updated_at": "2026-11-01T09:00:00+00:00",
  "last_info": {
    "cis": "010460000000001021AbC1234",
    "requestedCis": "010460000000001021AbC1234",
    "gtin": "04600000000010",
    "productName": "Пиво светлое фильтрованное, кега 30 л",
    "status": "INTRODUCED",
    "ownerInn": "0100000001",
    "ownerName": "ООО «Пример»",
    "productGroup": "beer",
    "packageType": "UNIT",
    "producerInn": "0100000008",
    "producerName": "ООО «Пивоварня»",
    "emissionDate": "2026-10-01T09:00:00.000Z",
    "introducedDate": "2026-10-03T09:00:00.000Z",
    "productionDate": "2026-10-02T00:00:00.000Z",
    "expirationDate": "2027-01-31T00:00:00.000Z",
    "statusEx": "CONNECT_TAP",
    "connectDate": "2026-10-28T09:00:00.000Z",
    "partialSaleInfo": {
      "innerUnitCount": 30000,
      "soldUnitCount": 12500,
      "rest": 17500
    }
  }
}

Живой запрос кода не на учёте:

curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt "https://localhost:8443/api/v1/kegs/010460000000001021FoR0001?client=%D0%9E%D0%9E%D0%9E%20%C2%AB%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%C2%BB"
{
  "cis": "010460000000001021FoR0001",
  "client": "ООО «Пример»",
  "tracked": false,
  "last_info": {
    "cis": "010460000000001021FoR0001",
    "requestedCis": "010460000000001021FoR0001",
    "gtin": "04600000000010",
    "productName": "Пиво светлое фильтрованное, кега 30 л",
    "status": "INTRODUCED",
    "ownerInn": "0100000009",
    "ownerName": "ООО «Другая организация»",
    "productGroup": "beer",
    "packageType": "UNIT",
    "producerInn": "0100000008",
    "producerName": "ООО «Пивоварня»",
    "emissionDate": "2026-10-01T09:00:00.000Z",
    "introducedDate": "2026-10-03T09:00:00.000Z",
    "productionDate": "2026-10-02T00:00:00.000Z",
    "expirationDate": "2027-01-31T00:00:00.000Z",
    "statusEx": "CONNECT_TAP",
    "connectDate": "2026-10-20T09:00:00.000Z",
    "partialSaleInfo": {
      "innerUnitCount": 30000,
      "soldUnitCount": 1000,
      "rest": 29000
    }
  }
}

Ошибки

HTTP error Условие
400 validation_failed Код не распознан; refresh не true/false/1/0
403 forbidden / license_denied client недоступен ключу / нет права организации для запроса в ГИС МТ
404 not_found Не на учёте без client; организация не настроена; ГИС МТ ответила на весь запрос HTTP 404
502 gis_error Ошибка ГИС МТ или токена при запросе
503 signing_failed Ошибка подписи при получении токена

Если ГИС МТ на неизвестный код отвечает элементом с cisInfo и ошибкой «не найден» (так этот ответ смоделирован в тестах программы и на стенде), ответ — 200 с почти пустым last_info (только requestedCis, cis), а не 404: признак «не найден» теряется (11.3, Д-3). Если элемент приходит без cisInfo, ответ — 502 gis_error с текстом ГИС МТ. Какую форму возвращает рабочая ГИС МТ, в этой проверке не установлено. Проверяйте наличие status в last_info или используйте /codes/check.

{
  "cis": "010460000000001021ZzZ9999",
  "client": "ООО «Пример»",
  "tracked": false,
  "last_info": {
    "requestedCis": "010460000000001021ZzZ9999",
    "cis": "010460000000001021ZzZ9999"
  }
}
{
  "error": "validation_failed",
  "message": "Некорректный код маркировки: «12345» (проверьте раскладку клавиатуры сканера: латиница, без CapsLock)"
}

7.9. GET /kegs/rests/fias — остатки разливного по адресам ФИАС

Назначение Остаток разливной продукции на кранах по адресам: адрес → товар → кеги, литры и итоги (для учётной системы, отчёта ЕГАИС, витрины)
Вызов GET /api/v1/kegs/rests/fias[?fias_id=<GUID>\|none][&client=<имя>][&details=false]
Доступ Роль retail; кеги организаций, доступных ключу; лицензия не проверяется
Последствия Нет; расчёт по учёту программы
Повтор Безопасен
Проверка Код; стенд — rests, rests-fias, rests-none, rests-bad

Параметры

Имя Тип По умолчанию Смысл
fias_id GUID или none все адреса Один адрес (GUID в любой записи .NET, регистр не важен) или none — только кеги без известного адреса
client строка все доступные Одна организация (нужен доступ)
details true/false/1/0 true false — без списка кег (items)

Учитываются только кеги со статусом подключения CONNECT_TAP. Адрес известен для кег, подключённых через программу с ФИАС (это ИП); у юрлиц адрес не передаётся, их кеги — в группе fias_id: null.

Ответ 200

Поле Тип Смысл
generated_at дата-время, +03:00 Момент расчёта по Москве
totals.kegs целое Число кег
totals.rest_liters, totals.volume_liters число, л Сумма известных остатков (отрицательный — 0) и объёмов
addresses[] массив Адреса: сначала с ФИАС по возрастанию, последним — null
addresses[].fias_id строка | null (пишется всегда) ФИАС в нижнем регистре; null — адрес неизвестен
addresses[].clients массив строк Организации с кегами по адресу (по алфавиту)
addresses[].kegs, rest_liters, volume_liters Итоги адреса
addresses[].products[] массив Товары по алфавиту: gtin, product_name, kegs, rest_liters, volume_liters, rest_unknown и volume_unknown (кег без известного остатка/объёма — в литры не входят), items
…items[] массив; нет при details=false Кеги по cis: cis, client, kpp, rest_liters и volume_liters (null пишется явно, если неизвестно), connect_date, expiration_date, updated_at
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt https://localhost:8443/api/v1/kegs/rests/fias
{
  "generated_at": "2026-11-01T12:00:00+03:00",
  "totals": {
    "kegs": 4,
    "rest_liters": 87.5,
    "volume_liters": 110
  },
  "addresses": [
    {
      "fias_id": "00000000-0000-4000-8000-000000000001",
      "clients": [
        "ИП Образцов О. О."
      ],
      "kegs": 2,
      "rest_liters": 40,
      "volume_liters": 50,
      "products": [
        {
          "gtin": "04600000000027",
          "product_name": "Пиво тёмное, кега 50 л",
          "kegs": 1,
          "rest_liters": 40,
          "volume_liters": 50,
          "rest_unknown": 0,
          "volume_unknown": 0,
          "items": [
            {
              "cis": "010460000000002721Ip00001",
              "client": "ИП Образцов О. О.",
              "rest_liters": 40,
              "volume_liters": 50,
              "connect_date": "2026-10-25",
              "expiration_date": "2027-01-31",
              "updated_at": "2026-11-01T09:00:00+00:00"
            }
          ]
        },
        {
          "gtin": "04600000000034",
          "product_name": "Сидр, кега 20 л",
          "kegs": 1,
          "rest_liters": 0,
          "volume_liters": 0,
          "rest_unknown": 1,
          "volume_unknown": 1,
          "items": [
            {
              "cis": "010460000000003421Ip00002",
              "client": "ИП Образцов О. О.",
              "rest_liters": null,
              "volume_liters": null,
              "connect_date": "2026-10-29",
              "expiration_date": "2027-01-31",
              "updated_at": "2026-11-01T09:00:00+00:00"
            }
          ]
        }
      ]
    },
    {
      "fias_id": null,
      "clients": [
        "ООО «Пример»"
      ],
      "kegs": 2,
      "rest_liters": 47.5,
      "volume_liters": 60,
      "products": [
        {
          "gtin": "04600000000010",
          "product_name": "Пиво светлое фильтрованное, кега 30 л",
          "kegs": 2,
          "rest_liters": 47.5,
          "volume_liters": 60,
          "rest_unknown": 0,
          "volume_unknown": 0,
          "items": [
            {
              "cis": "010460000000001021AbC1234",
              "client": "ООО «Пример»",
              "kpp": "010001001",
              "rest_liters": 17.5,
              "volume_liters": 30,
              "connect_date": "2026-10-28",
              "expiration_date": "2026-12-31",
              "updated_at": "2026-11-01T09:00:00+00:00"
            },
            {
              "cis": "010460000000001021AbC1235",
              "client": "ООО «Пример»",
              "rest_liters": 30,
              "volume_liters": 30,
              "connect_date": "2026-10-30",
              "expiration_date": "2027-01-31",
              "updated_at": "2026-11-01T09:00:00+00:00"
            }
          ]
        }
      ]
    }
  ]
}

Без детализации, только кеги без адреса:

{
  "generated_at": "2026-11-01T12:00:00+03:00",
  "totals": {
    "kegs": 2,
    "rest_liters": 47.5,
    "volume_liters": 60
  },
  "addresses": [
    {
      "fias_id": null,
      "clients": [
        "ООО «Пример»"
      ],
      "kegs": 2,
      "rest_liters": 47.5,
      "volume_liters": 60,
      "products": [
        {
          "gtin": "04600000000010",
          "product_name": "Пиво светлое фильтрованное, кега 30 л",
          "kegs": 2,
          "rest_liters": 47.5,
          "volume_liters": 60,
          "rest_unknown": 0,
          "volume_unknown": 0
        }
      ]
    }
  ]
}

Ошибки: 400 — fias_id не GUID и не none, details не логическое; 403 — client недоступен ключу.

{
  "error": "validation_failed",
  "message": "Параметр fias_id должен быть GUID ФИАС или none"
}

7.10. POST /kegs/track — поставить кеги на учёт

Назначение Поставить на учёт программы кеги, которые уже подключены к оборудованию в ГИС МТ (подключены в другой программе или в личном кабинете), — чтобы видеть их остатки
Вызов POST /api/v1/kegs/track, тело JSON
Доступ Роль panel; доступ к client; лицензия организации (отказ — внутри ответа 200, Д-1)
Последствия Только учёт программы (источник api); в ГИС МТ ничего не отправляется. Панель (если включена) получает остатки
Повтор Идемпотентен по результату: повтор вернёт already_tracked для уже поставленных
Асинхронность Нет; сведения запрашиваются в ГИС МТ пачками по 1000 кодов
Проверка Код; стенд — track*

Параметры (тело)

Поле Тип Обязательно Смысл
client строка да Организация учёта
codes массив, ≥ 1 да Коды; явного верхнего предела нет, ограничен размером тела
codes[].cis строка да Код кеги (4.4)
codes[].expiration_date дата нет Срок годности; нет — заводской из ГИС МТ

Ответ 200 — массив результатов в порядке codes:

Поле Тип Смысл
input строка Код как передан
cis строка, может отсутствовать Нормализованный КИ; нет — код не распознан
outcome строка added — поставлена; already_tracked — уже на учёте (у любой организации) или повтор в списке; not_connected — по ГИС МТ код не подключён к оборудованию; invalid — код не распознан; failed — нет ответа ГИС МТ по коду, ошибка по коду или не получен токен / нет права организации
message строка Пояснение
{
  "client": "ООО «Пример»",
  "codes": [
    {
      "cis": "010460000000001021RcV0001",
      "expiration_date": "2027-01-15"
    },
    {
      "cis": "010460000000001021InT0001"
    },
    {
      "cis": "12345"
    },
    {
      "cis": "010460000000001021RcV0001"
    },
    {
      "cis": "010460000000001021AbC1234"
    }
  ]
}
[
  {
    "input": "010460000000001021RcV0001",
    "cis": "010460000000001021RcV0001",
    "outcome": "added",
    "message": "Поставлен на учёт"
  },
  {
    "input": "010460000000001021InT0001",
    "cis": "010460000000001021InT0001",
    "outcome": "not_connected",
    "message": "КИ не подключён к оборудованию (статус INTRODUCED)"
  },
  {
    "input": "12345",
    "outcome": "invalid",
    "message": "Некорректный код маркировки: «12345» (проверьте раскладку клавиатуры сканера: латиница, без CapsLock)"
  },
  {
    "input": "010460000000001021RcV0001",
    "cis": "010460000000001021RcV0001",
    "outcome": "already_tracked",
    "message": "Повтор в списке"
  },
  {
    "input": "010460000000001021AbC1234",
    "cis": "010460000000001021AbC1234",
    "outcome": "already_tracked",
    "message": "Уже на учёте («ООО «Пример»»)"
  }
]

Ошибки запроса целиком: 400 — нет client, пустой codes, некорректный JSON; 403 — нет доступа к организации; 404 — организация не настроена; 502 gis_error — сбой запроса сведений в ГИС МТ. Отказ лицензии, ошибка токена и ошибка подписи не дают 4xx/5xx: все ещё не стоящие на учёте коды получают failed.

[
  {
    "input": "010460000000001021NeW0003",
    "cis": "010460000000001021NeW0003",
    "outcome": "failed",
    "message": "Нет действующего права организации (ЮЛ ИНН 0100000003): нет пробного периода или оплаты. Операция: запрос в ГИС МТ / ЭДО Лайт."
  }
]

7.11. DELETE /kegs/{cis} — снять кегу с учёта

Назначение Убрать кегу из учёта программы (закончилась, списана, ошибочно поставлена)
Вызов DELETE /api/v1/kegs/{cis}, {cis} URL-кодирован
Доступ Роль panel; кега организации, недоступной ключу, — 404. Лицензия не проверяется
Последствия Только учёт программы: запись переносится в архив с причиной manual_api; в ГИС МТ ничего не отправляется, статус кода не меняется. Панель (если включена) получает остатки
Повтор Повтор после успеха — 404; считайте его подтверждением, что кеги на учёте нет
Проверка Код; стенд — kegs-delete, kegs-delete-again
curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt -X DELETE https://localhost:8443/api/v1/kegs/010460000000001021AbC1235
{
  "cis": "010460000000001021AbC1235",
  "removed": true
}

Ошибки: 400 — код не распознан; 404 — не на учёте.

{
  "error": "not_found",
  "message": "КИ 010460000000001021AbC1235 не на учёте"
}

7.12. GET /codes/check — проверка кода маркировки

Назначение Сведения ГИС МТ о любом коде (свой или чужой, статус, товар, даты, для кеги — подключение и остаток, для упаковки — вложенные коды) и оценка «можно ли продать» от имени организации
Вызов GET /api/v1/codes/check?cis=<код>[&client=<имя>][&raw=true]
Доступ Роль retail; лицензия организации проверки; токен ГИС МТ
Последствия Только запросы в ГИС МТ (и в разрешительный режим, если в профиле есть его ключ). Ничего не меняет
Повтор Безопасен
Проверка Код; стенд — codes-* (без разрешительного режима: на стенде его ключа нет, поэтому sale_source = gis_info)

Параметры

Имя Тип Обязательно Смысл
cis строка да Код, URL-кодированный (4.4)
client строка нет Организация, от имени которой проверяется код (и чей ключ разрешительного режима используется). Нет — первая включённая организация, доступная ключу (с действующим токеном, если такая есть). Указанная выключенная — 404
raw true/false/1/0 нет, false Добавить полный cisInfo (raw) и ответ разрешительного режима (permissive_raw)

Если код принадлежит другой нашей организации, доступной ключу, он дополнительно запрашивается её токеном (часть сведений ГИС МТ отдаёт только владельцу); checked_as — чей ответ использован.

Ответ 200 (код не найден — тоже 200):

Поле Тип Смысл
input строка Код как передан
cis строка | null (пишется всегда) Код, отправленный в ГИС МТ; null — код некорректен
found логическое Найден в ГИС МТ
outcome строка ours — владелец одна из организаций программы; foreign — чужой; not_found; error — ошибка ГИС МТ по коду (error); invalid — некорректный код
error строка Текст ошибки (для not_found, error, invalid)
owner_inn, owner_name строки Владелец
ours логическое Владелец — наша организация
our_client строка Наша организация-владелец (только если доступна ключу)
status, status_ex, status_text строки Статус ГИС МТ и его текст по-русски
product_group, product_group_name строки Товарная группа
package_type строка UNIT, GROUP, SET, BUNDLE, BOX, ATK, LEVEL1…
gtin, product_name, producer_inn, producer_name строки Товар и производитель
emission_date, introduced_date, production_date, expiration_date, connect_date даты (по Москве) Даты кода
volume_liters, rest_liters числа, л Объём и остаток (кеги)
children_count, children, parent целое, массив, строка Агрегат: число и список вложенных кодов; код упаковки-родителя
tracked логическое Код на учёте программы у организации, доступной ключу
checked_as строка Организация, чьим токеном получен ответ
raw объект Полный cisInfo при raw=true
sale_allowed true/false/null (пишется всегда) Можно ли продать: null — не оценивалось
sale_status строка allowed, forbidden, unknown
sale_reasons массив строк Причины запрета по-русски ([] — нет)
sale_source строка permissive_mode — разрешительный режим ГИС МТ; gis_info — оценка по сведениям cises/info (нет ключа или режим недоступен); нет — не проверялось
sale_note строка Пометки: почему не разрешительный режим, «код отправлен без криптохвоста — криптоподпись не проверялась» и т. п.
permissive_raw объект Ответ разрешительного режима при raw=true

Оценка по gis_info — резервная: она не проверяет криптоподпись и не равна ответу разрешительного режима. Для продажи в розницу настройте ключ разрешительного режима в профиле организации.

curl.exe --insecure --pinnedpubkey "sha256//<pin>" -H @mge-api-key.txt "https://localhost:8443/api/v1/codes/check?cis=010460000000001021AbC1234&client=%D0%9E%D0%9E%D0%9E%20%C2%AB%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%C2%BB"
{
  "input": "010460000000001021AbC1234",
  "cis": "010460000000001021AbC1234",
  "found": true,
  "outcome": "ours",
  "owner_inn": "0100000001",
  "owner_name": "ООО «Пример»",
  "ours": true,
  "our_client": "ООО «Пример»",
  "status": "INTRODUCED",
  "status_ex": "CONNECT_TAP",
  "status_text": "В обороте · Подключена к оборудованию",
  "product_group": "beer",
  "product_group_name": "Пиво и слабоалкогольные напитки",
  "package_type": "UNIT",
  "gtin": "04600000000010",
  "product_name": "Пиво светлое фильтрованное, кега 30 л",
  "producer_inn": "0100000008",
  "producer_name": "ООО «Пивоварня»",
  "emission_date": "2026-10-01",
  "introduced_date": "2026-10-03",
  "production_date": "2026-10-02",
  "expiration_date": "2027-01-31",
  "connect_date": "2026-10-28",
  "volume_liters": 30,
  "rest_liters": 17.5,
  "tracked": true,
  "checked_as": "ООО «Пример»",
  "sale_allowed": true,
  "sale_status": "allowed",
  "sale_reasons": [],
  "sale_source": "gis_info",
  "sale_note": "разрешительный режим не настроен"
}

Код с криптохвостом (GS — %1D), организация по умолчанию:

{
  "input": "0104600000000041215Kq7mT2\u001d93Qw7z",
  "cis": "0104600000000041215Kq7mT2",
  "found": true,
  "outcome": "foreign",
  "owner_inn": "0100000009",
  "owner_name": "ООО «Другая организация»",
  "ours": false,
  "status": "INTRODUCED",
  "status_text": "В обороте",
  "product_group": "beer",
  "product_group_name": "Пиво и слабоалкогольные напитки",
  "package_type": "UNIT",
  "gtin": "04600000000041",
  "product_name": "Пиво светлое, бутылка 0,5 л",
  "producer_inn": "0100000008",
  "producer_name": "ООО «Пивоварня»",
  "emission_date": "2026-09-01",
  "introduced_date": "2026-09-03",
  "production_date": "2026-09-02",
  "expiration_date": "2027-03-01",
  "tracked": false,
  "checked_as": "ООО «Пример»",
  "sale_allowed": false,
  "sale_status": "forbidden",
  "sale_reasons": [
    "владелец кода — другая организация (ООО «Другая организация»)"
  ],
  "sale_source": "gis_info",
  "sale_note": "разрешительный режим не настроен"
}

Ошибки

HTTP error Условие
400 validation_failed Нет cis; код некорректен (в GET — ошибка, в POST — элемент invalid); raw не логическое
403 forbidden / license_denied client недоступен ключу / нет права организации
404 not_found Организация не настроена или выключена; ключу не доступна ни одна включённая организация
502 gis_error Ошибка ГИС МТ на весь запрос
503 token_unavailable / signing_failed Не получен токен организации / ошибка подписи при получении токена
{
  "error": "token_unavailable",
  "message": "Не удалось получить токен ГИС МТ «ООО «Нет связи»»: ГИС МТ: сервис временно недоступен (HTTP 503)"
}

7.13. POST /codes/check — проверка списка кодов

Назначение То же, что 7.12, для списка до 1000 кодов за один запрос
Вызов POST /api/v1/codes/check, тело JSON
Доступ, последствия, повтор Как у GET
Проверка Код; стенд — codes-post, codes-post-empty
Поле Тип Обязательно Смысл
codes массив строк, 1–1000 да Коды в любом виде; GS — символ \u001d в JSON
client строка нет Как в GET
raw логическое нет, false Как в GET

Ответ 200 — массив объектов из 7.12 в порядке codes. Некорректный код — элемент с outcome: "invalid", cis: null и error (не 400 на весь запрос).

{
  "client": "ООО «Пример»",
  "codes": [
    "010460000000001021AbC1234",
    "0104600000000041215Kq7mT2\u001d93Qw7z",
    "12345",
    "046000000000000019"
  ]
}
[
  {
    "input": "010460000000001021AbC1234",
    "cis": "010460000000001021AbC1234",
    "found": true,
    "outcome": "ours",
    "owner_inn": "0100000001",
    "owner_name": "ООО «Пример»",
    "ours": true,
    "our_client": "ООО «Пример»",
    "status": "INTRODUCED",
    "status_ex": "CONNECT_TAP",
    "status_text": "В обороте · Подключена к оборудованию",
    "product_group": "beer",
    "product_group_name": "Пиво и слабоалкогольные напитки",
    "package_type": "UNIT",
    "gtin": "04600000000010",
    "product_name": "Пиво светлое фильтрованное, кега 30 л",
    "producer_inn": "0100000008",
    "producer_name": "ООО «Пивоварня»",
    "emission_date": "2026-10-01",
    "introduced_date": "2026-10-03",
    "production_date": "2026-10-02",
    "expiration_date": "2027-01-31",
    "connect_date": "2026-10-28",
    "volume_liters": 30,
    "rest_liters": 17.5,
    "tracked": true,
    "checked_as": "ООО «Пример»",
    "sale_allowed": true,
    "sale_status": "allowed",
    "sale_reasons": [],
    "sale_source": "gis_info",
    "sale_note": "разрешительный режим не настроен"
  },
  {
    "input": "0104600000000041215Kq7mT2\u001d93Qw7z",
    "cis": "0104600000000041215Kq7mT2",
    "found": true,
    "outcome": "foreign",
    "owner_inn": "0100000009",
    "owner_name": "ООО «Другая организация»",
    "ours": false,
    "status": "INTRODUCED",
    "status_text": "В обороте",
    "product_group": "beer",
    "product_group_name": "Пиво и слабоалкогольные напитки",
    "package_type": "UNIT",
    "gtin": "04600000000041",
    "product_name": "Пиво светлое, бутылка 0,5 л",
    "producer_inn": "0100000008",
    "producer_name": "ООО «Пивоварня»",
    "emission_date": "2026-09-01",
    "introduced_date": "2026-09-03",
    "production_date": "2026-09-02",
    "expiration_date": "2027-03-01",
    "tracked": false,
    "checked_as": "ООО «Пример»",
    "sale_allowed": false,
    "sale_status": "forbidden",
    "sale_reasons": [
      "владелец кода — другая организация (ООО «Другая организация»)"
    ],
    "sale_source": "gis_info",
    "sale_note": "разрешительный режим не настроен"
  },
  {
    "input": "12345",
    "cis": null,
    "found": false,
    "outcome": "invalid",
    "error": "Некорректный код маркировки: «12345» (проверьте, что отсканирован код маркировки, а не штрихкод товара)",
    "ours": false,
    "tracked": false,
    "sale_allowed": null,
    "sale_status": "unknown",
    "sale_reasons": [],
    "sale_note": "некорректный код маркировки"
  },
  {
    "input": "046000000000000019",
    "cis": "046000000000000019",
    "found": false,
    "outcome": "not_found",
    "error": "КИ не найден",
    "ours": false,
    "tracked": false,
    "checked_as": "ООО «Пример»",
    "sale_allowed": null,
    "sale_status": "unknown",
    "sale_reasons": [],
    "sale_note": "транспортная упаковка (КИТУ) в розницу не продаётся — проверяются коды товаров"
  }
]

Ошибки: 400 — пустой список или больше 1000 кодов, некорректный JSON; остальные — как у GET.

{
  "error": "validation_failed",
  "message": "Не указано ни одного кода маркировки"
}

8. Практические сценарии

Готовые скрипты — api/examples: PowerShell (MarkGreenEngineApi.psm1, сценарии 01-…–03-…), 1С 8.3 (МаркГринЭнджинAPI.bsl), cURL, коллекция .http.

8.1. Первый запрос и выбор организации

  1. GET /health — программа доступна, версия 2.2.0 или новее.
  2. GET /tokens с ключом panel — список организаций; сохраните точное значение client нужной организации. Ключу retail этот метод недоступен: имя организации ему сообщает администратор (оно же — в GET /kegs, поле client).
  3. Дальше передавайте client во все методы. Имя меняется при переименовании профиля в программе — обрабатывайте 404 not_found «Профиль … не найден» как сигнал перечитать список.

8.2. Проверка кода перед продажей (касса, учётная система)

  1. Отсканированный код передайте как есть (с криптохвостом) в POST /codes/check с client — организацией-продавцом.
  2. sale_allowed: true — продажа разрешена по оценке программы; false — показать sale_reasons; null — оценки нет (sale_note).
  3. Проверьте sale_source: для решения о продаже нужен permissive_mode.
  4. Ответ 403 license_denied или 503 token_unavailable — программа не может проверить код. Решение о продаже в этом случае принимает ваша система по своим правилам; API не возвращает «разрешено» без проверки.

8.3. Подключить кегу и дождаться результата

  1. POST /kegs/connect → 202, doc_id, заголовок Location.
  2. Опрашивайте GET /documents/{doc_id} не чаще раза в 5 с первые 2 минуты, затем раз в минуту, пока не появится completed_at.
  3. CHECKED_OK — кега на учёте: GET /kegs/{cis} покажет source: "connect_tap", status_ex: "CONNECT_TAP".
  4. CHECKED_NOT_OK / PARSE_ERROR / PROCESSING_ERROR — причины в errors; исправьте и отправьте новый connect (повтор разрешён: код больше не «в обработке»).
  5. UNKNOWN — проверьте код живым запросом GET /kegs/{cis}?client=…: status_ex: "CONNECT_TAP" в last_info значит, что подключение прошло; тогда поставьте кегу на учёт POST /kegs/track.

8.4. Остатки для учётной системы

GET /kegs/rests/fias?details=false по расписанию (не чаще раза в минуту — данные обновляются программой раз в 15 минут) — итоги по адресам и товарам. Для сверки по кегам — details=true или GET /kegs.

8.5. Неверные параметры, нет права, нет связи

Ситуация Ответ Действие клиента
Ошибка в теле 400 validation_failed + details Показать details, не повторять без исправления
Нет права организации 403 license_denied Сообщить администратору (message), не повторять автоматически чаще раза в минуту
ГИС МТ недоступна 502 gis_error / 503 token_unavailable Повторить позже с паузой (1, 2, 5, 10 мин)
Программа выключена Нет соединения Повторить позже; при долгой недоступности — оповестить

9. Длительные операции и повторы

9.1. Общие правила

Метод Повтор после таймаута / обрыва
Все GET, HEAD Безопасен
POST /tokens/rotate Безопасен по данным, но каждый вызов — новый вход в ГИС МТ
POST /kegs/track Безопасен: уже поставленные вернутся already_tracked
DELETE /kegs/{cis} Безопасен: 404 после успеха означает «уже снята»
POST /codes/check Безопасен
POST /kegs/connect Только по процедуре 9.2

Ответы 429 содержат Retry-After — ждите не меньше. Для 502/503 используйте нарастающую паузу. Для 400, 401, 403, 404 повтор без изменения запроса бесполезен.

9.2. Ответ на POST /kegs/connect потерян

Ключа идемпотентности и метода «список документов» в 2.2.0 нет. Исход восстанавливается так:

  1. Повторите тот же запрос.
  2. 202 — первый запрос не дошёл до отправки; это первая отправка.
  3. 400, в details — «КИ …: уже отправлен в документе , ожидается результат»: первый документ отправлен; возьмите doc_id из текста и переходите к шагу 2 сценария 8.3.
  4. 400 «КИ …: уже подключён (на учёте у «…»)» — первый документ уже обработан успешно.
  5. Защита держится, только пока документ не окончательный. Если первый документ успел завершиться, повтор создаст новый подписанный документ:
  6. после CHECKED_NOT_OK, PARSE_ERROR, PROCESSING_ERROR, CANCELLED это допустимо — прежний не подключил кегу;
  7. после UNKNOWN исход прежнего документа не известен, и он мог пройти. Не повторяйте connect после UNKNOWN: сначала проверьте код живым запросом (сценарий 8.3, шаг 5);
  8. защита «уже подключён» действует, только пока кега стоит на учёте программы: после DELETE /kegs/{cis} или автоматического снятия повтор будет отправлен.

Защита работает внутри одной копии программы и не распространяется на другую копию или на подключение в личном кабинете ГИС МТ. Если программа аварийно завершилась ровно между отправкой и сохранением документа, повтор отправит документ ещё раз; ГИС МТ отклонит подключение уже подключённой кеги — результат увидите в errors.

10. Ошибки и устранение проблем

Ошибка Вид Причина Проверка Действие
Нет соединения Сеть Программа не запущена, API выключен, порт закрыт или занят Вкладка «API-журнал», netstat -ano \| findstr :8443 Включить API (2.1), открыть порт, сменить порт
Ошибка TLS / pin Сеть Сертификат пересоздан, отвечает другой сервер Окно «Сертификат и подключение API» Обновить pin у клиента
401 unauthorized Запрос Нет/неверный ключ, ключ < 24 символов, нет api_keys.json Журнал программы: ошибки разбора api_keys.json Исправить ключ
403 forbidden Права Адрес, роль или организация ключа message называет причину Настроить ключ в «Ключи API»
403 license_denied Лицензия Нет права организации или копии details, окно «Лицензия», кабинет 5.4
404 not_found «Метод API не найден» Запрос Опечатка в пути, неверный HTTP-метод, старый путь Каталог 6 Исправить запрос
404 not_found «Профиль … не найден» / «выключен» Данные Имя организации неверно или профиль выключен GET /tokens Исправить client
400 validation_failed Запрос Формат, обязательные поля, правила CONNECT_TAP details Исправить данные
413 payload_too_large / обрыв на большом теле Запрос Тело больше 8 МБ Размер запроса Делить на части (/codes/check — до 1000 кодов)
429 rate_limited Лимит Лимит ключа или блокировка адреса Retry-After Снизить частоту; исправить неверный ключ в другом клиенте с того же адреса
502 gis_error Внешняя система ГИС МТ недоступна или отклонила запрос message Повторить позже; для документов — проверить данные
503 token_unavailable Внешняя система Токен не получен GET /tokens → last_error Повторить позже
503 signing_failed Криптография Сертификат, ключ, PIN, носитель, КриптоПро Журнал программы, профиль организации Вставить носитель, ввести PIN в программе, обновить сертификат
500 internal_error Программа Ошибка программы Журнал logs/… Сообщить в поддержку с временем запроса

11. Совместимость, ограничения и найденные расхождения

11.1. Ограничения текущей реализации

11.2. Исправления относительно прежней документации

Прежняя редакция этого файла, комментарии api_keys.example.json и страница сайта /docs/api расходились с кодом 2.2.0:

Было В 2.2.0
Роль retail — «только GET /kegs и GET /kegs/{cis}» (docs/api.md, комментарий в api_keys.example.json) retail также GET /kegs/rests/fias, GET/POST /codes/check
GET /kegs/rests/fias отсутствовал в справочнике docs/api.md Добавлен (7.9)
Нет license_denied Добавлен (5.4)
«Живой запрос, ГИС МТ вернула “код не найден” → 404» 404 только при HTTP 404 на весь запрос; обычно — 200 с пустым last_info (Д-3)
«Ошибки ГИС МТ по коду в /kegs/track → failed, недоступность ГИС МТ → 502/503» Ошибка токена и отказ лицензии → failed у всех кодов, 200 (Д-1)
Сайт /docs/api: «полный справочник — README.txt, раздел “Входящий API”» В README.txt только /codes/check и /kegs/rests/fias; полный справочник — этот документ
API включён по умолчанию В дистрибутиве 2.2.0 выключен

11.3. Расхождения реализации, требующие отдельной задачи

Поведение API в рамках этой задачи не менялось; ниже — что найдено и как учитывать до исправления.

№ Где Что Влияние на интегратора
Д-1 KegService.TrackAsync (src/GE.TrueApi.Core/Kegs/KegService.cs, строки 172–181) Любая ошибка получения токена, включая LicenseDeniedException, превращается в outcome: "failed" для всех кодов, ответ 200. LicenseEndpointFilter эту ошибку не видит Нельзя отличить «нет права» от сбоя ГИС МТ по коду ответа — только по message. Обход оплаты нет: в ГИС МТ ничего не запрашивается
Д-2 Там же, строка 164 already_tracked для кега, стоящей на учёте у другой организации, содержит её имя: «Уже на учёте («…»)» — даже если ключу эта организация недоступна. Остальные методы такие кеги скрывают (404) Ключ panel с ограниченным clients узнаёт имя чужой организации и факт учёта кода (стенд track-other-org)
Д-3 KegService.FetchInfoAsync (строки 389–399) и KegEndpoints.Get Если ГИС МТ возвращает по коду cisInfo с ошибкой «не найден» (форма из тестов программы; для рабочей ГИС МТ не подтверждена), ошибка игнорируется: ответ 200 с last_info из одного requestedCis/cis. Для кеги на учёте при refresh=true поля кеги сохраняются, но last_info заменяется этим пустым ответом, а updated_at обновляется Проверяйте last_info.status; для поиска кода используйте /codes/check
Д-4 Дистрибутив: api_keys.example.json, docs/api.md до этой редакции, страница сайта /docs/api Устаревшее описание роли retail и места полного справочника (11.2) Документация исправлена в этом комплекте; пример ключей в дистрибутиве — при следующей сборке
Д-5 TrackRequestDto / ConnectRequestDto (JSON) camelCase-поля (connectDate) молча игнорируются, подставляется значение по умолчанию; для connect это означает документ с датой «сегодня» Используйте только snake_case; проверяйте codes в ответе 202

12. Как проверялся этот документ

Собрано из docs/api.md скриптом docs/api/tools/build_html.py.