Урок 2 из 4 · 35 мин

API ТГИД: v1, v2 для внешних систем и вебхуки

У ТГИД три двери для программ. API v1 — адреса самого веба, описаны в /openapi.json. API v2 — стабильные адреса для ERP, CRM, 1С и шлюзов SCADA с ключом scope: api. Вебхуки — ТГИД сам отправляет события на адрес вашей системы и подписывает их по Standard Webhooks. Урок — о том, какую дверь выбрать, как устроены ключи и почему подпись проверяют именно так.

API ТГИД: v1, v2 для внешних систем и вебхуки

Кратко. У ТГИД три двери для программ. API v1 — адреса самого веба, описаны в /openapi.json. API v2 — стабильные адреса для ERP, CRM, 1С и шлюзов SCADA с ключом scope: api. Вебхуки — ТГИД сам отправляет события на адрес вашей системы и подписывает их по Standard Webhooks. Урок — о том, какую дверь выбрать, как устроены ключи и почему подпись проверяют именно так.

Зачем это

Внешняя система должна узнавать о новых дефектах, нарядах и отключениях и создавать заявки, не ломаясь при каждом выпуске веба и не открывая лишнего. Неверный выбор дорого обходится: интеграция на адресах v1 сломается при обновлении, опрос без seq теряет события, приёмник без проверки подписи примет поддельный запрос.

Три двери

API v1 API v2 Вебхуки
Для кого веб ТГИД внешние системы внешние системы
Кто начинает клиент клиент (опрос) ТГИД (POST на ваш адрес)
Вход токен входа, 8 ч ключ scope: api, до 730 дн. секрет подписки whsec_…
Контракт меняется с вебом поля только добавляются состав событий в GET /api/v2
Описание /openapi.json, /docs /api/v2/openapi.json страница инструкции

Обычная связка: вебхук сообщает «случилось событие N», система дочитывает подробности через v2. Без вебхуков — опрос ленты /api/v2/events раз в минуту.

Токены и назначение

Все токены — JWT, подписанные JWT_SECRET. Различаются назначением (scope) и сроком:

Токен Где берут Срок по умолчанию Где действует
вход POST /api/v1/auth/login 480 мин везде: v1, v2, OGC, FeatureServer, /mcp
api личный кабинет 365 дн. только /api/v2/…
arcgis личный кабинет 90 дн. только FeatureServer
mcp личный кабинет 30 дн. только /mcp

Ключ с назначением на чужих адресах получает 401. Права у ключа — того, кто его выдал, и маскирование ПДн тоже по его праву pii_view. Отозвать один ключ нельзя: на сервере нет их списка, а ключ несёт отпечаток пароля. Рычаг отзыва — смена пароля или блокировка. Поэтому интеграции дают отдельного пользователя с минимальными правами.

API v1: что важно клиенту

  • У API нескольких городов город — префикс пути /city/<код>/…; за nginx его подставляет путь города.
  • Постраничная выдача в v1 своя у каждого семейства адресов (page/page_size, limit/offset, ссылка next). Слишком большой limit — 422 с границей.
  • Долгие операции отвечают {"task_id": …}; ход — GET /api/v1/task/{task_id} (PENDING → PROGRESS → SUCCESS или FAILURE).
  • Ошибки — {"detail": …}: строка, объект с code или список ошибок полей. Язык — по Accept-Language.

API v2: контракт

  • Списки по возрастанию id: limit до 500, ответ {"data": [...], "next_after_id": N}; следующая страница — after_id=N, null — конец. Фильтр updated_since — время ISO 8601 с поясом.
  • Лента событий — по номеру seq: ?after=<seq> → {data, next_after, has_more}. События хранятся 30 дней, поэтому последний seq храните у себя.
  • Заявка без дубля — POST /api/v2/work-orders с Idempotency-Key: повтор с тем же ключом вернёт тот же наряд (200, Idempotent-Replayed: true). Сеть может оборвать ответ, а не запрос — без ключа повтор создаст второй наряд.
  • Показания датчиков — пачкой до 10 000; неизвестные коды и время больше чем на сутки вперёд не пишутся, а перечисляются в ответе. Повтор того же времени заменяет значение — пачку можно безопасно отправить ещё раз.

Вебхуки: как устроена подпись

Каждое событие — POST с JSON без пробелов и заголовками webhook-id, webhook-timestamp, webhook-signature:

ключ     = base64_decode(секрет без «whsec_»)
подпись  = "v1," + base64( HMAC-SHA256(ключ, webhook-id + "." + webhook-timestamp + "." + тело) )

Почему правила проверки именно такие:

  • тело как пришло, до разбора JSON — пересборка меняет байты, и подпись не сойдётся;
  • время не старше 5 минут — иначе перехваченный запрос можно прислать повторно, и подпись будет верной;
  • сравнение постоянного времени — обычное сравнение строк по времени ответа выдаёт, сколько первых символов совпало;
  • несколько подписей через пробел — достаточно одной: так переживают смену секрета.

Доставка: ответ 2xx за 10 с. Иначе повторы через 1 мин, 5 мин, 30 мин, 2 ч, 5 ч, 10 ч, 10 ч (8 попыток, около суток), перенаправления не выполняются. Подписка, не принимающая ничего 3 дня, выключается сама. Один id может прийти дважды — получатель отбрасывает уже обработанные; порядок не гарантирован. Адрес — только https:// и публичный; внутренние адреса и http:// — только если разрешено на сервере.

Окно «Вебхуки»: подписки администратора

Как сделать в ТГИД

Задания

1. Какой ключ нужен

(а) 1С создаёт заявки на ремонт; (б) ГИС-отдел держит слой участков в QGIS постоянно; (в) инженер подключает Claude Desktop к данным сети; (г) портал акимата показывает карту сети посетителям.

Ответ

(а) Ключ API v2 (scope: api) отдельного пользователя с правом «Журналы». (б) Ключ ArcGIS для FeatureServer (90 дней); OGC API тоже подходит, но с токеном входа на 8 часов. (в) Токен MCP (scope: mcp). (г) Ключ встраивания tge_… от администратора — это не JWT, он открывает только встраиваемую карту (урок 32).

2. Прочитайте ответ и найдите ошибку

CRM выгружает наряды так и получает ответ (данные вымышленные):

GET /api/v2/work-orders?after_id=0&limit=1000
→ 422 {"detail":[{"type":"less_than_equal","loc":["query","limit"],"msg":"…","ctx":{"le":500}}]}

После исправления CRM читает первую страницу и останавливается, решив, что нарядов 500. В чём ошибки?

Ответ

limit в v2 не больше 500 — граница указана в ctx.le. Дальше: страница — не весь список. Нужно повторять запрос с after_id=<next_after_id>, пока next_after_id не станет null.

3. Проверьте подпись вебхука

Учебный секрет (вымышленный): whsec_Q09VUlNFLURFTU8tTk9ULUEtUkVBTC1TRUNSRVQtMDE=. Заголовки: webhook-id: evt_demo_101, webhook-timestamp: 1794204000 (09.11.2026 06:00:00 UTC), webhook-signature: v1,Stv+ivWJBlJZyApcTar+LthNrWCkahfXnE1Nlg/len4=. Тело:

{"id":"evt_demo_101","type":"defect.created","occurred_at":"2026-11-09T06:00:00Z","city":"demo","data":{"defect_id":502}}

Запрос пришёл в 06:12 UTC. Примет ли его функция verify из инструкции? Почему?

Ответ

Нет, по двум причинам. Первая: время. Запрос старше 5 минут (12 мин), verify отклонит его ещё до подсчёта подписи. Вторая: тело. Подпись Stv+…len4= посчитана для тела с "defect_id":501; для пришедшего тела с 502 верная подпись — v1,stE6BSSA5/xtXpaNkk+OCU6gkk2jPeb26anillB+Acs=. Тело изменили по дороге, и приёмник должен ответить отказом. Проверьте сами: функция verify со страницы Вебхуки: события и проверка подписи с параметром tolerance_s побольше.

4. Два наряда вместо одного

Call-центр создаёт заявку POST /api/v2/work-orders. Ответ не пришёл за 30 с, CRM повторила запрос — в ТГИД два одинаковых наряда. Как исправить на стороне CRM?

Ответ

Передавать Idempotency-Key, например номер обращения в CRM, и повторять с тем же ключом. ТГИД вернёт уже созданный наряд (200, Idempotent-Replayed: true). Тот же ключ с другим названием заявки — 422: ключ должен быть привязан к одному обращению.

Проверьте себя

Решено 0 из 7
1Чем API v2 отличается от v1 для внешней системы?
2Интеграция вызывает /api/v1/journals с ключом API v2 и получает 401. Почему?
3Как досрочно закрыть ключ API v2, если он утёк?
4Приёмник вебхуков проверяет подпись так: json.loads(тело) → json.dumps(...) → HMAC. Подпись не сходится никогда. Почему?
5Получатель вебхуков отвечает 302 на новый адрес. Что сделает ТГИД?
6Зачем в теле события вебхука только номера и состояния, без описаний нарядов и дефектов?
7Как синхронизироваться по ленте /api/v2/events без потерь?