Appearance
REST API by Task: Worked Examples
Last updated
Common integration jobs, with the request and what comes back. All examples assume $TOKEN holds an access token from /api/auth/login (see REST API Reference) and the appliance is https://scada.local.
Read live values
| Method | Path | Minimum role | Purpose |
|---|---|---|---|
| GET | /api/devices | viewer | Devices with their settings |
| GET | /api/devices/status/all | viewer | Connection status of every device, keyed by device id |
| GET | /api/devices/status/summary | viewer | { connected, faulted } counts |
| GET | /api/tags/live/:deviceId | viewer | Current value of every subscribed tag on one device |
| GET | /api/tags/subscriptions | viewer | Every subscribed tag with its settings |
bash
curl -s https://scada.local/api/tags/live/3 -H "Authorization: Bearer $TOKEN"json
{ "success": true, "data": [
{ "deviceId": 3, "tagPath": "HR100", "value": "182.4", "dataType": "float", "quality": "good", "timestamp": "2026-09-15T06:02:10.998Z" },
{ "deviceId": 3, "tagPath": "HR120", "value": "48213", "dataType": "int", "quality": "good", "timestamp": "2026-09-15T06:02:10.998Z" }
] }/api/tags/live/:deviceId returns what the appliance has cached — it does not poll the device on demand. For tags on the built-in Local Tags device, look up that device's id in /api/devices (driver local). More tag endpoints: Tag API Reference.
Write a value
| Method | Path | Minimum role | Body |
|---|---|---|---|
| POST | /api/tags/write | operator | { deviceId, tagPath, value } — a device tag, or a local tag on the Local Tags device |
| POST | /api/local-tags/write | operator | { tagPath, value } — a local (memory) tag |
bash
# Send a new setpoint to the press
curl -s -X POST https://scada.local/api/tags/write \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"deviceId":3,"tagPath":"HR200","value":185}'
# {"success":true,"data":{"deviceId":3,"tagPath":"HR200","value":185}}A device write is refused with 403 unless the device has write access enabled, and with 403 You do not have permission to write to this tag. when a security level restricts the tag. Every accepted write is recorded in the audit log under the calling user. A local-tag write needs the tag to exist (404 Local tag not found), checks the tag's security level, and does not need write access enabled on the Local Tags device; the value is stored as text and converted to the tag's data type. See Writing Tag Values and Local Tags.
Alarms
| Method | Path | Minimum role | Purpose |
|---|---|---|---|
| GET | /api/alarms/active | viewer | Alarms that are active or unacknowledged |
| GET | /api/alarms/history | viewer | Past alarms; page, limit (≤ 200), severity, from, to (ISO), search |
| PUT | /api/alarms/:id/acknowledge | operator | Acknowledge one alarm; optional body { "comment": "…" } |
| POST | /api/alarms/acknowledge | operator | Acknowledge several: { "ids": [12, 13], "comment": "…" } (at most 500) |
bash
curl -s "https://scada.local/api/alarms/history?severity=critical&from=2026-09-14T00:00:00Z&limit=100" \
-H "Authorization: Bearer $TOKEN"
# { "success": true, "data": [ … ], "meta": { "total": 12, "page": 1, "limit": 100 } }Acknowledging answers { "success": true, "data": { "acknowledged": true } }, or false for an id that doesn't exist or was already acknowledged (its first acknowledgement and comment stand). When the appliance requires acknowledgement comments, a request without one is refused with A comment is required to acknowledge an alarm. Rules, shelving and pipelines are in Alarms; notification settings, on-call schedules and the SMS queue in Notification settings.
History
| Method | Path | Minimum role | Purpose |
|---|---|---|---|
| GET | /api/historian/tags | viewer | Tags that have history enabled |
| GET | /api/historian/query | viewer | Points for one tag: deviceId, tagPath, start (required), stop, window + aggregate |
| GET | /api/historian/export | viewer | The same as CSV |
bash
curl -s -G https://scada.local/api/historian/query -H "Authorization: Bearer $TOKEN" \
--data-urlencode "deviceId=3" --data-urlencode "tagPath=HR100" \
--data-urlencode "start=-8h" --data-urlencode "window=15m" --data-urlencode "aggregate=mean"Aggregates, time handling and the unauthenticated /api/data/history.json are covered in Historian API.
Data tables (MES records, logs, lookups)
| Method | Path | Minimum role | Purpose |
|---|---|---|---|
| GET | /api/custom-tables/by-name/:name/data | viewer | Rows; limit (≤ 1000), offset, search, sortBy, sortDir |
| POST | /api/custom-tables/by-name/:name/data | editor | Insert a row |
| PUT | /api/custom-tables/by-name/:name/data/:rowId | editor | Update a row |
| DELETE | /api/custom-tables/by-name/:name/data/:rowId | editor | Delete a row |
bash
# An ERP pushes a lot record into the "lots" table
curl -s -X POST https://scada.local/api/custom-tables/by-name/lots/data \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"row":{"lot_number":"L-24117","part_number":"BRK-220","qty_planned":1200}}'Body shapes, column types and per-table access rules: Data Tables access.
Run logic on the appliance
| Method | Path | Minimum role | Purpose |
|---|---|---|---|
| POST | /api/named-queries/:idOrName/execute | any (the query's own role setting decides) | Run a named query: { "params": { … } } |
| POST | /api/workflows/:id/execute | editor | Start a workflow: { "params": { … }, "wait": false } |
| POST | /api/scripts/:id/execute | editor | Run a script now |
| POST | /api/schedules/:id/run | editor | Run a scheduled task now |
bash
curl -s -X POST https://scada.local/api/named-queries/lot_status/execute \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"params":{"lot":"L-24117"}}'A workflow started without "wait": true returns at once with { "status": "running" }; with wait the request stays open until the workflow finishes and returns its result. Named queries are the safe way to give an external system SQL access — the statement is fixed by whoever wrote it and only the parameters come from the caller. See Named Queries.
Production data
OEE, downtime, quality, work orders and recipes each have their own endpoints — for example /api/oee, /api/downtime, /api/work-orders. The request bodies and roles are documented with each feature: OEE, Work Orders.
A complete Python client
python
import time, requests
BASE = "https://scada.local/api"
session = requests.Session()
session.verify = "/etc/ssl/certs/scada-local.pem" # or False on a lab bench
def login():
r = session.post(f"{BASE}/auth/login", json={"email": "mes-link@plant.com", "password": "S3cure-Pass"})
r.raise_for_status()
body = r.json()
if body["data"].get("mfaRequired"):
raise RuntimeError("Use an integration account without two-factor")
session.headers["Authorization"] = "Bearer " + body["data"]["tokens"]["token"]
return body["data"]["tokens"]["refreshToken"]
def call(method, path, **kw):
r = session.request(method, BASE + path, timeout=10, **kw)
if r.status_code == 401: # expired: sign in again and retry once
login()
r = session.request(method, BASE + path, timeout=10, **kw)
body = r.json()
if not body.get("success"):
raise RuntimeError(f"{r.status_code}: {body.get('error')}")
return body["data"]
login()
while True:
for v in call("GET", "/tags/live/3"):
if v["quality"] == "good":
print(v["tagPath"], float(v["value"]) if v["dataType"] == "float" else v["value"])
time.sleep(5)From a script on the appliance
Prefer the helpers — tags.write(...), alarms.acknowledge(...), history.query(...) — which need no token. When you do need an endpoint without a helper:
javascript
const session = auth.login('automation@plant.com', 'S3cure-Pass'); // local accounts only; null on failure
const hdr = { Authorization: 'Bearer ' + session.tokens.token };
const res = await http.get('http://localhost:3001/api/alarms/history?limit=20', hdr);
log('Alarms in history: ' + res.meta.total);http.* returns the parsed JSON envelope and times out after 10 seconds. Keep passwords in Secrets rather than in the script text.