MQTT contract

Devices talk to the platform over MQTT. Topics are scoped by organization and device, messages are JSON, and identity is proven by a per-device certificate.

Topic taxonomy

Topics follow the pattern v1/{type}/{orgId}/{deviceId}[/...]:

TopicDirectionQoSRetained
v1/readings/{orgId}/{deviceId}device → platform1no
v1/gateways/{orgId}/{deviceId}/commandsplatform → device1no
v1/gateways/{orgId}/{deviceId}/commands/responsedevice → platform1no
v1/gateways/{orgId}/{deviceId}/healthdevice → platform0no
v1/gateways/{orgId}/{deviceId}/statusdevice → platform1yes (last will)
v1/gateways/{orgId}/{deviceId}/config/reporteddevice → platform1yes

Messages

All payloads are JSON. A reading carries a sensorId, an ISO-8601 UTC timestamp, a value, and optional metadata; readings may be batched as a JSON array. The value's JSON type determines its storage type — number, integer, boolean, or string. Commands follow an RPC-style shape (id, method, params, timestamp) with a matching response (id, status, result).

Practical limits apply: a sensorId up to 36 characters, string values up to 1000 characters, metadata up to 10 KB, and a message up to 256 KB. Readings are de-duplicated on (timestamp, organization, sensor).

Example payloads

Reading — published to v1/readings/{orgId}/{deviceId}. The organization comes from the topic, never the body.

{
  "sensorId": "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
  "timestamp": "2024-01-15T10:30:00.123Z",
  "value": 23.5,
  "metadata": {}
}

Readings can be batched as a JSON array:

[
  { "sensorId": "s1a2b3c4-…", "timestamp": "2024-01-15T10:30:00.000Z", "value": 23.5 },
  { "sensorId": "s1a2b3c4-…", "timestamp": "2024-01-15T10:30:05.000Z", "value": 23.6 }
]

Command — received on v1/gateways/{orgId}/{deviceId}/commands (RPC-style):

{
  "id": "cmd-123456",
  "method": "REBOOT",
  "params": { "delay_seconds": 5 },
  "timestamp": "2024-01-15T10:30:00Z"
}

Command response — published to …/commands/response, correlated by id:

{
  "id": "cmd-123456",
  "status": "success",
  "errorCode": 0,
  "result": { "message": "Rebooting in 2000 ms" }
}

status is one of accepted, running, success, error, or rejected.

Health — published to …/health:

{
  "uptimeSec": 3600,
  "freeHeap": 125000,
  "minFreeHeap": 98000,
  "rssi": -67,
  "mqttConnected": true,
  "readingsSent": 1250,
  "readingsDropped": 0
}

Status — retained on …/status. The device publishes online on connect; the broker publishes offline as the last will if the device drops:

{ "online": true, "firmwareVersion": "0.1.0" }
{ "online": false }

Security

Devices connect over TLS on port 8883 and authenticate with mutual TLS — the broker derives the device identity from the certificate. The organization in the topic is the source of truth; any organization field inside a message body is ignored. A device subscribes only to its own topics.

Ingestion

Published readings flow from the broker through an internal queue to a dedicated ingestion worker, which validates and writes them to the time-series database. See How it works for the full path.

Was this page helpful?