Docs/Apio IoT Platform/Platform core

Downlink

Sending downlink messages to devices (or other applications) allows to apply configurations or send signals to one or multiple devices (or applications). It can be used, for example, to adjust thermostats when temperature values change or create other more complex scenarios, such as set-point activation in energy communities.

Commands

Within Apio IoT, downlink messages are represented as Commands, full fledged resources with their own REST endpoints, the structure of a command is the following:

JSON
{
  "uuid": "810b7efb-f648-47eb-b03b-8edf290e0a27",
  "name": "reboot",
  "projectId": "my-project-id",
  "deviceId": "my-device-1",
  "status": "pending",
  "parameters": {
    "wait": "8s"
  },
  "metadata": {},
  "createdAt": "2026-10-01T10:00:00.000Z",
  "updatedAt": "2026-10-01T10:00:00.000Z"
}

In this example, we have a command named reboot, with a single parameter, named wait holding a value equal to the string 8s. A command targets either a device (deviceId) or a node (nodeId).

The status field tracks the command’s progress:

StatusMeaningAlso set
pendingThe command was created and sent. Every new command starts here
receivedThe device acknowledged the commandreceivedAt
completedThe device executed the commandcompletedAt
failedThe device could not execute the command

Devices report the status by acknowledging the command.

Sending Commands

To a device

To send a command to a device, use the REST API:

POSThttps://api.apio.network/projects/{projectId}/devices/{deviceId}/commands

Body:

JSON
{
  "name":"reboot",
  "parameters":{
    "wait":"8s"
  }
}

The response contains the new command, with its uuid and status: "pending". The command is delivered immediately to the device over MQTT.

The commands a device accepts, with their parameters, can be described in its device type.

To a node

Commands can also target a node, such as a gateway:

POSThttps://api.apio.network/projects/{projectId}/nodes/{nodeId}/commands

The body is the same as for devices.

Receiving Commands

Info

Downlink messages are currently supported only for the MQTT protocol.

To receive commands, subscribe to one of the MQTT topics below. Subscribing requires the apio.core.commands.read permission.

TopicReceives
apio/core/projects/{projectId}/devices/{deviceId}/commands/downlinkCommands for one device
apio/core/projects/{projectId}/nodes/{nodeId}/commands/downlinkCommands for one node
apio/core/projects/{projectId}/commands/downlinkEvery command of the project

Every topic is also available in a shorter form, convenient for constrained devices: apio/core/p/{projectId}/d/{deviceId}/commands/downlink, apio/core/p/{projectId}/n/{nodeId}/commands/downlink and apio/core/p/{projectId}/commands/downlink.

The payload is a JSON string with the command:

JSON
{
  "uuid": "810b7efb-f648-47eb-b03b-8edf290e0a27",
  "name": "reboot",
  "projectId": "my-project-id",
  "deviceId": "my-device-1",
  "status": "pending",
  "parameters": {
    "wait": "8s"
  },
  "metadata": {},
  "createdAt": "2026-10-01T10:00:00.000Z",
  "updatedAt": "2026-10-01T10:00:00.000Z"
}
Warning

Commands are delivered only to clients that are connected and subscribed when the command is sent; they are not queued for offline clients. When a device reconnects, it can fetch the commands it missed with GET /projects/{projectId}/devices/{deviceId}/commands?status=pending.

Acknowledging Commands

A device reports the progress of a command by publishing to the acknowledgement topic that matches the topic it received the command on, with /ack appended:

TopicUse for
apio/core/projects/{projectId}/devices/{deviceId}/commands/downlink/ackDevice commands
apio/core/projects/{projectId}/nodes/{nodeId}/commands/downlink/ackNode commands
apio/core/projects/{projectId}/commands/downlink/ackAny command of the project

The short forms (apio/core/p/...) are accepted here too. Publishing requires the apio.core.commands.write permission.

The payload contains the command uuid and the new status. When status is omitted, the command is marked as received. Any other field is stored in the command’s metadata.ack, so the device can return the result of the operation:

JSON
{
  "uuid": "810b7efb-f648-47eb-b03b-8edf290e0a27",
  "status": "completed",
  "result": "rebooted in 6s"
}

A typical device acknowledges a command twice: with received as soon as it gets it, and with completed or failed when it is done.

Applications that are not connected over MQTT can update the status through the REST API, with PUT /projects/{projectId}/commands/{commandId}.

Tracking Commands

Read a command to check its status, receivedAt, completedAt and the acknowledgement in metadata.ack:

GEThttps://api.apio.network/projects/{projectId}/commands/{commandId}

List the commands sent to a device or a node, optionally filtering by status:

GEThttps://api.apio.network/projects/{projectId}/devices/{deviceId}/commands?status=failed
GEThttps://api.apio.network/projects/{projectId}/nodes/{nodeId}/commands

Platform Commands

Besides the commands you define, the platform sends two commands of its own. Devices that support these features must handle them.

CommandSent whenParameters
firmwareReleaseA firmware release targets the devicefirmwareId, versionNumber, fileUrl, timestamp
credentialsRotationThe device’s credentials are rotatedsecret, expire, rotatedCredentialsId

Rotating Credentials

Devices and nodes authenticate with API keys. To replace the key of a device without touching it physically, rotate it:

POSThttps://api.apio.network/projects/{projectId}/devices/{deviceId}/credentials/{credentialId}/rotate
POSThttps://api.apio.network/projects/{projectId}/nodes/{nodeId}/credentials/{credentialId}/rotate

credentialId is the id of the API key currently used by the device. The platform:

  1. Creates a new API key with the same permissions, expiring one year after the current one. Its name is the old name followed by (rotated-<date>).
  2. Sends the device a credentialsRotation command. parameters.secret holds the new key, parameters.expire its expiration date and parameters.rotatedCredentialsId the id of the key being replaced.

The device should store the new key, acknowledge the command and reconnect with it. The old key stays valid until it expires or is deleted: once the device has switched, delete the old key. Deleting a key disconnects any MQTT client still using it.

Note

The new key is sent only once, inside the command. In the copy stored by the platform, parameters.secret is masked.

Type to search guides and API endpoints.