API Overview
The backend exposes two REST surfaces on port 5000, plus a gRPC service and a SignalR hub:
| Surface | Base path | Auth | Audience |
|---|---|---|---|
| Internal API | /api/… | JWT bearer (login session) | The dashboard UI; usable for on-box scripting |
| External API | /api/external/v1/… | X-AiBoard-ApiKey header | Third-party integrations (SCADA, historians) |
| gRPC Inference | port 50051 | none (internal network only) | Backend ↔ inference runtime |
| SignalR hub | /hub/realtime | JWT (?access_token=) | Live dashboard updates |
Every internal endpoint requires authentication by default (deny-by-default
policy); only POST /api/auth/login and GET /api/system/health are anonymous.
Two roles exist: admin (full control) and viewer (read-only dashboards).
Interactive API docs (Swagger UI) are served at http://<box>:5000/.
Authentication
Section titled “Authentication”/api/auth/login Exchange username/password for a JWT (8-hour lifetime). Failed logins are rate-limited per IP (5 per 60 s).
Authorization: Bearer <jwt>There are no refresh tokens — re-login when the token expires. GET /api/auth/me
returns the identity behind a token. The admin account is created at install
time by the bundle installer (seed-admin); there are no default
credentials.
Internal API map
Section titled “Internal API map”Datasources
Section titled “Datasources”Input (read path) and output (write path) datasources are managed separately
under /api/datasources/input and /api/datasources/output — both
admin-only. Each supports the same lifecycle:
| Verb + path (relative) | Purpose |
|---|---|
GET / · GET /{id} | List / detail (secret fields redacted as ***) |
POST / · PUT /{id} · DELETE /{id} | Create / update / delete |
GET /plugins · GET /plugins/{type}/schema | Available protocol plugins + their config form schema |
POST /test-connection · POST /{id}/test-connection | Probe reachability |
POST /{id}/tags · POST /{id}/browse-tags | Replace tag mappings / ask the device for its tag list |
Datasources are pure connection management: what exists and how to reach it.
Which model runs on which input, and which pipeline is live, belong to flows
— see the table below. Output datasources additionally support
POST /{id}/test-write — see
Output Datasources.
Base path /api/flows. A flow binds one input datasource, one model version and
any number of output datasources. It is the pairing, and enabling it is what
arms the pipeline.
| Verb + path (relative) | Purpose |
|---|---|
GET / · GET /{id} | List / detail |
POST / · PUT /{id} · DELETE /{id} | Create / update / delete |
POST /{id}/enable | Arm this flow to Ready (body { "enabled": true | false }) |
At most one flow may be enabled at a time; enabling one stands the previous one
down. Editing an enabled flow returns 409 disable_flow_first — a live pipeline
is not re-pointed underneath itself.
Enabling arms the flow to Ready; it does not start streaming. Starting is
still POST /api/inference/start, so “which flow is selected” and “is it
streaming” stay separate facts.
Models
Section titled “Models”/api/models List all model versions (any authenticated user).
/api/models/upload Upload an .onnx file (admin). Validated via the inference runtime; versions are immutable.
/api/models/{modelId}/activate/{version} Activate a version (admin).
/api/models/{modelId}/rollback Re-activate the previous valid version (admin).
Also: GET /{modelId}/active, GET /{modelId}/history,
DELETE /{modelId}/{version}, and DELETE /{modelId}/cleanup (prune old
versions, keep-count configurable).
Inference lifecycle & observability
Section titled “Inference lifecycle & observability”/api/inference/status Current state: idle / ready / running, active datasource, loaded model.
/api/inference/start Begin streaming (admin). 409 if the datasource is not Ready.
/api/inference/stop Stop streaming gracefully — buffered predictions drain to output sinks first (admin).
/api/inference/stats Latency aggregation feeding the Inference Stats page.
/api/inference/backpressure Hot-path counters: drops, sink blocking, channel depth. Monotonic since startup.
Also: GET /api/inference/predictions/recent,
GET /api/inference/payloads/{requestId}, and per-model latency alert
thresholds under /api/inference/thresholds (PUT/DELETE admin).
System, logs, and preferences
Section titled “System, logs, and preferences”| Endpoint | Purpose |
|---|---|
GET /api/system/health (anonymous) | Liveness — used by the Docker healthcheck |
GET /api/system/status · GET /api/system/metrics | Runtime status; CPU/GPU/memory metrics |
GET /api/logs | Paginated centralized logs (backend + inference) |
GET/PUT /api/dashboard/layout | Per-user dashboard layout |
GET/PUT/DELETE /api/me/preferences | Per-user preference blob |
Admin: configuration import/export
Section titled “Admin: configuration import/export”/api/admin/inference-config/export Export the whole pipeline — datasources, tag mappings and flows — as one versioned JSON envelope (secrets redacted).
/api/admin/inference-config/import Import an envelope transactionally — any item failure rolls back the whole file. Modes: merge (default) or replace.
External API
Section titled “External API”Designed for third-party systems. Disabled auth-wise until an API key exists;
keys are issued on the box CLI (issue-api-key), shown once, and revocable
(list-api-keys, revoke-api-key). Requests are rate-limited (100 per 60 s per
client).
X-AiBoard-ApiKey: ak_<your-key>/api/external/v1/predictions/latest Most recent prediction: output values, confidence, model version, window timestamps.
/api/external/v1/predictions/history?limit=&offset= Recent predictions (in-memory, ~1 hour retention, limit ≤ 1000). 503 while inference is down.
Error format
Section titled “Error format”Validation failures return HTTP 400 with machine-readable codes (for example
name_already_exists, shape_mismatch, model_not_paired,
config_invalid:port:range). Lifecycle conflicts return 409 (not_ready,
version_exists). The dashboard maps these codes to localized messages.