This document describes the HTTP REST API exposed by the SMAC-Server application.
The API is served on port 80 (HTTP) and port 443 (HTTPS, when a TLS
certificate is configured). All endpoints are prefixed with /api/v1.
The following table lists the top-level API areas. Each area is documented in its own section below.
| Area | Base Path | Description |
|---|---|---|
| Authentication | /api/v1/auth, /api/v1/sessions |
Login, token refresh, logout, session management → :ref:`authentication` |
| Users | /api/v1/users |
User account CRUD and disable → :ref:`users` |
| System | /api/v1/system, /api/v1/storage, /api/v1/sms |
Device info, metrics, networking, clock, mail, SMS, config → :ref:`system` |
| System Update | /api/v1/system/update |
OTA update upload and installation → :ref:`system-update` |
| Time-Series Database | /api/v1/vmetrics |
VictoriaMetrics query, export, delete, reset → :ref:`timeseries` |
| I/O Management | /api/v1/io |
Units, signals, ports, connections, processing chains → :ref:`io-management` |
| Synthetic Signals | /api/v1/io/synthetic |
Virtual computed signals → :ref:`synthetic-signals` |
| Modbus Server | /api/v1/io/endpoints/modbus |
Built-in Modbus TCP/RTU server and registers → :ref:`modbus-server` |
| Firewall | /api/v1/firewall |
nftables rules and internet sharing → :ref:`firewall` |
| Alerting | /api/v1/alerting |
Alert signals, destinations, and rules → :ref:`alerting` |
| Licensing | /api/v1/licensing |
License import, list, and removal → :ref:`licensing` |
| Apps | /api/v1/apps |
Application enable/disable and debug control → :ref:`apps` |
| Ping / Health | /api/v1/ping |
Liveness check → :ref:`ping` |
The API uses JSON Web Tokens (JWT) signed with the EdDSA algorithm. Two token types are issued:
- Access Token – short-lived (3 minutes), used to authorize API requests.
- Refresh Token – long-lived (30 days), used to obtain a new access token without re-authenticating.
The access token contains the following claims:
sub– the login name of the authenticated user (string)iat– issued-at timestamp (ISO 8601)exp– expiry timestamp (ISO 8601, 3 minutes afteriat)sid– session identifier (UUID without dashes)roles– array of role name strings, e.g.["SystemAdministrator"]
SystemAdministrator– full access to all endpointsGlobalAppAdministrator– application-level administrationAppAdministrator– per-application administrationAppUser– standard user access
Authenticated requests must carry the access token in one of the following ways (listed in order of preference by the server):
Authorization: Bearer <accessToken>x-access-token: <accessToken>(custom header)?access_token=<accessToken>(query parameter, used by :func:`hasSystemAdministratorAccessToken` for certain routes)
Most endpoints require the SystemAdministrator role. A small number of endpoints (ping, system information, system metrics, system clock read, licensing list, unit icon retrieval) are accessible without authentication.
- Login – exchange credentials for an access + refresh token pair.
- Refresh – exchange a refresh token for a new access token.
- Logout – invalidate the refresh token (and thus the session).
- Close Session – delete a specific session by its ID (requires a valid access token belonging to that session).
Authenticate with username and password.
Authentication: none
Request body (JSON, max 1000 bytes):
username(string, required) – login namepassword(string, required) – plaintext password
Example:
{ "username": "hubadmin", "password": "hubadmin" }Responses:
200 OK– body is JSON:{ "accessToken": "eyJhbGciOiJFZERTQSIs...", "tokenType": "Bearer", "refreshToken": "a1b2c3d4e5f6..." }400 Bad Request– body too large or missing fields401 Unauthorized– invalid credentials
Obtain a new access token using a refresh token.
Authentication: none
Request body (JSON, max 1000 bytes):
refreshToken(string, required)
Example:
{ "refreshToken": "a1b2c3d4e5f6..." }Responses:
200 OK– body is JSON:{ "accessToken": "eyJhbGciOiJFZERTQSIs...", "tokenType": "Bearer" }400 Bad Request– body too large or missing field401 Unauthorized– unknown or expired refresh token500 Internal Server Error– token creation failed
Invalidate a session by its refresh token.
- Authentication: none
- Request body (JSON, max 1000 bytes):
refreshToken(string, required)
- Responses:
200 OK– empty body400 Bad Request– body too large or missing field401 Unauthorized– unknown refresh token
Close a specific session. The caller's access token must belong to the same
session (sid claim must match sessionId).
- Authentication: valid access token (any role),
sidmust match - Path parameter:
sessionId(string) – UUID of the session to close
- Responses:
200 OK– empty body401 Unauthorized– missing or invalid token403 Forbidden– token valid butsiddoes not match404 Not Found– session not found
User management is exposed as a standard REST collection. The collection is a
plain JSON array of objects (no "data" sub-key wrapper).
uuid(string, required) – unique identifier (auto-generated on create)loginName(string, required) – unique login namefullName(string, required) – display namepassword(string, required on create, optional on modify) – plaintext password; stored as SHA-256 hash server-side. Never returned in GET responses.role(integer, required) – one of:0– SystemAdministrator1– GlobalAppAdministrator2– AppAdministrator3– AppUser
disabled(boolean, optional) – whether the account is disabled (defaults tofalseon create)
List all users (passwords are stripped).
Authentication: SystemAdministrator
Response ``200 OK`` – JSON array:
[ { "uuid": "907cc14b665a47f2963907f344f7bb73", "loginName": "hubadmin", "fullName": "HUB Administrator", "role": 0, "disabled": false } ]
Retrieve a single user.
- Authentication: SystemAdministrator
- Path parameter:
uuid(string) - Response ``200 OK`` – single user object (no password)
- Response ``404 Not Found`` – user not found
Create a new user.
Authentication: SystemAdministrator
Request body (JSON):
{ "loginName": "operator1", "fullName": "Operator One", "password": "s3cret!", "role": 3 }Responses:
200 OK– body is the new user's UUID (string)400 Bad Request– invalid body409 Conflict– a user with the sameloginNamealready exists
Modify an existing user.
Authentication: SystemAdministrator
Path parameter:
uuid(string)Request body (JSON):
{ "loginName": "operator1", "fullName": "Operator One (renamed)", "password": "newPass123", "passwordConfirm": "newPass123", "role": 3, "disabled": false }Notes:
passwordis optional. If omitted or empty, the existing password hash is preserved.passwordConfirmis accepted but stripped before storage.- If the user is not found and no password is supplied, the request fails.
Responses:
200 OK– empty body400 Bad Request– invalid body404 Not Found– user not found
Delete a user. The built-in hubadmin account cannot be deleted.
- Authentication: SystemAdministrator
- Path parameter:
uuid(string) - Responses:
200 OK– empty body400 Bad Request– attempt to delete the protectedhubadminaccount404 Not Found– user not found
Enable or disable a user account.
Authentication: SystemAdministrator
Path parameter:
uuid(string)Request body (JSON):
{ "disabled": true }Responses:
200 OK– empty body400 Bad Request– invalid body404 Not Found– user not found
System-level endpoints cover device information, metrics, networking, storage, clock, mail, SMS, and configuration objects.
Retrieve basic system information.
Authentication: none
Response ``200 OK`` – JSON:
{ "systemClock": 1721560000, "uptime": 86400, "osVersion": "1.2.3 (Stable)", "osLicenseValid": 1, "osLicenseExpiryDate": "2027-01-01", "ipAddresses": [ "192.168.1.10 (eth0)", "10.0.0.5 (wlan0)", "192.168.123.1 (usb0)" ] }
Retrieve real-time system metrics.
Authentication: none
Response ``200 OK`` – JSON:
{ "totalCpuUsage": 23.5, "brokenDownCpuUsage": { "user": 10.2, "system": 5.1, "idle": 84.7 }, "systemLoad": 0.42, "memoryTotal": 2097152, "memoryAvailable": 1572864, "storageTotal": 16106127360, "storageAvailable": 12884901888, "storageBytesReadPerSecond": 1024, "storageBytesWrittenPerSecond": 2048, "eth0Rx": 1500000, "eth0Tx": 800000, "eth1Rx": 0, "eth1Tx": 0, "wlan0Rx": 500000, "wlan0Tx": 300000, "wwan0Rx": 0, "wwan0Tx": 0 }
Retrieve the most recent system journal messages.
- Authentication: SystemAdministrator
- Response ``200 OK`` – plain-text, one message per line
List running system processes.
- Authentication: SystemAdministrator
- Response ``200 OK`` – JSON array of process objects
Scan for available Wi-Fi networks.
- Authentication: SystemAdministrator
- Response ``200 OK`` – JSON array of network objects
Read the current system clock (seconds since epoch).
Authentication: none
Response ``200 OK`` – JSON:
1721560000
Set the system clock.
Authentication: SystemAdministrator
Request body – plain-text Unix timestamp (seconds), max 128 bytes. Value must be greater than
1234567890.Example:
1721560000Responses:
200 OK– empty body400 Bad Request– body too large or value out of range
Synchronise hardware clocks to the current system time.
- Authentication: SystemAdministrator
- Response ``200 OK`` – empty body
Write an OpenVPN client configuration file and restart the service.
- Authentication: SystemAdministrator
- Request body – raw OpenVPN config text, max 64 KiB
- Responses:
200 OK– empty body400 Bad Request– body too large500 Internal Server Error– failed to write file
Retrieve storage usage details.
- Authentication: SystemAdministrator
- Response ``200 OK`` – JSON object with storage partition details
Remove all Docker files except volumes.
- Authentication: SystemAdministrator
- Responses:
200 OK– empty body500 Internal Server Error– cleanup failed
Remove all Docker files including volumes.
- Authentication: SystemAdministrator
- Responses:
200 OK– empty body500 Internal Server Error– reset failed
Remove a specific Docker volume.
- Authentication: SystemAdministrator
- Path parameter:
name(string) – volume name - Responses:
200 OK– body is the volume name404 Not Found– volume not found
Trigger the device identification LED blink sequence (10 seconds).
- Authentication: SystemAdministrator
- Response ``200 OK`` – empty body
Reboot the device.
- Authentication: SystemAdministrator
- Response ``200 OK`` – empty body
Send a test e-mail using the supplied SMTP settings (does not persist them).
Authentication: SystemAdministrator
Request body (JSON):
{ "serverAddress": "smtp.example.com", "serverPort": 587, "encryptionMode": 2, "authenticationMethod": 2, "username": "mailer@example.com", "password": "secret", "senderName": "SIINEOS Hub", "senderAddress": "hub@example.com", "recipient": "admin@example.com", "subject": "Test", "body": "This is a test e-mail." }Responses:
200 OK– empty body400 Bad Request– e-mail could not be sent
Send an SMS message via the cellular modem.
Authentication: SystemAdministrator
Request body (JSON, max 4 KiB):
{ "recipientNumbers": "+4915112345678,+491529876543", "text": "Alert: Temperature high on Unit 3" }Responses:
200 OK– empty body400 Bad Request– missing fields or body too large500 Internal Server Error– SMS send failed
System settings are exposed as configuration objects. In the JSON payload,
each property is wrapped in a "data" sub-key. For example:
{
"hostname": { "data": "hub-gm100-abc123" },
"deviceName": { "data": "Central Gateway" },
"location": { "data": "Building 1, Room 234" }
}The following configuration objects are available:
GET/POST /api/v1/system/config/datetime– timezone, NTP server, browser auto-syncGET/POST /api/v1/system/config/eth0– Ethernet 1 settings (mode, IP, DHCP server, etc.)GET/POST /api/v1/system/config/eth1– Ethernet 2 settingsGET/POST /api/v1/system/config/wlan0– Wi-Fi settings (SSID, passphrase, channel, etc.)GET/POST /api/v1/system/config/wwan0– cellular modem settings (APN, PIN, roaming, etc.)GET/POST /api/v1/system/config/device– hostname, device name, locationGET/POST /api/v1/system/config/communication-leds– LED target assignments (red, green)GET/POST /api/v1/system/config/debugging– debug/trace logging, filter rules, process metricsGET/POST /api/v1/system/config/services– SSH, VictoriaMetrics, Docker, memory monitor, MQTT broker, OpenVPNGET/POST /api/v1/system/config/tls– CA certificate, device certificate, private key (base64-encoded PEM)GET/POST /api/v1/system/config/smtp– SMTP server, port, encryption, authentication, sender, password
All configuration object endpoints require SystemAdministrator authentication.
Example – reading the device configuration:
GET /api/v1/system/config/device HTTP/1.1
Authorization: Bearer <accessToken>Example response:
{
"name": { "data": "Central Gateway" },
"uuid": { "data": "abc123def456" },
"state": { "data": true },
"location": { "data": "Building 1, Room 234" }
}Example – writing the device configuration:
POST /api/v1/system/config/device HTTP/1.1
Authorization: Bearer <accessToken>
Content-Type: application/json{
"name": { "data": "Central Gateway (renamed)" },
"location": { "data": "Building 2, Room 100" }
}The system update flow is a multi-step process: start an upload, send chunks, then poll for installation state.
Begin a new update bundle upload.
Authentication: SystemAdministrator
Request body (JSON, max 1000 bytes):
{ "bytesToReceive": 536870912 }bytesToReceive(integer, required) – total size of the upload in bytes. Must be ≥ 1024 and ≤ 256 MiB.
Responses:
200 OK– empty body400 Bad Request– body too large, size too small, or size limit exceeded500 Internal Server Error– failed to open output file
Upload a chunk of the update bundle.
- Authentication: SystemAdministrator
- Request body – raw binary data, max 4 MiB per chunk
- Responses:
200 OK– body is the current byte offset (string), e.g."1048576"400 Bad Request– chunk too large, file not open, or size limit exceeded
Poll the current installation progress.
Authentication: SystemAdministrator
Response ``200 OK`` – JSON:
{ "message": "Installing update bundle...", "progress": 45 }
Retrieve error data from the last (or current) update attempt.
- Authentication: SystemAdministrator
- Response ``200 OK`` – plain-text error description (may be empty)
Endpoints for querying, exporting, and managing time-series data stored in the local VictoriaMetrics instance.
Retrieve the list of available time-series (metric labels) for a given time range.
Authentication: SystemAdministrator
Query parameters:
start(string, required) – start timestamp in milliseconds (≥ 13 digits)end(string, required) – end timestamp in milliseconds (≥ 13 digits)
Responses:
200 OK– JSON array of series descriptors:[ { "name": "eth0_rx[unit=Gateway][signal=Throughput]", "hash": "a1b2c3d4e5f6" }, { "name": "cpu_usage[unit=Gateway]", "hash": "f6e5d4c3b2a1" } ]202 Accepted– labels are still being fetched; retry later400 Bad Request– missing or invalidstart/end
Export time-series data as a CSV file (chunked/streaming response).
Authentication: SystemAdministrator
Query parameters:
start(string, required) – start timestamp in millisecondsend(string, required) – end timestamp in millisecondsstep(string, optional) – step size in seconds (default60)rollup(string, optional) – comma-separated rollup functions:min,max,avg,sum,cntseries(string, required) – comma-separated list of series hashesdecimalSeparator(string, optional) –"."(default) or ","dateTimeFormat(string, optional) –timestamp(default),local,utc,localized,isodateTimeLocale(string, optional) – locale string, e.g.de_DE(defaultC)
Response:
200 OK– chunkedtext/plainCSV withContent-Disposition: attachment; filename="SIINEOS-VictoriaMetrics-Export_<start>_<end>.csv"400 Bad Request– invalid parameters401 Unauthorized– missing/invalid token
CSV format: semicolon-separated. First column is the timestamp (formatted according to
dateTimeFormat), subsequent columns are one per requested series.
Delete one or more time-series from the database.
Authentication: SystemAdministrator
Request body (JSON): array of series hash strings
["a1b2c3d4e5f6", "f6e5d4c3b2a1"]
Responses:
200 OK– empty body (deletion successful)400 Bad Request– empty or invalid body404 Not Found– one or more series not found500 Internal Server Error– HTTP error during deletion
Reset (wipe) the VictoriaMetrics database.
- Authentication: SystemAdministrator
- Responses:
200 OK– empty body500 Internal Server Error– reset failed
The I/O subsystem manages units (physical or virtual I/O devices), signals (measurable or controllable channels within a unit), and ports (connection points on a unit). All endpoints require SystemAdministrator authentication unless noted otherwise.
List all I/O unit UUIDs.
Response ``200 OK`` – JSON array of UUID strings:
["907cc14b665a47f2963907f344f7bb73", "a1b2c3d4e5f60718293a4b5c6d7e8f90"]
Retrieve the announcement data for a single unit.
Path parameter:
unitUuid(string)Response ``200 OK`` – JSON:
{ "providerName": "Synthetic Signals", "providerId": "synthetic_signals", "providerType": 4, "providerConnectivity": 0, "uuid": "907cc14b665a47f2963907f344f7bb73", "address": "", "name": "Synthetic signals", "location": "", "enabled": true, "connected": true, "error": "", "icon": "icon.svg", "busy": false, "signalsBrowsable": false, "signalsResettable": false, "signalAddressesContainPinAssignment": false, "signals": ["sig-uuid-1", "sig-uuid-2"], "upstreamUnit": "", "upstreamPort": "", "ports": [] }Response ``404 Not Found`` – unit not found
Create a new I/O unit instance for the given provider.
- Request body – plain-text provider identifier (e.g.
"modbus_client") - Responses:
200 OK– body is the new unit's UUID400 Bad Request– provider not found
Remove a unit and its configuration.
- Responses:
200 OK– empty body404 Not Found– unit not found
Unit settings are stored as a configuration object with the "data"
sub-key pattern.
Read the full unit configuration.
Response ``200 OK`` – JSON with
"data"sub-keys:{ "general": { "name": { "data": "My Modbus Unit" }, "uuid": { "data": "a1b2c3d4..." }, "state": { "data": true }, "providerId": { "data": "modbus_client" }, "upstreamUnit": { "data": "" }, "upstreamPort": { "data": "" }, "signals": { "data": [ ... ] } }, "data": { "customIcon": { "data": "" } } }
Replace the full unit configuration.
- Request body (JSON, max 4 MiB) – same structure as the GET response.
The
providerIdmust match the unit's actual provider; theuuidis overwritten with the path parameter. - Responses:
200 OK– empty body400 Bad Request– invalid JSON or body too large404 Not Found– unit not found409 Conflict– provider ID mismatch
Read a specific configuration sub-object (e.g. "general", "data", or a
signal's "signal" object).
- Path parameters:
unitUuid,configObjectId - Response ``200 OK`` – JSON with
"data"sub-keys for that sub-object - Response ``404 Not Found`` – unit or config object not found
Write a specific configuration sub-object.
- Request body (JSON, max 4 MiB)
- Responses:
200 OK– empty body400 Bad Request– invalid JSON404 Not Found– unit or config object not found
Rename a unit.
- Request body – plain-text name, max 256 bytes
- Responses:
200 OK– empty body400 Bad Request– body too large404 Not Found– unit not found
Retrieve the unit's custom icon (binary).
- Authentication: none
- Query parameter:
<hash>(string) – hash of the icon data (for cache-busting) - Response ``200 OK`` – binary image data
- Response ``404 Not Found`` – unit not found
Upload a custom icon for the unit.
- Request body – binary image data, max 128 KiB
- Responses:
200 OK– empty body400 Bad Request– body too large404 Not Found– unit not found
Remove the custom icon (reverts to the provider default).
- Responses:
200 OK– empty body404 Not Found– unit not found
Set the upstream unit and port for daisy-chained units.
Request body (JSON, max 256 bytes):
{ "unit": "upstream-unit-uuid", "port": "port-uuid" }Responses:
200 OK– empty body400 Bad Request– body too large or invalid JSON404 Not Found– unit not found
Send a provider-specific control command to a unit.
- Path parameters:
unitUuid,action(provider-defined string) - Request body – provider-specific payload, max 4 MiB
- Responses:
200 OK– provider-specific response400 Bad Request– body too large404 Not Found– unit not found501 Not Implemented– unit does not support control
Read a port's configuration.
- Response ``200 OK`` – JSON with
"data"sub-keys - Response ``404 Not Found`` – unit or port not found
Write a port's configuration.
- Request body (JSON) – same structure as GET response
- Responses:
200 OK– empty body404 Not Found– unit or port not found
List signal UUIDs for a unit.
- Response ``200 OK`` – JSON array of UUID strings
Retrieve a single signal's announcement data.
Response ``200 OK`` – JSON:
{ "uuid": "sig-uuid-1", "type": "Temperature", "identifier": "temp_1", "address": "0", "name": "Temperature Sensor 1", "group": "Sensors", "dataSeriesSet": "", "readable": true, "writable": false, "enabled": true, "dataType": 6, "unit": "°C", "decimals": 1, "minValue": -40, "maxValue": 125, "color": "#00dbc9", "visualization": "gauge" }
Add a single new signal to a unit.
- Response ``200 OK`` – body is the new signal's UUID
- Response ``404 Not Found`` – unit not found
Add multiple signals to a unit in one request.
Request body (JSON): array of signal property objects
[ { "name": { "data": "Signal A" }, "address": { "data": "0" } }, { "name": { "data": "Signal B" }, "address": { "data": "1" } } ]Response ``200 OK`` – JSON array of new signal UUIDs
Response ``400 Bad Request`` – invalid JSON
Response ``404 Not Found`` – unit not found
Remove multiple signals from a unit.
Query parameter:
uuids(string, required) – comma-separated signal UUIDsExample:
?uuids=sig-1,sig-2,sig-3Responses:
200 OK– empty body400 Bad Request– missinguuidsparameter404 Not Found– unit not found500 Internal Server Error– removal failed
Duplicate an existing signal with a new name.
Request body (JSON):
{ "name": "Temperature Sensor 1 (copy)" }Responses:
200 OK– body is the new signal's UUID400 Bad Request– missingnamefield404 Not Found– unit or source signal not found500 Internal Server Error– duplication failed
Signal settings use the configuration object pattern with "data"
sub-keys. See :ref:`signal-processing-chains` for the full key reference.
Read a signal's configuration.
- Response ``200 OK`` – JSON with
"data"sub-keys (see :ref:`signal-processing-chains`)
Write a signal's configuration.
- Request body (JSON) – same structure as GET response
- Responses:
200 OK– empty body404 Not Found– unit or signal not found
Write configuration for multiple signals in one request.
- Query parameter:
uuids(string, required) – comma-separated signal UUIDs - Request body (JSON) – configuration object (applied to all listed signals)
- Responses:
200 OK– empty body400 Bad Request– invalid JSON404 Not Found– unit or any signal not found
Remove all signals from a unit.
- Responses:
200 OK– empty body404 Not Found– unit not found500 Internal Server Error– clear failed
Reset specific signals (e.g. zero counters).
Request body (JSON): array of signal UUIDs
["sig-uuid-1", "sig-uuid-2"]
Responses:
200 OK– empty body400 Bad Request– invalid JSON404 Not Found– unit not found
Send a signal-specific control command.
- Path parameters:
unitUuid,signalUuid,action - Request body – provider-specific payload, max 4 MiB
- Responses:
200 OK– provider-specific response400 Bad Request– missing action or body too large404 Not Found– unit or signal not found501 Not Implemented– signal does not support control
I/O connections define signal processing chains that route a source signal's
output to a destination signal's input. The collection is a plain JSON array
(no "data" wrapper).
uuid(string, required) – unique identifiersource(object, required):unit(string) – source unit UUIDsignal(string) – source signal UUID
destination(object, required):unit(string) – destination unit UUIDsignal(string) – destination signal UUID
signalProcessings(array, optional) – array of processing chain configuration objects (see :ref:`signal-processing-chains`)disabled(boolean, optional) – whether the connection is disabled
Endpoints:
GET /api/v1/io/connections– list all connectionsGET /api/v1/io/connections/{uuid}– get one connectionPOST /api/v1/io/connections– create a connectionPUT /api/v1/io/connections/{uuid}– modify a connectionDELETE /api/v1/io/connections/{uuid}– delete a connectionPUT /api/v1/io/connections/disable/{uuid}– enable/disable a connection
All require SystemAdministrator authentication.
Read the default (template) signal processing chain configuration. This is a read-only configuration object.
- Authentication: SystemAdministrator
- Response ``200 OK`` – JSON with
"data"sub-keys (see :ref:`signal-processing-chains`)
Every I/O signal has an embedded signal processing chain that transforms
the raw measurement value through a sequence of optional stages before
producing the final output. The chain is configured via the signal's
configuration object and uses the "data" sub-key pattern.
The processing stages are applied in the following fixed order:
- Pre-processing (mathematical expression)
- Linear scaling
- Delta
- Limit
- Threshold comparison
- Comparator
- Edge detection
- Time derivative
- Aggregation
- Post-processing (mathematical expression)
All keys below appear in the signal's configuration JSON. Each value is wrapped
in { "data": <value> }.
preProcessingEnabled(boolean) – enable/disable the stagepreProcessingExpression(string) – mathematical expression using variablexfor the input value. Example:"1.25 + (x / 2)"
linearScalingEnabled(boolean)linearScalingX1(float) – minimum input valuelinearScalingY1(float) – corresponding minimum output valuelinearScalingX2(float) – maximum input valuelinearScalingY2(float) – corresponding maximum output value
deltaEnabled(boolean)deltaMode(unsigned integer):0– absolute difference to previous value1– relative change from previous value2– relative change in percent3– sign difference from previous value
limitEnabled(boolean)limitDownwards(boolean) – apply lower limitlimitMinimumValue(float) – lower boundlimitUpwards(boolean) – apply upper limitlimitMaximumValue(float) – upper bound
thresholdComparisonEnabled(boolean)thresholdComparisonMode(unsigned integer):0– signal is above threshold1– signal is above or equal2– signal is below threshold3– signal is below or equal
thresholdComparisonValue(unsigned integer) – threshold value
comparatorEnabled(boolean)comparatorLowerThreshold(float)comparatorUpperThreshold(float)comparatorOutputValueOff(float) – output when below lower thresholdcomparatorOutputValueOn(float) – output when above upper thresholdcomparatorTimerIntervalOff(unsigned integer, ms) – required duration below lower thresholdcomparatorTimerIntervalOn(unsigned integer, ms) – required duration above upper threshold
edgeDetectionEnabled(boolean)edgeDetectionCountRising(boolean) – count rising edgesedgeDetectionCountFalling(boolean) – count falling edges
timeDerivativeEnabled(boolean)timeDerivativeTimeBase(unsigned integer):0– per second (Δx/s)1– per minute (Δx/min)2– per hour (Δx/h)3– per day (Δx/d)
aggregationEnabled(boolean)aggregationType(unsigned integer):0– minimum value1– maximum value2– average value3– oldest value4– sum of all values5– median
aggregationInterval(unsigned integer, seconds) – window sizeaggregationUpdateMode(unsigned integer):0– continuously (update on every sample)1– periodically (update at end of interval)
postProcessingEnabled(boolean)postProcessingExpression(string) – mathematical expression using variablex. Example:"x * 1.05"
In addition to the processing chain keys, the signal configuration includes:
name(string) – display nameuuid(string, read-only) – signal UUIDenabled(boolean) – enable/disable the signalvmetricsPushEnabled(boolean) – record values to VictoriaMetricsvmetricsPushInterval(unsigned integer, seconds) – recording intervaluseCustomIdentifier(boolean)customIdentifier(string) – custom identifier (overrides auto-generated)group(string) – grouping labeldataSeriesSet(string) – data series set labelsiPrefix(unsigned integer) – SI prefix (0 = none, 3 = k, 9 = µ, etc.)measurementUnit(string) – unit string (e.g."DegreeCelsius")decimals(unsigned integer) – number of decimal places (0–5)customDataType(unsigned integer) – override data type (0 = keep original)visualizationType(string) –"none","gauge","led","counter"color(string) – hex colour or named colourminimumValue(double) – display minimummaximumValue(double) – display maximum
{
"name": { "data": "Motor Temperature" },
"uuid": { "data": "a1b2c3d4e5f6" },
"enabled": { "data": true },
"vmetricsPushEnabled": { "data": true },
"vmetricsPushInterval": { "data": 60 },
"useCustomIdentifier": { "data": false },
"customIdentifier": { "data": "" },
"group": { "data": "Motor" },
"dataSeriesSet": { "data": "" },
"siPrefix": { "data": 0 },
"measurementUnit": { "data": "DegreeCelsius" },
"decimals": { "data": 1 },
"customDataType": { "data": 0 },
"visualizationType": { "data": "gauge" },
"color": { "data": "#00dbc9" },
"minimumValue": { "data": -40 },
"maximumValue": { "data": 150 },
"preProcessingEnabled": { "data": false },
"preProcessingExpression": { "data": "x" },
"linearScalingEnabled": { "data": true },
"linearScalingX1": { "data": 0 },
"linearScalingY1": { "data": -40 },
"linearScalingX2": { "data": 255 },
"linearScalingY2": { "data": 150 },
"deltaEnabled": { "data": false },
"deltaMode": { "data": 0 },
"limitEnabled": { "data": true },
"limitDownwards": { "data": true },
"limitMinimumValue": { "data": -40 },
"limitUpwards": { "data": true },
"limitMaximumValue": { "data": 150 },
"thresholdComparisonEnabled": { "data": false },
"thresholdComparisonMode": { "data": 0 },
"thresholdComparisonValue": { "data": 0 },
"comparatorEnabled": { "data": false },
"comparatorLowerThreshold": { "data": 0 },
"comparatorUpperThreshold": { "data": 0 },
"comparatorOutputValueOff": { "data": 0 },
"comparatorOutputValueOn": { "data": 1 },
"comparatorTimerIntervalOff": { "data": 0 },
"comparatorTimerIntervalOn": { "data": 0 },
"edgeDetectionEnabled": { "data": false },
"edgeDetectionCountRising": { "data": false },
"edgeDetectionCountFalling": { "data": false },
"timeDerivativeEnabled": { "data": false },
"timeDerivativeTimeBase": { "data": 0 },
"aggregationEnabled": { "data": false },
"aggregationType": { "data": 2 },
"aggregationInterval": { "data": 60 },
"aggregationUpdateMode": { "data": 0 },
"postProcessingEnabled": { "data": false },
"postProcessingExpression": { "data": "x" }
}Synthetic signals are virtual I/O signals computed from other signals using arithmetic or logical expressions. The collection is a plain JSON array.
uuid(string, required) – unique identifiername(string, required) – display nameenabled(boolean, optional) – defaults totruesource1(object, required):unit(string) – source unit UUIDsignal(string) – source signal UUID
source2(object, required):unit(string) – second source unit UUIDsignal(string) – second source signal UUID
calculation(string, required) – the calculation expression. Supported values:"+","-","*","/"– arithmetic on A and B"&&","||"– logical AND / OR".."– latch (A sets to 1, B resets to 0)"+."– counter with reset (A = count, B = reset trigger)"++"– counter without reset (A = count)- Any other string – evaluated as a mathematical expression with variables
A,B,P(previous value) and access toMath.*functions
List all synthetic signals.
- Authentication: SystemAdministrator
- Response ``200 OK`` – JSON array
Get a single synthetic signal.
- Response ``200 OK`` – single object
- Response ``404 Not Found`` – not found
Create a synthetic signal.
Request body (JSON):
{ "name": "Flow Difference", "source1": { "unit": "unit-uuid-1", "signal": "sig-uuid-1" }, "source2": { "unit": "unit-uuid-2", "signal": "sig-uuid-2" }, "calculation": "-" }Responses:
200 OK– body is the new UUID400 Bad Request– invalid body
Modify a synthetic signal.
- Request body (JSON) – same schema as create
- Responses:
200 OK– empty body404 Not Found– not found
Replace all synthetic signals at once.
- Request body (JSON) – array of synthetic signal objects
- Responses:
200 OK– empty body400 Bad Request– invalid body
Delete a synthetic signal.
- Responses:
200 OK– empty body404 Not Found– not found
Enable/disable a synthetic signal.
- Request body:
{ "disabled": true }
Clone the calculation settings from one synthetic signal to another.
- Path parameters:
sourceUuid,destUuid - Responses:
200 OK– empty body404 Not Found– either signal not found
Propagate the name from the registry entry to the live I/O signal.
- Responses:
200 OK– empty body404 Not Found– signal not found
Update the registry name to match the live I/O signal name.
- Responses:
200 OK– empty body404 Not Found– signal not found500 Internal Server Error– registry update failed
The built-in Modbus server exposes TCP and RTU interfaces with configurable registers.
Read/write Modbus TCP server settings.
- Authentication: SystemAdministrator
- Configuration keys (
"data"sub-key pattern):enabled(boolean) – enable/disable the TCP servernetworkAddress(string) – listen address (default"0.0.0.0")networkPort(unsigned integer) – listen port (default502)
Read/write Modbus RTU slave settings.
- Authentication: SystemAdministrator
- Configuration keys:
enabled(boolean)rtuBusInterface(unsigned integer):0– serial port1– builtin RS485 interface2– backplane bus
rtuSerialPortName(string) – e.g."ttyUSB0"rtuBaudRate(unsigned integer) – e.g.115200rtuDataBits(unsigned integer) – 5, 6, 7, or 8rtuParity(unsigned integer):0– no parity2– even3– odd4– space5– mark
rtuStopBits(unsigned integer):1– 1 stop bit2– 2 stop bits3– 1.5 stop bits
modbusId(unsigned integer) – Modbus slave address (1–254)
Manage Modbus register mappings. The collection is a plain JSON array.
uuid(string, required) – unique identifiertype(integer) – Modbus register typeaddress(integer) – register addressdataType(integer, optional) – data type:0– unsigned small integer (1 register)1– signed small integer (1 register)2– unsigned integer (2 registers)3– signed integer (2 registers)4– float (2 registers)5– unsigned big integer (4 registers)6– signed big integer (4 registers)7– double (4 registers)
ioMode(integer) –0= read from signal,1= write to signalsignal(object):unit(string) – target unit UUIDsignal(string) – target signal UUID
Example register:
{
"uuid": "reg-uuid-1",
"type": 3,
"address": 0,
"dataType": 4,
"ioMode": 0,
"signal": { "unit": "unit-uuid-1", "signal": "sig-uuid-1" }
}Firewall rules are managed as four separate collections (all plain JSON arrays with move-up/move-down support) plus internet connection sharing settings.
uuid(string, required)enabled(boolean, optional, defaulttrue)networkProtocol(string) – protocol identifierinputInterface/outputInterface(string) – interface namesourceAddress/destinationAddress(string) – IP or CIDRdestinationPorts(string) – space-separated port listaction(string) – rule action (accept/drop)
GET /api/v1/firewall/rules/incoming– listGET /api/v1/firewall/rules/incoming/{uuid}– get onePOST /api/v1/firewall/rules/incoming– createPUT /api/v1/firewall/rules/incoming/{uuid}– modifyDELETE /api/v1/firewall/rules/incoming/{uuid}– deletePUT /api/v1/firewall/rules/incoming/{uuid}/up– move upPUT /api/v1/firewall/rules/incoming/{uuid}/down– move down
GET /api/v1/firewall/rules/outgoing– listGET /api/v1/firewall/rules/outgoing/{uuid}– get onePOST /api/v1/firewall/rules/outgoing– createPUT /api/v1/firewall/rules/outgoing/{uuid}– modifyDELETE /api/v1/firewall/rules/outgoing/{uuid}– deletePUT /api/v1/firewall/rules/outgoing/{uuid}/up– move upPUT /api/v1/firewall/rules/outgoing/{uuid}/down– move down
GET /api/v1/firewall/rules/ipforwarding– listGET /api/v1/firewall/rules/ipforwarding/{uuid}– get onePOST /api/v1/firewall/rules/ipforwarding– createPUT /api/v1/firewall/rules/ipforwarding/{uuid}– modifyDELETE /api/v1/firewall/rules/ipforwarding/{uuid}– deletePUT /api/v1/firewall/rules/ipforwarding/{uuid}/up– move upPUT /api/v1/firewall/rules/ipforwarding/{uuid}/down– move down
Additional fields for IP forwarding rules:
inputInterface(string)outputInterface(string)sourceAddress(string)destinationAddress(string)
GET /api/v1/firewall/rules/portforwarding– listGET /api/v1/firewall/rules/portforwarding/{uuid}– get onePOST /api/v1/firewall/rules/portforwarding– createPUT /api/v1/firewall/rules/portforwarding/{uuid}– modifyDELETE /api/v1/firewall/rules/portforwarding/{uuid}– deletePUT /api/v1/firewall/rules/portforwarding/{uuid}/up– move upPUT /api/v1/firewall/rules/portforwarding/{uuid}/down– move down
Additional fields for port forwarding rules:
localPort(string) – local port to forward todestinationPort(string) – destination portdestinationAddress(string) – target IPmasquerade(boolean, optional) – enable masquerade
Read internet connection sharing settings.
Authentication: SystemAdministrator
Response ``200 OK`` – JSON:
{ "enabled": true, "inputInterfaces": ["eth0", "wlan0"], "outputInterface": "wwan0" }
Write internet connection sharing settings.
Authentication: SystemAdministrator
Request body (JSON, max 100000 bytes):
{ "enabled": true, "inputInterfaces": ["eth0", "wlan0"], "outputInterface": "wwan0" }enabled(boolean, required)inputInterfaces(array of strings, required) – interfaces to share fromoutputInterface(string, required) – interface to share through. Must be one of:eth0,eth1,wlan0,wwan0,tun0,docker0
Responses:
200 OK– empty body400 Bad Request– invalid body or unknown interface
The alerting subsystem comprises three collections: signals (conditions to monitor), destinations (notification targets), and rules (bindings between signals and destinations). All are plain JSON arrays with disable support.
uuid(string, required)name(string, required)disabled(boolean, optional)evalMode(string, required) – one of:"binary""thresholds""counter""cycles"
evalParams(object, required):- For
binary:lowActive(boolean) - For
thresholds:comparison(string:"above","aboveOrEqual","below","belowOrEqual","equal","insideRange","outsideRange"),lowerThreshold(number),upperThreshold(number, for range comparisons) - For
counter:counterStepSize(number) - For
cycles:pulseThreshold(number)
- For
source(object, required):unit(string) – source I/O unit UUIDsignal(string) – source I/O signal UUID
cycleTime(number, optional) – timeout in seconds for counter/cycles modesstateTransition(object, optional):alarmDelayEnabled(boolean)alarmDelay(number, seconds)okDelayEnabled(boolean)okDelay(number, seconds)
severity(string, optional) –"none","low","medium","high"category(string, optional) – free-text category label
uuid(string, required)name(string, required)disabled(boolean, optional)type(string, required) – one of:"email","sms","webhook","app","iosignal","mqtt","vmetrics"Type-specific sub-objects:
email:recipients(string, comma-separated),subject(string),body(string)sms:recipients(string),text(string)webhook:url(string),body(string),method(string:"GET","POST","PUT","DELETE")app:appId(string)iosignal:unit(string),signal(string)mqtt:brokerAddress(string),brokerPort(integer),topic(string),data(string)vmetrics:metricName(string)
Placeholder tokens available in text fields:
{{NAME}},{{STATE}},{{VALUE}},{{SEVERITY}},{{CATEGORY}},{{CLASSIFICATION}},{{ID}},{{SOURCENAME}},{{SOURCEVALUE}},{{SOURCELOCATION}},{{HOSTNAME}},{{DEVICENAME}},{{DEVICELOCATION}}
uuid(string, required)name(string, required)disabled(boolean, optional)alertSignals(array, required) – array of{ "uuid": "<signal-uuid>" }objects, or[{ "uuid": "*" }]to match all signalsdestinations(array, required) – array of{ "uuid": "<destination-uuid>" }objectstriggers(object, required):ok(boolean) – trigger on OK statealarm(boolean) – trigger on alarm state
severityLevels(object, required):all(boolean) – match all severitiesnone(boolean) – include "no severity"low(boolean)medium(boolean)high(boolean)
repetition(integer, optional) – re-notification interval in minutes (0 or omitted = no repetition)
GET /api/v1/alerting/signals– listGET /api/v1/alerting/signals/{uuid}– get onePOST /api/v1/alerting/signals– createPUT /api/v1/alerting/signals/{uuid}– modifyDELETE /api/v1/alerting/signals/{uuid}– deletePUT /api/v1/alerting/signals/disable/{uuid}– enable/disable
GET /api/v1/alerting/destinations– listGET /api/v1/alerting/destinations/{uuid}– get onePOST /api/v1/alerting/destinations– createPUT /api/v1/alerting/destinations/{uuid}– modifyDELETE /api/v1/alerting/destinations/{uuid}– deletePUT /api/v1/alerting/destinations/disable/{uuid}– enable/disable
GET /api/v1/alerting/rules– listGET /api/v1/alerting/rules/{uuid}– get onePOST /api/v1/alerting/rules– createPUT /api/v1/alerting/rules/{uuid}– modifyDELETE /api/v1/alerting/rules/{uuid}– deletePUT /api/v1/alerting/rules/disable/{uuid}– enable/disable
All alerting endpoints require SystemAdministrator authentication.
License management for the SIINEOS platform and third-party applications.
List all installed licenses.
Authentication: none
Response ``200 OK`` – JSON array:
[ { "id": "lic-uuid-1", "productId": "de.inhub.siineos", "productName": "SIINEOS Hub", "productVersion": "1.2.3", "productSize": 512, "valid": true, "validFrom": "2024-01-01", "validUntil": "2027-01-01", "licensee": "ACME Corp" } ]
Import a license by uploading a license certificate file.
Authentication: SystemAdministrator
Request body – raw binary license certificate file (max 100000 bytes). The server parses the certificate and extracts license metadata internally. No JSON is sent by the client.
Responses:
200 OK– body is JSON with the imported license metadata:{ "id": "lic-uuid-1", "productId": "de.inhub.siineos", "productName": "SIINEOS Hub", "productVersion": "1.2.3", "productSize": 512, "valid": true, "validFrom": "2024-01-01", "validUntil": "2027-01-01", "licensee": "ACME Corp" }400 Bad Request– body too large409 Conflict– a license with the same ID is already installed415 Unsupported Media Type– the certificate is not valid422 Unprocessable Entity– the certificate is valid but the license is not accepted (e.g. expired)
Remove a license.
- Authentication: SystemAdministrator
- Path parameter:
licenseId(string) - Responses:
200 OK– empty body404 Not Found– license not found500 Internal Server Error– removal failed
Application control endpoints for managing installed apps on the hub.
Set a control property on an app.
Authentication: SystemAdministrator
Path parameters:
appId(string) – application identifiercontrolProperty(string) – one of:"enabled","debug","trace"
Request body – JSON boolean (max 10 bytes)
Example:
trueResponses:
200 OK– empty body400 Bad Request– body too large or invalid JSON404 Not Found– unknown control property
Health-check endpoint.
- Authentication: none
- Response ``200 OK`` – empty body
The API uses standard HTTP status codes. Error responses generally have an empty body or a short plain-text message.
400 Bad Request– malformed request body, missing required fields, body exceeds size limit, invalid parameter values, or a business-rule violation (e.g. deleting the protected admin account).Typical body: empty string or a short message such as
"upload file size too small"or"body length limit exceeded".401 Unauthorized– missing, malformed, or expired access token; or invalid credentials during login.Typical body:
"Authentication required"or empty.403 Forbidden– valid token but insufficient role (e.g. a non-admin user accessing an admin-only endpoint), or session ID mismatch.Typical body:
"Admin privileges required"or"Invalid access token".404 Not Found– the requested resource (user, unit, signal, license, session, etc.) does not exist.Typical body: empty string.
409 Conflict– a resource with the same unique key already exists (e.g. duplicate user login name, duplicate license ID, provider ID mismatch on unit config write).Typical body: empty string or the conflicting value.
415 Unsupported Media Type– the uploaded file is not a valid license certificate.422 Unprocessable Entity– the request is syntactically correct but semantically invalid (e.g. license certificate is well-formed but the license is expired or not accepted).500 Internal Server Error– an unexpected server-side failure (e.g. file I/O error, external service failure, database write failure).Typical body: empty string.
501 Not Implemented– the requested operation is not supported by the target unit or signal.Typical body:
"Not implemented".
- All JSON request bodies have a maximum size of 1 MiB unless otherwise stated (e.g. 4 MiB for unit config writes, 128 KiB for icon uploads, 256 MiB for update bundle chunks).
- UUIDs in the API are 32-character hexadecimal strings without dashes.
- Timestamps in query parameters for the time-series API are in milliseconds since epoch (13 digits).
- The
"data"sub-key pattern applies exclusively to configuration-object endpoints. Collection endpoints (users, firewall rules, alerting, I/O connections, synthetic signals, Modbus registers) use plain JSON objects without the wrapper.