Overview
One client-mode connector, Balena, for balenaCloud (https://api.balena-cloud.com). It covers two APIs that share the same host and the same API key:
- the balena API v7, an OData interface over fleets (called
applicationin the API), devices, releases, tags and variables; - the Supervisor API proxy (
/supervisor/...), which forwards a command to the balena Supervisor on one device, identified by its UUID.
Topics use
cloud-iot/balena/<object>/<verb>; replies land on <topic>/response. For openBalena, change BASE_ADDRESS to your own API host, for example https://api.balena.example.com.
Authentication
Every event sendsAuthorization: Bearer <key>. Use a named API key: in the balenaCloud dashboard open Preferences, then Access tokens, and create an API key, or run balena api-key generate coreflux with the balena CLI. Session tokens also work but expire.
Pass the key as api_key on every payload. A missing or invalid key returns 401 on device, tag, variable, fleet-write and Supervisor events. Fleets and releases are publicly readable resources, so without a valid key GetFleet and ListReleases return 200 with an empty d list for private fleets, and ListFleets returns 400 Missing binding: @__ACTOR_ID. Treat an empty d on those events as a possible auth problem.
Basic Syntax
MQTT examples
Request bodies go underbody, so control fields like api_key, uuid and fleet_slug are not sent to balena.
Find the devices of a fleet: publish to cloud-iot/balena/device/list:
{"d": [ ... ]}. Values inside odata must be URL-encoded: write spaces as %20.
Check one device: publish {"api_key": "<key>", "uuid": "<device uuid>"} to cloud-iot/balena/device/get. The reply includes is_online, overall_status, CPU temperature, memory and storage use, and the running, pinned and target releases. Use the full 32-character UUID.
Pin a device to a release: publish to cloud-iot/balena/device/update:
{"is_pinned_on__release": null} to make the device track its fleet again. The same event renames a device ({"device_name": "press-07"}) or sets a note ({"note": "line 1 maintenance"}). A successful PATCH returns 200 with the text OK.
Pin the whole fleet: publish to cloud-iot/balena/fleet/update:
cloud-iot/balena/release/list with {"api_key": "<key>", "fleet_id": 1234}, and the fleet id from cloud-iot/balena/fleet/get.
Tag a device with its UNS location: publish to cloud-iot/balena/device-tag/create:
device is the numeric device id from GetDevice, not the UUID. Change an existing tag with cloud-iot/balena/device-tag/update and {"device_id": 1234567, "tag_key": "uns_path", "body": {"value": "..."}}; creating the same key twice returns 409.
Set a device environment variable: publish to cloud-iot/balena/device-env/create with {"api_key": "<key>", "body": {"device": 1234567, "name": "MQTT_BROKER", "value": "mqtt://broker.local:1883"}}. The services on that device restart to pick up the new value.
Restart one service on a device: publish to cloud-iot/balena/supervisor/restart-service:
{"uuid": "<device uuid>"} as the body: blink and reboot as is, SupervisorRestartApp with "data": {"appId": 1234}, SupervisorUpdate with "data": {"force": true} and SupervisorDeviceState with "method": "GET". The device must be online and connected to the balena VPN.
Helper actions
Keep the API key in a broker secret, poll fleet health on a timer, and let operators or alarms trigger restarts and release pins with small payloads:BalenaPollFleetHealth publishes the device list of coreflux/line-1 to cloud-iot/balena/device/list/response every five minutes; feed it into a model or dashboard. Restrict who can publish to ops/balena/# and read cloud-iot/balena/# with a rule: these topics can reboot devices and their payloads carry the API key.
Notes
- Errors come back on the response topic:
401for a missing or invalid key,400for a bad OData query or unknown field, an emptydlist when a read matches no device or fleet,409for a duplicate tag key or variable name, and429with aRetry-Afterheader when rate limited. Supervisor events fail when the device is offline or off the VPN. GetDevicenamesoverall_statusin$selecton purpose: balena only returns that field when it is requested.- Fleet slugs look like
org/fleetand go inside the quotes as is. - The Supervisor restart-service call through the cloud proxy follows the documented proxy pattern (
/supervisor/<path>withuuidanddata); balena documents the local form of that endpoint. - Deleting devices, fleets, tags or variables, moving devices between fleets, OS and Supervisor upgrades, service and fleet variables, and public device URLs are not part of this template.

