OTA#
The OTA service rolls out firmware to things over the air. A user creates an update for a set of things in a group, the service notifies those things, and each thing reports its progress until it runs the new firmware or fails. Firmware files are stored by the Filestore service as group files.
Updates#
An update describes the firmware and the things that should install it.
| Field | Description |
|---|---|
id |
Unique update identifier |
group_id |
ID of the group of the things being updated |
file_name |
Name of the firmware group file in the Filestore service |
version |
Version that things must report on success |
file_size |
Firmware size in bytes |
checksum |
Hex-encoded SHA-256 of the firmware, used by the thing to verify the download |
description |
Optional free-form description |
timeout |
Seconds without progress after which a thing is marked failed (default 24 hours) |
created_at |
Creation timestamp (RFC 3339) |
stats |
Number of things of the update per status, and the total number of target things |
Each thing of an update has its own progress: status, error, reported_version and updated_at.
| Status | Meaning |
|---|---|
pending |
The thing has not started the update yet |
downloading |
The thing is downloading the firmware |
installing |
The thing is installing the firmware |
succeeded |
The thing reported the version of the update |
failed |
The thing reported an error or another version, timed out, or was superseded |
How it works#
- Create. A user creates an update for things of a group. Every thing starts as
pending. A thing runs at most one update at a time: its older running update is markedfailedwithsuperseded by update <id>. - Notify. The service publishes the update on the
things/<thing_id>/ota/updateMQTT topic of each thing. Only the OTA service can publish on that topic. Things that were offline fetch the update on reconnect withGET /ota/pending. - Download. The thing downloads the firmware from the
urlof the update with its thing key, resuming with HTTP Range requests if needed (see Filestore), and verifies the SHA-256 checksum. - Report. The thing reports its progress with
POST /ota/status, or by publishing on itsthings/<thing_id>/ota/statusMQTT topic. A thing can report only its own status. Reports reach only the OTA service; they are not stored and do not trigger rules or webhooks. Asucceededreport must includeversion; if it differs from the version of the update, the thing is markedfailed. - Timeout. Things without progress for the
timeoutof the update are markedfailedwithtimed out. The service checks this everyMF_OTA_TIMEOUT_CHECK_INTERVAL. - Retry. A user can move the failed things of an update back to
pending, and they are notified again. Things that received a newer update in the meantime are skipped, so a retry never rolls a thing back to older firmware. The service never retries on its own.
Viewing updates requires the viewer role in the group, while creating, retrying and removing updates require the editor role. Updates and progress are removed when their group or thing is removed.
Managing updates#
Create an update#
curl -s -S -i -X POST \
-H "Authorization: Bearer <user_token>" \
-H "Content-Type: application/json" \
-d '{
"file_name": "fw-sensor-1.4.0.bin",
"version": "1.4.0",
"file_size": 421888,
"checksum": "<sha256_hex>",
"description": "Sensor firmware 1.4.0",
"thing_ids": ["111e4567-e89b-12d3-a456-426614174000"]
}' \
https://localhost/svcota/groups/<group_id>/updates
The response contains the created update with 201 Created.
List updates of a group#
curl -s -S -i \
-H "Authorization: Bearer <user_token>" \
"https://localhost/svcota/groups/<group_id>/updates?offset=0&limit=10"
{
"total": 1,
"offset": 0,
"limit": 10,
"updates": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"group_id": "211e4567-e89b-12d3-a456-426614174000",
"file_name": "fw-sensor-1.4.0.bin",
"version": "1.4.0",
"file_size": 421888,
"checksum": "<sha256_hex>",
"description": "Sensor firmware 1.4.0",
"timeout": 86400,
"created_at": "2026-11-04T12:00:00Z",
"stats": {
"targets": 1,
"pending": 0,
"downloading": 1,
"installing": 0,
"succeeded": 0,
"failed": 0
}
}
]
}
View an update#
curl -s -S -i \
-H "Authorization: Bearer <user_token>" \
https://localhost/svcota/updates/<update_id>
List the things of an update#
The status query parameter filters the things by status.
curl -s -S -i \
-H "Authorization: Bearer <user_token>" \
"https://localhost/svcota/updates/<update_id>/things?status=failed"
{
"total": 1,
"offset": 0,
"limit": 10,
"status": "failed",
"thing_updates": [
{
"thing_id": "111e4567-e89b-12d3-a456-426614174000",
"status": "failed",
"error": "timed out",
"updated_at": "2026-11-05T12:00:00Z"
}
]
}
Retry failed things#
curl -s -S -i -X POST \
-H "Authorization: Bearer <user_token>" \
https://localhost/svcota/updates/<update_id>/retry
Remove an update#
curl -s -S -i -X DELETE \
-H "Authorization: Bearer <user_token>" \
https://localhost/svcota/updates/<update_id>
Returns 204 No Content.
Device API#
Things authenticate with their thing key.
Fetch the pending update#
curl -s -S -i \
-H "Authorization: Thing <thing_key>" \
https://localhost/ota/pending
When an update is pending, the response is:
{
"update_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"version": "1.4.0",
"file_name": "fw-sensor-1.4.0.bin",
"url": "https://localhost/fs/groupfiles/fw-sensor-1.4.0.bin",
"size": 421888,
"checksum": "<sha256_hex>"
}
When nothing is pending, the response is 204 No Content. Over MQTT, the same object is delivered under the payload field of the standard command message.
Report progress#
status is one of downloading, installing, succeeded or failed. version is required with succeeded, and error describes a failure.
curl -s -S -i -X POST \
-H "Authorization: Thing <thing_key>" \
-H "Content-Type: application/json" \
-d '{"update_id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "status": "succeeded", "version": "1.4.0"}' \
https://localhost/ota/status
Configuration#
MF_OTA_FIRMWARE_URL sets the public base URL from which things download firmware group files (default https://localhost/fs/groupfiles), and MF_OTA_TIMEOUT_CHECK_INTERVAL sets how often things without progress are checked for timeout (default 10m). See the OTA service README for the full list of environment variables.
For the full API reference, see the API documentation.