Перейти к содержанию

Справочник API OKTO

Полный справочник REST и WebSocket API центрального локального сервера (далее — ЦЛС) и граничного edge-сервиса. Для каждого эндпоинта: путь, роль, параметры, тело, пример ответа, коды ошибок.

Аудитория: интеграторы, авторы автоматизаций, разработчики клиентов. Связанные документы: SERVER_MANAGEMENT.ru.md, ARCHITECTURE.ru.md, DEPLOYMENT.ru.md.


Содержание

  1. Базовые URL и версионирование
  2. Формат ответа
  3. Аутентификация
  4. Коды ошибок
  5. Health и version
  6. Auth — /api/v1/auth/*
  7. Users — /api/v1/users/*
  8. Terminals — /api/v1/terminals/*
  9. Devices — /api/v1/devices/*
  10. Device commands — /api/v1/devices/{id}/commands
  11. Device groups — /api/v1/device-groups/*
  12. Firmware — /api/v1/firmware/*
  13. Dashboard — /api/v1/dashboard/*
  14. Sync (для edge) — /api/v1/sync
  15. Alerts — /api/v1/alerts/*
  16. Audit log — /api/v1/audit-log
  17. Connection mode — /api/v1/config/connection-mode
  18. WebSocket: /ws/device
  19. WebSocket: /ws/dashboard
  20. Edge REST API (локальный, для операторского UI)

1. Базовые URL и версионирование

Сервис Дефолт URL Порт
Центральный локальный сервер https://<factory-host>/api/v1 8081
Edge-сервис (на устройстве) http://<edge-host>/api/v1 8080

Все REST-эндпоинты находятся под префиксом /api/v1. При breaking changes вводится /api/v2, старая версия сохраняется как минимум один мажор.


2. Формат ответа

Успех:

{
  "success": true,
  "data": { /* полезная нагрузка */ },
  "error": null,
  "meta": { "total": 100, "limit": 50, "offset": 0 }
}

Ошибка:

{
  "success": false,
  "data": null,
  "error": {
    "code": "DEVICE_NOT_FOUND",
    "message": "Device edge-99 does not exist",
    "details": { "identifier": "edge-99" }
  },
  "meta": null
}

Поле meta опционально и заполняется только для list-эндпоинтов с пагинацией.


3. Аутентификация

Все приватные эндпоинты ожидают заголовок:

Authorization: Bearer <JWT>
  • User JWT выдаётся через POST /auth/login. Claims: sub=userId, scope=user, role.
  • Device JWT выдаётся через POST /devices/{id}/token + X-Enrollment-Key. Claims: sub=deviceId, scope=device.

WS-эндпоинты принимают JWT через query-string ?token=<JWT>.

Legacy HMAC session tokens (старый формат из AuthService.createSession) валидны до истечения, но новые клиенты должны использовать JWT.


4. Коды ошибок

HTTP error.code Значение
400 INVALID_PAYLOAD JSON невалиден / не соответствует схеме
400 MISSING_PARAM Отсутствует обязательный query/form-параметр
400 VALIDATION_ERROR Нарушены бизнес-правила (длина, формат, уникальность)
401 UNAUTHORIZED Нет токена или он не прошёл проверку
401 INVALID_ENROLLMENT_KEY X-Enrollment-Key не совпал
403 FORBIDDEN Роль не имеет права на это действие
403 DEVICE_NOT_REGISTERED Auto-enrollment выключен, устройство незнакомо
404 NOT_FOUND Общий 404
404 DEVICE_NOT_FOUND Нет устройства с таким identifier
404 RELEASE_NOT_FOUND Нет прошивки с таким id
409 CONFLICT Конкурентное обновление (optimistic lock)
409 DEVICE_DISABLED Устройство в disabled=true — команда запрещена
413 PAYLOAD_TOO_LARGE Artifact превышает firmware.maxArtifactSizeBytes
422 UNPROCESSABLE Запрос валиден синтаксически, но не может быть выполнен
429 RATE_LIMITED Слишком частые вызовы (добавить rate-limit плагин)
500 INTERNAL_ERROR Необработанное исключение
502 CLOUD_UPSTREAM_ERROR OKTO Cloud недоступен
503 SERVICE_UNAVAILABLE ЦЛС в режиме maintenance или БД недоступна

5. Health и version

GET /health

Без аутентификации. Возвращает 200 если Ktor жив.

HTTP/1.1 200 OK
Content-Type: text/plain

OK

GET /api/v1/health

С проверкой БД. 200 если ok, 503 если БД недоступна.

{ "success": true, "data": { "status": "ok", "db": "ok", "uptimeSeconds": 345123 } }

GET /api/v1/version

{
  "success": true,
  "data": {
    "service": "factory-server",
    "version": "1.2.3",
    "gitSha": "abc1234",
    "buildTimestamp": "2026-04-17T18:42:00Z"
  }
}

6. Auth — /api/v1/auth/*

POST /auth/login

Публичный. Возвращает JWT + сессию (для легаси).

Request:

{ "username": "admin", "password": "admin123" }

Response 200:

{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs…",
    "sessionToken": "…",
    "user": { "id": "…", "username": "admin", "role": "ADMIN", "disabled": false }
  }
}

Ошибки: - 401 INVALID_CREDENTIALS - 403 USER_DISABLED - 429 RATE_LIMITED

Аудит: LOGIN_SUCCESS / LOGIN_FAILED.

POST /auth/logout

Приватный. Инвалидирует session row. JWT остаётся валидным до exp (ограничение).

GET /auth/me

Приватный. Возвращает текущего пользователя.

{
  "success": true,
  "data": { "id": "…", "username": "admin", "role": "ADMIN", "disabled": false, "lastLoginAt": "…" }
}

POST /auth/change-password

Приватный. Требует текущий пароль.

{ "currentPassword": "...", "newPassword": "..." }

7. Users — /api/v1/users/*

Все операции — роль ADMIN (кроме GET /users — MANAGER+).

GET /users

Список пользователей. Фильтры: role, disabled, q (search по username).

{
  "success": true,
  "data": [
    { "id": "u-1", "username": "admin", "role": "ADMIN", "disabled": false, "createdAt": "…", "lastLoginAt": "…" }
  ],
  "meta": { "total": 3, "limit": 50, "offset": 0 }
}

POST /users

{ "username": "operator1", "password": "secret", "role": "OPERATOR" }

Валидация: - username уникален, длина 3–32, ASCII. - password ≥ 8 символов. - role ∈ enum UserRole.

GET /users/{id}

PUT /users/{id}

{ "role": "MANAGER", "disabled": false }

DELETE /users/{id}

Soft delete: disabled=true (данные не удаляются для аудита).

Аудит: USER_CREATED / USER_ROLE_CHANGED / USER_DELETED.


8. Terminals — /api/v1/terminals/*

Терминалы — логические точки доступа (обычно соответствуют edge-устройствам). Используются для scope-ирования пользователя.

GET    /terminals                     — список
POST   /terminals                     — создать
GET    /terminals/{id}                — детали
PUT    /terminals/{id}                — обновить
DELETE /terminals/{id}                — удалить
POST   /terminals/{id}/heartbeat      — heartbeat от терминала

Поля:

{
  "id": "term-1",
  "name": "Терминал №1",
  "deviceId": "edge-01",
  "allowedUsers": ["u-1","u-2"]
}

9. Devices — /api/v1/devices/*

GET /devices

Список зарегистрированных устройств. Фильтры:

Параметр Тип Описание
status ONLINE,OFFLINE,ERROR,MAINTENANCE Множественный
companyId string
productionLineId string
connectionMode DIRECT_CLOUD,VIA_LOCAL_SERVER
enabled bool
groupId string
q string Подстрочный поиск по name/identifier
limit, offset Пагинация

Response:

{
  "success": true,
  "data": [
    {
      "identifier": "edge-01",
      "name": "Line 1 terminal",
      "type": "EDGE",
      "status": "ONLINE",
      "companyId": "acme",
      "productionLineId": "line-1",
      "connectionMode": "VIA_LOCAL_SERVER",
      "connectionModeOverride": false,
      "enabled": true,
      "groupId": "grp-eu",
      "firmwareVersion": "1.2.3",
      "version": "1.2.3",
      "ipAddress": "10.0.1.23",
      "lastHeartbeat": "2026-04-17T22:15:43Z",
      "lastCommandAt": "2026-04-17T22:14:11Z",
      "createdAt": "2026-01-10T12:00:00Z"
    }
  ],
  "meta": { "total": 14, "limit": 50, "offset": 0 }
}

POST /devices

Ручная регистрация (без auto-enrollment).

{
  "identifier": "edge-05",
  "name": "Spare terminal",
  "companyId": "acme",
  "productionLineId": "line-5",
  "connectionMode": "VIA_LOCAL_SERVER"
}

GET /devices/{id}

Полные детали устройства.

PUT /devices/{id}

Обновление (имя, группа, enabled).

DELETE /devices/{id}

Удаление. Связанные aggregated_* строки остаются (soft unregister).

POST /devices/{id}/token

Публичный endpoint с X-Enrollment-Key. См. SERVER_MANAGEMENT.ru.md §3.

POST /devices/{id}/heartbeat

Edge-устройство шлёт heartbeat с метриками (альтернатива WS StatusEvent — используется в DIRECT_CLOUD режиме).

{
  "deviceId": "edge-01",
  "status": "ONLINE",
  "timestamp": "2026-04-17T22:15:43Z",
  "ipAddress": "10.0.1.23",
  "version": "1.2.3",
  "metrics": {
    "cpuUsage": 42.5,
    "memoryUsage": 61.2,
    "diskUsage": 28.4,
    "offlineQueueDepth": 2,
    "bottlesProcessedLastHour": 1380,
    "errorsLastHour": 0
  }
}

Сохраняется в devices.last_heartbeat + device_metrics (если метрики поданы).

GET /devices/{id}/metrics

История метрик.

Параметры: since, until, resolution=5m (агрегация).

{
  "success": true,
  "data": [
    { "timestamp": "2026-04-17T22:00:00Z", "cpuUsage": 38.2, "memoryUsage": 58.1, "offlineQueueDepth": 1 }
  ]
}

GET /devices/connected

Моментальный срез WS-сессий:

{
  "success": true,
  "data": [
    { "deviceId": "edge-01", "connectedAt": "…", "lastSeen": "…", "firmwareVersion": "1.2.3" }
  ]
}

GET /devices/{id}/status, GET /devices/{id}/history

Зарезервированы для кастомных интеграций (статус за 24 ч, история подключений).


10. Device commands

POST /devices/{id}/commands

См. детали в SERVER_MANAGEMENT.ru.md §5.

GET /devices/{id}/commands

Фильтры: status, type, createdBy, since, until, limit, offset.

GET /devices/{id}/commands/{cmdId}

GET /devices/{id}/logs

История логов конкретного устройства.

GET /devices/{id}/logs?limit=200&offset=0&level=WARN&since=2026-04-17T20:00:00Z

Поля: id, deviceId, commandId?, ts, level, logger, line.

GET /devices/{id}/config

{
  "success": true,
  "data": {
    "deviceId": "edge-01",
    "configJson": "{\"scanner\":{\"timeoutMs\":5000}}",
    "version": 3,
    "updatedAt": "…",
    "updatedBy": "u-admin"
  }
}

PUT /devices/{id}/config

{ "configJson": "{\"logging\":{\"level\":\"DEBUG\"}}", "version": 3, "autoDispatch": true }
  • version — optimistic lock (если не совпадает с текущим — 409).
  • autoDispatch: true → сервер сразу шлёт PushConfigCmd устройству.

11. Device groups

GET /device-groups

{
  "success": true,
  "data": [
    { "id": "grp-eu", "name": "EU terminals", "description": "…", "memberCount": 12, "createdAt": "…" }
  ]
}

POST /device-groups

{ "name": "EU terminals", "description": "All terminals in EU region" }

GET /device-groups/{id}

Возвращает детали + участников.

PUT /device-groups/{id}

DELETE /device-groups/{id}

GET /device-groups/{id}/members

Список deviceId + опционально сами устройства.

POST /device-groups/{id}/members

{ "deviceIds": ["edge-01","edge-02","edge-03"] }

DELETE /device-groups/{id}/members

{ "deviceIds": ["edge-03"] }

POST /device-groups/{id}/commands

Bulk dispatch. См. SERVER_MANAGEMENT.ru.md §6.


12. Firmware — /api/v1/firmware/*

POST /firmware/releases

Upload. Content-Type: application/octet-stream. Параметры в query:

Param Required Description
version Уникальная версия (SemVer рекомендовано)
channel stable (default) / beta / canary
filename Имя файла, default firmware.jar
notes Release notes
signatureBase64 Ed25519 signature (если включена проверка)

Response 200:

{
  "success": true,
  "data": {
    "id": "73e395e6-…",
    "version": "1.2.3",
    "channel": "stable",
    "artifactUrl": "/api/v1/firmware/releases/73e395e6-…/artifact",
    "sha256": "d5e5a02b…",
    "sizeBytes": 12345678,
    "releaseNotes": "…",
    "createdAt": "…",
    "createdBy": "u-admin"
  }
}

GET /firmware/releases

Список. Фильтры: channel, since, until.

GET /firmware/releases/{id}

DELETE /firmware/releases/{id}

Удаляет метаданные и артефакт на диске. Деплои со ссылкой на релиз остаются (для аудита), но url становится невалидным.

GET /firmware/releases/{id}/artifact

Скачивание. Стримит application/octet-stream. Проверяет Authorization (user или device JWT).

POST /firmware/deployments

{ "releaseId": "73e395e6-…", "deviceIds": ["edge-01","edge-02"] }

Альтернативно { "releaseId": "…", "groupId": "grp-eu" }.

Response: Map<deviceId, CommandResult>.

GET /firmware/deployments

Фильтры: releaseId, deviceId, status.

{
  "success": true,
  "data": [
    { "id": "dep-…", "releaseId": "73e395e6-…", "deviceId": "edge-01", "status": "SUCCESS", "startedAt": "…", "completedAt": "…", "errorMessage": null }
  ]
}

13. Dashboard — /api/v1/dashboard/*

GET /dashboard/overview

Сводка для главной страницы консоли.

{
  "success": true,
  "data": {
    "onlineDevices": 12,
    "totalDevices": 14,
    "bottlesProcessedToday": 12854,
    "batchesCreatedToday": 214,
    "palletsCreatedToday": 18,
    "cloudQueueDepth": 3,
    "cloudQueueDeadLetters": 0,
    "averageSyncLagSeconds": 2.1
  }
}

GET /dashboard/production

Агрегаты по производственным линиям / компаниям.

{
  "success": true,
  "data": [
    { "productionLineId": "line-1", "bottles": 8214, "batches": 120, "pallets": 10 }
  ]
}

GET /dashboard/devices/summary

Short-form устройства (для табличного виджета).

GET /dashboard/sync/queue

Статистика cloud_sync_queue:

{
  "success": true,
  "data": {
    "pendingCount": 3,
    "inProgressCount": 1,
    "failedCount": 0,
    "deadLetterCount": 0,
    "oldestPendingAgeSeconds": 14
  }
}

14. Sync (для edge) — /api/v1/sync

Ручка, через которую edge-устройство (в режиме VIA_LOCAL_SERVER) отправляет накопленные операции.

Аутентификация — device JWT.

POST /api/v1/sync HTTP/1.1
Authorization: Bearer <deviceJwt>
Content-Type: application/json

{
  "deviceId": "edge-01",
  "timestamp": "2026-04-17T22:15:43Z",
  "operations": [
    {
      "operationId": "op-…",
      "operationType": "BOTTLE_CREATE",
      "payload": { "bottle": { "identifier": "b-1", "exciseDutyNumber": "…", "productionLineId": "line-1" } },
      "createdAt": "…"
    }
  ]
}

Response:

{
  "success": true,
  "data": {
    "accepted": ["op-1","op-2"],
    "rejected": [ { "operationId": "op-3", "reason": "duplicate excise_duty_number" } ]
  }
}

Отклонённые операции edge помечает как DONE (они либо дубликаты, либо нарушают инварианты и не подлежат retry).


15. Alerts — /api/v1/alerts/*

GET    /alerts                     — список
GET    /alerts/counts              — агрегированный счёт по severity
GET    /alerts/{id}                — детали
POST   /alerts/{id}/acknowledge    — ACK

Поля алерта:

{
  "id": 123,
  "deviceId": "edge-01",
  "severity": "WARN",
  "category": "printer_offline",
  "message": "Printer videojet-1 not responding",
  "details": "…",
  "acknowledged": false,
  "acknowledgedBy": null,
  "acknowledgedAt": null,
  "createdAt": "…"
}

16. Audit log

GET /api/v1/audit-log

Фильтры: userId, entityId, action, since, until, limit, offset.

{
  "success": true,
  "data": [
    {
      "id": 45123,
      "actorUserId": "u-admin",
      "action": "DEVICE_COMMAND_DISPATCHED",
      "entityType": "device",
      "entityId": "edge-01",
      "ip": "10.0.1.5",
      "userAgent": "Mozilla/5.0 …",
      "metaJson": { "commandType": "force_sync", "commandId": "cmd-…" },
      "ts": "2026-04-17T22:15:43Z"
    }
  ],
  "meta": { "total": 4521, "limit": 200, "offset": 0 }
}

17. Connection mode

GET /api/v1/config/connection-mode

{
  "success": true,
  "data": {
    "settings": {
      "defaultMode": "VIA_LOCAL_SERVER",
      "allowDeviceOverride": true,
      "cloudEndpoint": "https://app.okto.ru/api/v1",
      "cloudConfigured": true
    },
    "stats": {
      "totalDevices": 14,
      "viaLocalServerCount": 12,
      "directCloudCount": 2,
      "withOverridesCount": 3
    }
  }
}

PUT /api/v1/config/connection-mode

{
  "defaultMode": "VIA_LOCAL_SERVER",
  "allowDeviceOverride": true,
  "cloudEndpoint": "https://app.okto.ru/api/v1",
  "cloudApiKey": "secret"
}

Переопределение для конкретного устройства

PUT /api/v1/devices/{id}/connection-mode
{ "mode": "DIRECT_CLOUD", "reason": "Manual override for testing" }

Очистка override

DELETE /api/v1/devices/{id}/connection-mode

Устройство возвращается к глобальному дефолту.


18. WebSocket: /ws/device

Принимает device JWT в query-string: wss://<factory>/ws/device?token=<deviceJwt>.

После handshake:

  1. Устройство шлёт DeviceHelloMessage { deviceId, version, bootTs } (первый кадр).
  2. Сервер регистрирует в DeviceConnectionRegistry и начинает слать команды.
  3. Устройство присылает StatusEvent, LogLineEvent, CommandResult, CommandProgress, DeviceScanEvent, DevicePrintEvent, DeviceAlertEvent.
  4. Сервер шлёт PING каждые management.heartbeatIntervalSeconds; устройство должно отвечать PONG.

Формат — JSON с полем type (classDiscriminator).

Примеры

Сервер → устройство (команда):

{
  "type": "force_sync",
  "id": "cmd-550e8400-e29b-41d4-a716"
}

Устройство → сервер (результат):

{
  "type": "cmd_result",
  "commandId": "cmd-550e8400-e29b-41d4-a716",
  "success": true,
  "output": "Force-sync completed. In-progress: 7",
  "error": null,
  "exitCode": null,
  "data": { "durationMs": 412 }
}

Устройство → сервер (heartbeat):

{
  "type": "status",
  "deviceId": "edge-01",
  "status": "ONLINE",
  "ts": "2026-04-17T22:15:43Z",
  "metrics": { "cpuUsage": 38.1, "memoryUsage": 57.0, "offlineQueueDepth": 0 }
}

Close codes

Код Причина
1000 Нормальное закрытие
1008 Невалидный JWT или отсутствует токен
1011 Внутренняя ошибка сервера
4000 Не прислан DeviceHelloMessage в течение 5 секунд
4001 Device JWT истёк
4002 Device разрешил disabled=true и сервер закрыл сессию

19. WebSocket: /ws/dashboard

Принимает user JWT в query-string: wss://<factory>/ws/dashboard?token=<userJwt>.

Subscribe

Первый кадр обязателен в течение 5 с:

{ "type": "subscribe", "deviceIds": ["edge-01"], "eventTypes": ["status","cmd_result","log_line","alert","scan"] }

Пустые массивы = получать всё. eventTypes ∈ {status, scan, print, alert, cmd_result, cmd_progress, log_line}.

Unsubscribe

{ "type": "unsubscribe" }

Примеры входящих событий

{"type":"status","deviceId":"edge-01","status":"ONLINE","ts":"…","metrics":{}}
{"type":"cmd_result","deviceId":"edge-01","commandId":"cmd-…","success":true,"output":"…","ts":"…"}
{"type":"log_line","deviceId":"edge-01","ts":"…","level":"ERROR","line":"…"}
{"type":"alert","deviceId":"edge-01","severity":"WARN","message":"Printer offline"}

Close codes

Аналогичны /ws/device. Дополнительно: - 4003 — Timeout на первое subscribe-сообщение.


20. Edge REST API (локальный, для операторского UI)

Edge-сервис также поднимает REST на порту 8080 для оператора.

POST /api/v1/auth/login

Локальный логин оператора.

GET /api/v1/tasks

Список копакинг-заданий на линии.

POST /api/v1/bottles

Создать бутылку (обычно из сканера):

{
  "bottles": [
    { "identifier": "b-1", "exciseDutyNumber": "0104650001234567211234pl01", "productionLineId": "line-1" }
  ],
  "deviceId": "edge-01"
}

POST /api/v1/batches

Создать батч из N бутылок.

POST /api/v1/pallets

Создать паллету из M батчей.

POST /api/v1/batches/{id}/fixate

Финализировать временный батч.

GET /api/v1/hardware/status

Статус принтеров/сканеров/ПЛК.

GET /api/v1/queue/stats

Статистика локальной очереди:

{
  "pendingCount": 12,
  "inProgressCount": 1,
  "doneCount": 8421,
  "failedCount": 0,
  "oldestPendingAgeSeconds": 3
}

POST /api/v1/sync/trigger

Мгновенный sync (эквивалент force_sync команды).

GET /api/v1/connection-mode

Текущий режим (DIRECT_CLOUD / VIA_LOCAL_SERVER).

PUT /api/v1/connection-mode

Смена режима (если allowDeviceOverride=true на сервере).


Обновлено: апрель 2026. Статус: активно поддерживается. Для вопросов — engineering@okto.ru.