HubEx API — обзор
Что здесь: путеводитель по 22 сервисам HubEx API, авторизация, самые используемые ручки. Когда сюда идти: любая работа с API — читать до выбора конкретных ручек и сервиса. Источник:
doc.hubex.ru, «Путеводитель по схемам» · Обновлено: 2026-07-02
HubEx — облачная FSM-платформа (Field Service Management) для управления выездным обслуживанием. Автоматизирует цикл работы с заявками: приём обращения → назначение исполнителя → выполнение → закрытие → акты и аналитика. Подробнее про саму систему и её сущности — продукт описан в вики HubEx (домен HubEx.Wiki).
Авторизация
Все модули используют единый механизм:
POST {BASE_URL}/AUTHZ/AccessTokens body: {"serviceToken": "..."} -> access_token
Далее на каждый запрос: Authorization: Bearer {access_token}.
{BASE_URL}=https://api.hubex.ru/fsm(подтверждено живым вызовом 2026-07-06); полный путь ручки —{BASE_URL}/{SERVICE}/..., напр.https://api.hubex.ru/fsm/WORK/Tasks.- Если пользователь уже дал готовый JWT access_token (не serviceToken) — шаг
POST /AccessTokensне нужен, использовать токен напрямую какBearer. Токены живут ~30 минут (exp - iat) — при долгой задаче считать бюджет времени и не тянуть с запросами.
Дополнительные заголовки:
- ⚠
X-Application-ID: 3— обязателен на каждом запросе (без него любой эндпоинт →403). - Пагинация: заголовок
Range: "items=FROM-TILL", либо queryoffset,fetch. Ответ206= частичный контент. - Любой эндпоинт может вернуть
401/403.
Бесшовная реавторизация (refresh, проверено живым вызовом 2026-07-14): access_token живёт ~30 мин — чтобы не переавторизовываться логином, при старте сразу возьми refresh-токен, а по истечении access обменивай его на новый:
1) POST {BASE_URL}/AUTHZ/RefreshTokens Bearer {access_token} + X-Application-ID
тело: {} (GenerateData: опц. "validity") -> JwtResultBase.refresh_token
живёт ~1 год (expires_in=31536000). Сохрани его.
2) POST {BASE_URL}/AUTHZ/AccessTokens X-Application-ID (авторизация не нужна — метод анонимный)
тело: {"refreshJwt": "<refresh_token>"} -> новый access_token (expires_in=1800, ~30 мин)
Тот же POST /AccessTokens обменивает и serviceToken ({"serviceToken":"..."}), и refreshJwt — это единая ручка выпуска access_token. Полученный access идёт как Bearer до следующего истечения, дальше — снова шаг 2 (логин не требуется, пока жив refresh).
- ⚠
POST /AUTHZ/RefreshTokensтребует живой access: с протухшим →401(проверено живым вызовом 2026-07-15 на токене, мёртвом 6 мин). Значит refresh нельзя взять лениво «по первому 401» — к тому моменту менять уже нечего. Брать refresh сразу при старте, пока access жив. - ⚠ Токен принимается ещё ~5 минут после
exp(похоже на дефолтныйClockSkew= 5 мин в .NET): тот жеPOST /AUTHZ/RefreshTokensс токеном, мёртвым 107 сек, отдал200, а мёртвым 6 мин —401(проверено 2026-07-15). Ловушка при отладке: «протухший» токен какое-то время отвечает200, и тест логики истечения внутри этого окна измеряет не то, что кажется. Проверять — только за пределами 5 минут отexp. - ⚠ Надёжный способ узнать общее количество без вычитки списка —
HEAD /Tasks [task-query], тотал приходит в заголовке ответаContent-Range: items=0-0/{total}.
Пагинация — проверено живыми вызовами 2026-07-06 (GET /Tasks, 180 записей):
Range: items=FROM-TILL— 1-индексация, границы включительно.items=1-50вернёт первые 50 записей. Это НЕ HTTP-байтовый 0-индекс.?offset=N&fetch=M— 0-индексация обычным образом (offset= сколько пропустить).offset=0&fetch=50эквивалентенRange: items=1-50— оба вернули один и тот же набор (id 7090…7041).- ⚠
Range: items=0-49(по привычке с 0) — не ошибка, но и не то же самое, чтоitems=1-50:FROM=0молча схлопывается до 1, аTILL=49остаётся как есть → получаешь 49 записей вместо 50 (Content-Range: items=1-49/180). Данные при этом не теряются и не дублируются, если следующая страница берётTILL+1как свойFROM(просто первая “страница” окажется на 1 короче остальных) — но точный размер страницы предсказать нельзя, если начинать с 0. - Правый край при выходе за тотал безопасно клампится:
items=151-999при тотале 180 →Content-Range: items=151-180/180, без ошибки. - Практика: не считать смещения вручную — после каждого ответа парсить фактический
Content-Range: items=A-B/totalи запрашивать следующую страницу сFROM=B+1; условие остановки —B == total(или объём ответа < запрошенногоfetch). - ⚠ Запрос без
Rangeмолча обрезается на 10000 записей — и отвечает200, а не206(проверено живым вызовом 2026-07-15 на тенанте 1: в тенанте 577953 объекта,HEAD /ES/Assets→Content-Range: items=0-0/577953, аGET /ES/AssetsбезRange→200и ровно 10000 записей; данные за границей достижимы:Range: items=10001-10005→206и 5 записей). Ловушка в том, что200читается как «отдано всё»: клиент, считающий количество по длине ответа, покажет 10000 вместо 578 тысяч и не заметит. Считать количество только поContent-Range. Граница проверена наGET /ES/Assets; похоже на общий лимит выдачи, но на других сервисах не подтверждалась. - ⚠ Без заголовка
Rangeв ответе нетContent-Range(проверено 2026-07-15:GET /COMMON/ContactsбезRange→200, все 55 записей, заголовка нет). Нужен тотал — шлиRangeвсегда, даже если страница заведомо одна.
Форма ответа
⚠ Незаполненные поля не приходят вовсе — набор ключей меняется от записи к записи (проверено 2026-07-15: в 10000 записей GET /ES/Assets — 19 разных наборов, общих полей 7 из 15). Нет ключа = поле пустое, а не «поля нет в API»: перечень полей брать из schemas/, читать через obj.get(...).
⚠ Тело ошибки обычно — ErrorModel (в ADM/ES он назван ExceptionHandlingModelsErrorModel, у 6 сервисов не объявлен вовсе), но на кодах ≥400 изредка встречаются и другие типы, вплоть до прикладных — сверяйся по ручке в schemas/<SVC>.md. 401/403 может вернуть любой эндпоинт — в кодах ручек они не печатаются.
Плагины (аддоны): откуда токен и контекст
Плагин HubEx — сторонняя страница, встроенная в интерфейс через iframe (описание механизма — домен HubEx.Wiki). Своей авторизации плагину не нужно: система сама прокидывает контекст query-параметрами открытой страницы.
| Параметр | Когда приходит | Что это |
|---|---|---|
access_token |
всегда, в любом месте размещения | access_token текущего пользователя — идёт дальше как Bearer (права = права пользователя) |
assetID |
плагин размещён на странице объекта | идентификатор объекта → GET /ES/Assets/{assetID} |
taskID |
плагин размещён на форме заявки | идентификатор заявки → GET /WORK/Tasks/{taskID} |
Дальше — обычные вызовы API: Authorization: Bearer {access_token} + обязательный X-Application-ID: 3 (см. выше). Шаг POST /AUTHZ/AccessTokens не нужен — токен уже готовый.
- CORS открыт полностью — API отдаёт
Access-Control-Allow-Origin: *, браузерный плагин ходит в API напрямую, свой прокси не нужен. - ⚠ Этих параметров нет в swagger (справочник описывает только сам REST API, не контракт встраивания) — источник: мейнтейнер, 2026-07-15. В
endpoints/их не ищи. - ⚠ Токен живёт ~30 мин, как и любой access_token, а плагин получает его один раз при открытии iframe — на долгой сессии в открытой вкладке вызовы начнут отдавать
401. Refresh-токен в iframe не прокидывается. - Регистрация плагина в тенанте (
POST /ADM/Tenants/this/packages, типADDONPackageAddData:addonUrl,resourceID= слот размещения) — операция кросс-тенантного администратора, на практике идёт через поддержку HubEx. Справочник значенийresourceIDнаружу не выведен.
Фильтрация
⚠ API молча игнорирует незнакомые query-параметры — не отвечает ошибкой (проверено 2026-07-15: GET /ES/Assets?zzzGarbage=51 → 200 и полная невыборка, ровно как без параметра; HEAD /ES/Assets?zzzGarbage=1 → Content-Range: items=0-0/577953, то есть тотал всего тенанта). Практические следствия: опечатка в имени фильтра (contactId вместо contactID, assetId вместо assetID) не даст 400 — вернутся неотфильтрованные данные, и это выглядит как рабочий ответ. Отсюда же способ проверить, что фильтр реально поддерживается: сравнить выдачу с фильтром и с заведомо мусорным параметром — если совпали, фильтр не работает.
⚠ По умолчанию всегда исключать мягко удалённые записи (isDeleted=false), если человек явно не просил включить удалённые. isDeleted — независимый флаг: любой другой фильтр (isClosed, isCompleted, …) НЕ подразумевает исключение удалённых сам по себе — комбинируй явно. Подробности и живая проверка на заявках — endpoints/WORK.md и notes/WORK.md.
Подробности логина (пароль/SSO/SMS) и получения realm — endpoints/AUTHN.md. Обновление/выпуск токенов — endpoints/AUTHZ.md.
Кросс-тенант админ: перебор тенантов без per-tenant ключей
Аккаунт-админ с доступом ко многим тенантам получает список тенантов и токен для любого из них двумя вызовами (проверено живым вызовом 2026-07-06):
1) POST {BASE_URL}/AUTHN/Accounts/login
Заголовок: Authorization: Basic base64(login:password) тело: {}
-> AuthResult: access_token (админский, ~30 мин) + tenantEntities[]
(каждый: tenantID, uriName, name, tenantMemberID) — все доступные тенанты (было 367).
2) POST {BASE_URL}/AUTHZ/Accounts/authorize
Заголовок: Authorization: Bearer {админский access_token}
тело: {"tenantID": <id>, "tenantMemberID": <id>}
-> AuthorizationResult.access_token — токен, скоупнутый на тенант.
Дальше все запросы к тенанту идут с этим Bearer.
- ⚠ Логин — именно Basic-заголовок, не поля в теле (иначе
409 ParameterNull [authorization]). - ⚠
tenantMemberIDдля кросс-тенант админа — всегда1, хардкожено вcli/hubex_core/api.py(resolve_bearer_token), не читается изtenantEntities/tenants.json. Кешированный вtenants.jsontenantMemberID(изtenantEntities) может указывать на другое членство без прав чтения — весь API отвечает403при валидной авторизации (проверено 2026-07-06: тенант 1 vessel-service, кешированный378→403наWORK/Tasks/ADM/Users;1→200). CLI-флаг--member-id(api get/writeвcli/hubex_cli.py) остаётся как явный оверрайд на крайний случай, но по умолчанию не нужен. - ⚠
isCrossTenantAdminможет бытьfalse, ноtenantEntitiesвсё равно полный — ориентируйся на него, не на флаг. - Обязателен заголовок
X-Application-ID: 3; пагинация —Range: items=FROM-TILL, ответ206+Content-Range: .../{total}. - Если готовый
access_tokenтенанта уже дан — оба шага пропускаются, токен идёт какBearerнапрямую.
Сервисы
Авторизация и пользователи
| Сервис | Модуль | Что внутри | |——|——–|———–| | AUTHN | Authentication | Логин (пароль, SSO, SMS), получение realm | | AUTHZ | Authorization | Получение/обновление access_token, service_token | | AUTH | Accounts | Регистрация, верификация email/телефона, смена пароля, logout | | ADM | Administration | Пользователи (CRUD, поиск, роли, участки, навыки, аватары, геолокация), роли и полномочия, тенанты, лицензии, приглашения, шаблоны пользователей |
Заявки (основной рабочий процесс)
| Сервис | Модуль | Что внутри | |——|——–|———–| | WORK | Work/Tasks | Главный модуль. Заявки (создание, поиск, обновление, стадии, назначение), чек-листы, выполненные работы, акты, виды работ, комментарии/сообщения к заявкам, шаблоны заявок, дополнительные поля, печатные формы | | TSTG | Task Staging | Жизненные циклы заявок: стадии, переходы, ветки, правила автоназначения, требования к стадиям | | SLA | SLA | Критичности, правила дедлайнов, атрибуты SLA | | PMP | Planned Maintenance | Плановые заявки: расписания, частоты, автосоздание заявок по графику |
Объекты и оборудование
| Сервис | Модуль | Что внутри | |——|——–|———–| | ES | Enterprise Structure | Объекты/оборудование (CRUD, иерархия, атрибуты, классы, типы), компании, участки, навыки, теги, QR-коды, контактные лица |
Кадры и персонал
| Сервис | Модуль | Что внутри | |——|——–|———–| | PA | Personnel Admin | Трудоустройство, назначение на объекты, рейтинги сотрудников, критерии оценки, геотрекинг | | WSP | Work Schedule | Графики работы: правила смен, рабочие расписания, превью и продление графиков |
Договоры
| Сервис | Модуль | Что внутри | |——|——–|———–| | SC | Service Contracts | Договоры обслуживания: привязка объектов, вложения, атрибуты |
Склад и материалы
| Сервис | Модуль | Что внутри | |——|——–|———–| | WH | Warehouse | Склады, материалы, приход/расход/перемещение, инвентаризация, штрихкоды, единицы измерения |
Уведомления и сообщения
| Сервис | Модуль | Что внутри | |——|——–|———–| | MSG | Messaging | Триггеры уведомлений, шаблоны сообщений, почтовые ящики, правила рассылки, push-уведомления |
Отчёты и экспорт
| Сервис | Модуль | Что внутри | |——|——–|———–| | REPORT | Reports | Отчёты: по объектам, исполнителям, компаниям, стадиям, время реакции, время выполнения, Power BI | | EXPORT | Export | Экспорт данных: заявки, пользователи, объекты, компании, материалы (Excel) |
Общие и вспомогательные
| Сервис | Модуль | Что внутри |
|——|——–|———–|
| COMMON | Common | Контакты (/Contacts), вложения (загрузка файлов), справочники валют, часовых поясов, стран, локализация |
| UI | UI | Компоненты интерфейса, фильтры, шаблоны раскладки, настройки отображения |
| NEWS | News | Новости и объявления |
| LIC | Licensing | Сканер лицензий (запуск/остановка/статус) |
| CM | Client Management | Геолокация клиентов |
| PROXY | Proxy | Проксирование запросов к сторонним сервисам |
Типовые сценарии → какие сервисы смотреть
| Задача | Сервисы |
|---|---|
| Создать заявку | WORK (Tasks), ES (объект), ADM (исполнитель) |
| Назначить исполнителя | WORK (Tasks), TSTG (автоназначение), ADM (пользователи) |
| Провести заявку по жизненному циклу | WORK (смена стадии), TSTG (настройка ЖЦ) |
| Зафиксировать выполненные работы | WORK (CompletedWorks, Acts) |
| Создать объект/оборудование | ES (Assets) |
| Настроить плановое обслуживание | PMP (ScheduledTasks) |
| Управлять складом/материалами | WH |
| Настроить уведомления | MSG (Triggers, Templates) |
| Получить отчёт | REPORT |
| Выгрузить данные | EXPORT |
| Авторизоваться | AUTHN (логин) → AUTHZ (токен) |
| Управлять ролями и правами | ADM (Roles, Permissions) |
| Настроить SLA/дедлайны | SLA |
| Управлять договорами | SC |
Наиболее используемые ручки
Общие типы (используются в разных схемах): IdName, IdNameDeleted, IdNameDescription, IdCodeName, IdResult, Period, UserShort, Currency, MeasurementUnit, AttributeType, Domain, Location, LocationShort, Attachment/AttachmentGet, ErrorModel.
| # | Ручка | Сервис |
|---|---|---|
| 1 | POST /AccessTokens — выпуск access_token по serviceToken |
AUTHZ |
| 2 | GET /Tasks — список заявок (map |
WORK |
| 3 | POST /Tasks — создание заявки |
WORK |
| 4 | GET /Tasks/{taskID} — детали заявки (TaskDetailedResult) |
WORK |
| 5 | PATCH /Tasks/{taskID} — точечное обновление поля заявки |
WORK |
| 6 | POST /TaskStagingHistory — смена стадии заявки |
WORK |
| 7 | GET /Tasks/{taskID}/stages/next — доступные переходы стадий |
WORK |
| 8 | POST /CompletedWorks — фиксация выполненных работ |
WORK |
| 9 | GET /Users — список пользователей (map |
ADM |
| 10 | GET /Users/{id} — детали пользователя (UserDetailedResult) |
ADM |
| 11 | GET /Assets — список объектов/оборудования (map |
ES |
| 12 | POST /Assets — создание объекта |
ES |
| 13 | GET /Companies — список компаний |
ES |
| 14 | GET /TaskStages/{id} — параметры стадии ЖЦ |
TSTG |
| 15 | GET /Criticalities — критичности заявок |
SLA |
Полная частотность и остальные ручки — в справочниках отдельных сервисов.