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).
Уровни проверки, которые указаны в карточках:
- код — поведение установлено по исходному коду сборки 78ca5a8;
- стенд — запрос выполнен на настоящих сборках API выпуска 2.2.0 (
GE.TrueApi.Server.dll,GE.TrueApi.Core.dllиз ZIP выпуска, хэши совпали с его MANIFEST): Kestrel, сертификат,api_keys.json, роли, лимиты, менеджер токенов, учёт кег, документы CONNECT_TAP и проверка КМ — настоящие; ГИС МТ, КриптоПро, панель и центр лицензирования заменены синтетическими; - не проверено — обмен с настоящей ГИС МТ, подписью КриптоПро и центром лицензирования в рамках этого документа не выполнялся.
Оглавление
- Карта взаимодействий
- Назначение и совместимость
- Подготовка программы
- Быстрый старт
- Общий контракт
- Авторизация, роли, организации и лицензия
- Каталог методов
- Карточки методов
- Практические сценарии
- Длительные операции и повторы
- Ошибки и устранение проблем
- Совместимость, ограничения и найденные расхождения
- Как проверялся этот документ
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 внешняя система может:
- узнать состояние и получить действующий токен ГИС МТ организации (для собственных запросов в True API);
- подключить кеги пива к оборудованию для розлива (документ CONNECT_TAP) и отследить результат обработки;
- вести учёт кег на кранах: список, одна кега, постановка на учёт и снятие, остатки разливного по адресам ФИАС;
- проверить коды маркировки — сведения ГИС МТ и оценку «можно ли продать».
| Компонент | Требование |
|---|---|
| Программа | 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 }.
Включите одним из способов:
- В программе: «⋯» → «Настройки» → «Входящий API» → «Принимать запросы от панели и магазинов», сохранить, перезапустить программу (настройки раздела действуют после перезапуска, в окне они помечены знаком ⟳).
- Или в файле
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 открытого ключа —
sha256//<base64>; это форматcurl --pinnedpubkeyиCURLOPT_PINNEDPUBLICKEY; - либо отпечаток SHA-256 сертификата —
AB:CD:…; - либо сертификат, сохранённый как доверенный файл (для 1С, examples/1c).
Pin, отпечаток, адрес API, команда брандмауэра и готовая команда curl показаны в окне «⋯» → «Сертификат и подключение API…»
(также кнопка на вкладке «API-журнал»).
- Если при запуске API (запуск программы или её перезапуск) до окончания срока сертификата меньше 30 дней, программа продлевает его с тем же ключом: pin не меняется, отпечаток меняется. Во время работы срок не проверяется — если программа работает без перезапуска, сертификат может истечь. Клиенты, закрепившие отпечаток или сохранившие файл сертификата (1С), после продления должны их обновить; закрепление по pin продление переживает.
ExtraHostNamesзаписываются в сертификат только при его создании. Если вы добавили имя позже, удалитеserver.pfxиserver.pfx.keyи перезапустите программу. Будет создан новый ключ, pin изменится — обновите его у всех клиентов.- Свой сертификат (
AutoCertificate = false, свойPfxPath/PfxPassword) программа не продлевает, только предупреждает в журнале.
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
- Имена полей —
snake_case(client,connect_date,uuid_token). - Имена полей в теле запроса сверяются без учёта регистра, но camelCase не распознаётся:
connectDateвместоconnect_dateмолча игнорируется, и программа подставляет значение по умолчанию. Неизвестные поля игнорируются без ошибки (сценарийconnect-camelcaseв отчёте проверки). - Поле со значением
nullв ответе не выводится — кроме полей, где это оговорено в карточке (cisв/codes/check,sale_allowed,fias_idи литры в/kegs/rests/fias). Отсутствие поля иnullв ответе означают одно и то же — «значения нет». - Перечисления (
status,outcome,sale_status…) — строки вsnake_case. Числа вместо строк не принимаются. - ИНН, КПП, GTIN, коды маркировки, идентификаторы документов — всегда строки. Не преобразуйте их в числа: потеряете ведущие нули
(
"010000000001"). - Кириллица в ответах не экранируется. Управляющие символы экранируются: символ GS в коде маркировки приходит как
\u001D. - Ответ на некорректный JSON:
400 validation_failed,message«Некорректный JSON в теле запроса»,details— путь к полю («Некорректное значение поля $.codes[0].connect_date») или «Тело запроса не является корректным 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, то есть печатные символы, кроме {, |, }, ~) и отбрасывают криптохвост. Принимаются:
- префиксы сканера
]C1,]d2,]Q3, ведущий символ FNC1/GS; - разделитель GS как символ
\u001d(в JSON), или текстом<GS>,\x1d,\u001d; - скобочная запись
(01)…(21)…(91)…(92)…; - код кеги пива без разделителя — ровно 31 символ, где после КИ (25 символов) идут «93» и 4 символа проверки.
/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 «уже отправлен в документе |
| Асинхронность | Ответ 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 |
строка | для кода не на учёте | От имени какой организации запросить код в ГИС МТ |
Логика ответа:
- Код на учёте у организации, доступной ключу → объект «кега» (7.7) плюс
last_info— последний ответ ГИС МТcisInfoбез изменений (поля и регистр — как у ГИС МТ, напримерrequestedCis,partialSaleInfo).clientигнорируется. - Код не на учёте и нет
client→404«КИ не на учёте; укажите client для запроса в ГИС МТ». - Код не на учёте, есть
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. Первый запрос и выбор организации
GET /health— программа доступна, версия 2.2.0 или новее.GET /tokensс ключомpanel— список организаций; сохраните точное значениеclientнужной организации. Ключуretailэтот метод недоступен: имя организации ему сообщает администратор (оно же — вGET /kegs, полеclient).- Дальше передавайте
clientво все методы. Имя меняется при переименовании профиля в программе — обрабатывайте404 not_found«Профиль … не найден» как сигнал перечитать список.
8.2. Проверка кода перед продажей (касса, учётная система)
- Отсканированный код передайте как есть (с криптохвостом) в
POST /codes/checkсclient— организацией-продавцом. sale_allowed: true— продажа разрешена по оценке программы;false— показатьsale_reasons;null— оценки нет (sale_note).- Проверьте
sale_source: для решения о продаже нуженpermissive_mode. - Ответ
403 license_deniedили503 token_unavailable— программа не может проверить код. Решение о продаже в этом случае принимает ваша система по своим правилам; API не возвращает «разрешено» без проверки.
8.3. Подключить кегу и дождаться результата
POST /kegs/connect→202,doc_id, заголовокLocation.- Опрашивайте
GET /documents/{doc_id}не чаще раза в 5 с первые 2 минуты, затем раз в минуту, пока не появитсяcompleted_at. CHECKED_OK— кега на учёте:GET /kegs/{cis}покажетsource: "connect_tap",status_ex: "CONNECT_TAP".CHECKED_NOT_OK/PARSE_ERROR/PROCESSING_ERROR— причины вerrors; исправьте и отправьте новыйconnect(повтор разрешён: код больше не «в обработке»).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 нет. Исход восстанавливается так:
- Повторите тот же запрос.
202— первый запрос не дошёл до отправки; это первая отправка.400, вdetails— «КИ …: уже отправлен в документе, ожидается результат»: первый документ отправлен; возьмите doc_idиз текста и переходите к шагу 2 сценария 8.3.400«КИ …: уже подключён (на учёте у «…»)» — первый документ уже обработан успешно.- Защита держится, только пока документ не окончательный. Если первый документ успел завершиться, повтор создаст новый подписанный документ:
- после
CHECKED_NOT_OK,PARSE_ERROR,PROCESSING_ERROR,CANCELLEDэто допустимо — прежний не подключил кегу; - после
UNKNOWNисход прежнего документа не известен, и он мог пройти. Не повторяйтеconnectпослеUNKNOWN: сначала проверьте код живым запросом (сценарий 8.3, шаг 5); - защита «уже подключён» действует, только пока кега стоит на учёте программы: после
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. Ограничения текущей реализации
- API работает только пока запущена программа на этом компьютере; кластеризации и очереди запросов нет.
- Нет ключа идемпотентности и списка документов CONNECT_TAP (9.2).
- Нет пагинации и фильтров в
GET /kegsиGET /tokens. - Нет методов ЭДО, ЕГАИС, чеков, управления профилями и ключами.
HEADподдерживается только у/health; неподдерживаемый метод —404, не405.- Секрет передаётся только заголовком
X-Api-Key; передача ключа в URL не поддерживается (и не нужна). - Ответ
conflict(409) объявлен, но не используется.
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. Как проверялся этот документ
- Инвентаризация — по регистрации маршрутов
ApiApp.MapEndpoints,HealthEndpoints,TokenEndpoints,KegEndpoints,CodeEndpoints(commit78ca5a8): 14 регистраций (13 методов +HEAD /health) и обработчик неизвестных путей. Скриптapi/tools/check_docs.pyсверяет реестр с кодом, OpenAPI и этим документом. - Стенд —
tools/GE.TrueApi.ApiDocStand: настоящиеGE.TrueApi.Server.dll(SHA-25640d3120d…adf35) иGE.TrueApi.Core.dll(c02f531b…dbe1) из ZIP выпуска 2.2.0 — те же, что в MANIFEST; часы остановлены на 2026-11-01T09:00:00Z. 75 сценариевapi/tools/run_cases.py, все прошли; ответы —api/verification/stand-cases.json. Все JSON-примеры этого документа подставлены из этого файла и сверяются с ним. - Примеры
api/tools/verify_examples.pyна том же стенде: коллекция.http(14 запросов),examples.cmd(curl.exe Windows 8.13.0 и curl 8.18.0),examples.sh, сценарии PowerShell 5.1 — без ошибок; протоколapi/verification/examples-run.txt. - Существующие тесты
tests/GE.TrueApi.Server.Testsна87eed57: 228 из 228. - Не проверено: обмен с настоящей ГИС МТ и разрешительным режимом, подпись КриптоПро, центр лицензирования, работа по сети между компьютерами, примеры 1С на платформе 1С (платформы нет в среде проверки). Подробно — api/verification-report.md.