Screens
Screens are the HMI pages the designer builds; the API calls them views, under /api/v1/views. Start screens are named entry points into them, under /api/v1/start-screens. Background in Screens and Start screens.
Routes
| Method and path | Role | Purpose |
|---|---|---|
GET /api/v1/views?withBody=true | viewer | Every screen, newest change first |
POST /api/v1/views | engineer | Create an empty screen |
GET /api/v1/views/{id} | viewer | One screen with its components |
PATCH /api/v1/views/{id} | engineer | Change name, description, meta or folder |
PUT /api/v1/views/{id} | engineer | Save name, meta and components in one step, with conflict detection |
PUT /api/v1/views/{id}/components | engineer | Replace the components only |
DELETE /api/v1/views/{id} | engineer | Delete a screen |
GET /api/v1/views/folders | viewer | Folder paths |
POST /api/v1/views/folders | engineer | Create a folder |
POST /api/v1/views/{id}/presence | engineer | Report that the caller has the screen open in the editor |
DELETE /api/v1/views/{id}/presence | engineer | The caller closed the editor |
GET /api/v1/start-screens?all= | viewer | Start screens the caller may see, and the caller's choice |
POST /api/v1/start-screens | engineer | Add a start screen |
PATCH /api/v1/start-screens/{id} | engineer | Change one |
DELETE /api/v1/start-screens/{id} | engineer | Remove one |
PUT /api/v1/start-screens/order | engineer | Reorder |
PUT /api/v1/start-screens/me | viewer (session only) | The caller's own default and launch choice |
Screens are stored as files in the project directory, not in the database. Their ids are generated by the gateway; there are no slugs. A screen is opened by id.
Screen objects
Without components (list, create, PATCH):
{ "id": "…", "name": "Overview", "description": "", "folder": "Line1", "meta": "{}", "updatedAt": "2026-11-08T12:00:00.0000000+00:00", "_count": { "components": 12 } }
With components (GET /api/v1/views/{id}, GET /api/v1/views?withBody=true, PUT): the same fields plus components, an array of:
| Field | Type | Meaning |
|---|---|---|
type | string | One of the known component types (label, numeric, gauge, bar, button, toggle, indicator, led, sparkline, input, image, rectangle, header, template, tank, slider, dropdown, clock, multistate, trend, table, line, arrow, ellipse, polyline, polyshape, text, coord, flex, the sym-* process symbols, pipe, pipe-junction, instrument, status-indicator, command, setpoint, field, analog-bar, live-value, alarm-summary). Unknown types are 400 |
x, y, w, h, z | number | Position and size, rounded to integers. w and h are at least 8; defaults 160 × 80. z defaults to the array index |
locked | boolean | |
config | object or JSON string | The widget's settings; see Widgets |
localId | string | Id inside the screen; must be unique |
parentLocalId | string | The container (coord or flex) this component sits in |
name | string | |
layout | object or JSON string | Layout settings inside a container |
meta and config are stored as JSON text; a value that is not valid JSON is 400. updatedAt is an ISO-8601 string with seven fractional digits; it is also the version token used for conflict detection.
Creating and updating
POST /api/v1/views body {"name": "Overview", "description": "", "meta": "{}", "folder": "Line1"}; name is required (400 {"message":"name is required"}).
PATCH /api/v1/views/{id} takes the same fields, all optional; an empty name is 400.
PUT /api/v1/views/{id}/components takes a bare array of components and returns the stored array.
Saving with baseUpdatedAt
PUT /api/v1/views/{id} is the designer's save:
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | no | |
meta | string | no | JSON text |
components | array | yes | The full component list |
baseUpdatedAt | string | no | The updatedAt the editor started from |
force | boolean | no | Save even if someone else saved since |
When baseUpdatedAt is sent and differs from the stored updatedAt, and force is not true, nothing is written and the answer is:
409 { "statusCode": 409, "code": "CONFLICT", "message": "Someone else has saved this screen since you opened it.", "current": { "...the screen as it is now, with components" } }
On success the response is the screen with components and its new updatedAt.
Folders
GET /api/v1/views/folders returns the folder paths. POST /api/v1/views/folders body {"path": "Line1/Pumps"}; .. is stripped, backslashes become slashes, leading and trailing slashes are removed. Response {"path": "Line1/Pumps"}.
Presence
POST /api/v1/views/{id}/presence records that the signed-in person has the screen open and answers {"editors": [...]}, the other people who do. It needs a session, not an API key (400 otherwise). DELETE on the same path removes the caller. 404 {"message":"Screen not found"} for an unknown screen.
Start screens
GET /api/v1/start-screens returns:
{
"entries": [ { "id": "…", "name": "Operators", "viewId": "…", "viewName": "Overview", "missing": false, "folder": "", "roles": ["operator", "engineer"], "isDefault": true, "order": 0 } ],
"defaultId": "…", "myChoice": null, "gatewayOpenOnLaunch": true, "myOpenOnLaunch": null, "openOnLaunch": true, "canManage": false
}
Without all=true the list holds only entries whose roles include the caller's role (an empty roles means everyone) and whose view still exists. all=true returns every entry and needs engineer (403 otherwise). defaultId is the caller's choice if still visible, else the gateway default, else the first entry. openOnLaunch is the caller's own setting when set, else the gateway's.
POST and PATCH take EntryBody:
| Field | Type | Meaning |
|---|---|---|
name | string | Required on create; up to 80 characters |
viewId | string | Required on create; must exist |
folder | string | Group name, up to 120 characters |
roles | string[] | Subset of viewer, operator, engineer, admin; listing all four is stored as empty (everyone) |
isDefault | boolean | Makes this the one default. Unmarking the default hands it to the next in order; the only entry cannot be unmarked (400) |
At most 100 start screens (400). The first entry created becomes the default and turns the gateway's Open start screen on launch setting on. Deleting the default hands it to the next entry.
PUT /api/v1/start-screens/order body {"ids": ["…", "…"]}: each id once; entries left out keep their place after the listed ones. Returns the ordered list.
PUT /api/v1/start-screens/me body {"startScreenId": "…", "openOnLaunch": "default" | "on" | "off"}, both optional. It belongs to a person, so it needs a session cookie, not an API key. An entry the caller may not see is 400. Returns the same shape as GET.