Systems
A System is what the edge agent (or a polling source) connects to: a PLC, an OPC UA server, a folder of CSV exports. It owns every tag it sends. A machine lives inside a System and is made of the tags assigned to it; alerts, health, OEE and predictions stay per machine. A reading names its System by identifier (system_identifier, or machine_identifier under its earlier name) and is routed to a machine by its tag.
All endpoints require a bearer JWT, the Systems page (administrators have it by default; it can be granted per role or per user), and access to every machine: a user limited to some machine groups gets 403, because a System's tags can feed machines outside their groups. Creating, changing and assigning also need the administrator tier and the systems.write action; deleting needs systems.delete. Every write is recorded in the audit log under the resource system.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/systems | List Systems |
GET | /api/v1/systems/summary | Unassigned tags across the company |
POST | /api/v1/systems | Create a System |
GET | /api/v1/systems/{system_id} | Get a System |
PATCH | /api/v1/systems/{system_id} | Update a System |
DELETE | /api/v1/systems/{system_id} | Delete a System |
GET | /api/v1/systems/{system_id}/tags | List a System's tags |
POST | /api/v1/systems/{system_id}/tags/assign | Assign tags to a machine |
POST | /api/v1/systems/{system_id}/tags/unassign | Unassign tags |
POST | /api/v1/systems/{system_id}/tags/assign-unassigned | Assign every unassigned tag to a machine |
The System object
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"tenant_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"name": "Line 3 PLC",
"system_identifier": "LINE-3-PLC",
"protocol": "s7",
"location": "Hall B",
"is_active": true,
"machine_count": 2,
"unassigned_tag_count": 5,
"last_seen_at": "2026-10-08T09:41:00Z",
"status": "receiving",
"created_at": "2026-10-08T08:00:00Z",
"updated_at": "2026-10-08T08:00:00Z"
}
status is receiving (data in the last 24 hours), silent (data once, none since) or never_seen.
List Systems
GET /api/v1/systems
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
page_size | integer | 20 | Items per page (max 100) |
search | string | Match name or identifier |
Returns { "items": [System, …], "pagination": { "total", "page", "page_size" } }.
Unassigned tags
GET /api/v1/systems/summary returns { "unassigned_tags": 3 }: tags, across every active System, that belong to no machine yet.
Create a System
POST /api/v1/systems
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | 1–255 chars |
system_identifier | string | Yes | What the agent sends; unique per company (1–255 chars) |
protocol | string | Yes | One of opc_ua, modbus_tcp, modbus_rtu, s7, ethernet_ip, melsec, fins, ads, bacnet_ip, mqtt, sparkplug_b, pi_web_api, csv, json |
location | string | No | Up to 500 chars |
A System's protocol is a label for how it is read. Two other settings name protocols with their own spellings: a polled source's protocol (opcua or modbus, see Sensor Data) and an Edge Agent source's type (modbus, modbus_rtu, opcua, s7, ethernet_ip, melsec, fins, ads, bacnet, mqtt, sparkplug_b, pi_web_api, csv, json, see Edge Agent configuration). Use the spelling each field lists.
Returns 201 Created with the System; 409 if the identifier is taken. A new System has no machines; every tag it sends waits, unassigned, until you assign it. Add machines to it with POST /api/v1/machines and system_id (see Machines).
Get, update and delete
GET /api/v1/systems/{system_id} returns one System (404 if it is not yours). PATCH takes any of the create fields (409 on a taken identifier). DELETE returns 204 No Content, or 409 while the System still has machines: delete or move them first. A deleted System's tags and history stay, its polling sources stop, and its identifier can be given to a new System.
A System's tags
GET /api/v1/systems/{system_id}/tags
| Parameter | Type | Default | Description |
|---|---|---|---|
filter | string | all | all, unassigned or assigned |
machine_id | uuid | Only the tags of this machine | |
search | string | Match the tag, its metric or its name | |
page, page_size | integer | 1, 20 | Paging (max 100) |
Unassigned tags come first.
{
"items": [
{
"id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427",
"system_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"tag": "DB10.DBD4:REAL",
"is_raw": true,
"metric_name": "oil_temp",
"unit": "celsius",
"machine_id": null,
"machine_name": null,
"standardized_name": null,
"is_confirmed": false,
"last_seen_at": "2026-10-08T09:41:00Z",
"assigned_at": null,
"state": "receiving"
}
],
"pagination": { "total": 1, "page": 1, "page_size": 20 }
}
A tag's state is receiving or silent (nothing in the last 24 hours).
Assign and unassign
POST /api/v1/systems/{system_id}/tags/assign
{ "tag_ids": ["1b4e28ba-2fa1-11d2-883f-0016d3cca427"], "machine_id": "550e8400-e29b-41d4-a716-446655440000" }
Up to 500 tags per request. Returns { "assigned": 1, "history_moving": 1 }.
POST /api/v1/systems/{system_id}/tags/unassign takes { "tag_ids": [...] }; POST /api/v1/systems/{system_id}/tags/assign-unassigned takes { "machine_id": "…" } and assigns every tag the System has waiting.
What happens to readings already stored:
- Assigning a tag that was unassigned moves its waiting readings to the machine, in the background (
history_movingcounts those tags). They feed the machine's baselines at the next baseline run; they raise no alerts. - Reassigning a tag to another machine, or unassigning it, changes only what happens next: readings already stored stay with the machine they belonged to.
- If a move is interrupted (a restart, say), a background sweep finishes it within a few minutes.
Errors
| Status | When |
|---|---|
404 | A tag is not on this System, or the machine is not one of yours |
409 | The machine belongs to another System |
409 | The machine would have two tags recording the same metric. The whole request is refused; the message names the metric and the tags |