Skip to main content
Technical Preview

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 application in 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.
Use it to let the broker manage the edge gateways it runs next to: watch device health, restart a stuck container when a line alarm fires, pin a fleet to a known-good release before a shift, or tag devices with their UNS location. 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.
Never commit an API key. Store it as a broker secret (for example BALENA_API_KEY) and map it into payloads with GET SECRET. A balena API key acts with your full user permissions, and the API has no undo: test every $filter with a GET before you use it in a PATCH.

Authentication

Every event sends Authorization: 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 under body, 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:
The reply is {"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:
Send {"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:
Get release ids from 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:
The other Supervisor events take {"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: 401 for a missing or invalid key, 400 for a bad OData query or unknown field, an empty d list when a read matches no device or fleet, 409 for a duplicate tag key or variable name, and 429 with a Retry-After header when rate limited. Supervisor events fail when the device is offline or off the VPN.
  • GetDevice names overall_status in $select on purpose: balena only returns that field when it is requested.
  • Fleet slugs look like org/fleet and go inside the quotes as is.
  • The Supervisor restart-service call through the cloud proxy follows the documented proxy pattern (/supervisor/<path> with uuid and data); 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.
Last modified on October 21, 2026