Skip to content

Deployments

A deployment is the record that pairs a specific firmware artifact with a specific device and tracks the update from the moment the server queues it until the device confirms success or failure. Every time you push a firmware version to one or more devices, the server creates one FirmwareDeployment row per device.

POST /firmware/{id}/deploy always requires an explicit list of target devices: pass device_ids with at least one device UUID. A request with an empty device_ids list, or without the field, is rejected with HTTP 422. The server never selects devices on its own, so one request can never reach the whole fleet by accident.

If the firmware has min_version set, devices below that version are skipped unless the caller passes force: true (requires owner role or above).

There are two ways to limit how many devices an update reaches. Both go on the POST /firmware/{id}/deploy request. In the console, both are options under Rollout Strategy in the deploy dialog.

Pass rollout_percentage (0–100) to limit the deployment to a random sample of the devices you listed. For example, rollout_percentage: 10 creates deployments for 10% of them. Run the same call again with a higher percentage to widen the rollout. This is a one-shot sampling. It does not expand over time.

Pass a rollout object to deploy in batches. The server creates one deployment per device up front. It releases them one batch at a time and advances on its own.

{
"device_ids": ["<device-uuid>", "<device-uuid>"],
"rollout": {
"batch_percent": 20,
"batch_interval_s": 3600,
"max_failure_percent": 10
}
}
FieldMeaning
batch_sizeDevices per batch (integer, at least 1).
batch_percentDevices per batch as a percentage of the devices the server actually deploys to (greater than 0, up to 100). Rounded down to a whole number, with a minimum of one device.
batch_interval_sSeconds to wait before moving on. Default 0.
max_failure_percentPause the rollout when more than this share of a batch has failed. Default 10.

Set exactly one of batch_size and batch_percent. The request is rejected with HTTP 422 if you set both or neither. The console offers only batch_percent. It takes the interval in minutes and sends it as batch_interval_s.

Devices are batched in the order you listed them in device_ids. The last batch holds the remainder and can be smaller than the others. If you also pass rollout_percentage, the random sample is taken first and only the sampled devices are batched.

The response to the deploy call includes rollout_group_id and batch_count. Every deployment in the rollout also carries rollout_group_id and rollout_batch (the batch number, starting at 0).

How it advances. The first batch starts immediately. The server checks the active rollouts about every 10 seconds. The current batch counts as done when every deployment in it has finished (installed, failed or rolled_back). If batch_interval_s is above 0, the batch also counts as done once that many seconds have passed since it started, even if devices are still working. When a batch is done, the server activates the next one. After the last batch, the rollout is completed.

Automatic pause. When a batch is done, the server counts its failed and rolled_back deployments. If that share is above max_failure_percent, the rollout moves to paused and records the reason. A paused rollout never resumes by itself.

Controlling a rollout. These calls take organization_id as a query parameter. They need the member role or above:

CallEffect
GET /api/rollouts/{id}Returns state (active, paused, completed or aborted), batch_index, batch_count, params and paused_reason.
POST /api/rollouts/{id}/pauseStops the rollout from starting further batches. Use it on an active rollout. Devices already in flight are not touched.
POST /api/rollouts/{id}/resumeUse it on a paused rollout. You accept the failures of the batch that paused it. The server starts the next batch, or completes the rollout if that was the last one.
POST /api/rollouts/{id}/abortUse it on an active or paused rollout. Deployments still scheduled become failed with the message “rollout aborted”. The batch already running finishes on its own. A completed rollout cannot be aborted (HTTP 422).

The console shows the same state, batch progress, paused reason and Pause, Resume and Abort buttons on the Deployments tab of the Firmware page.

A paused rollout stays paused until someone resumes or aborts it. This is different from pausing a single deployment, which expires after one hour (see Pause, resume, and cancel).

Each deployment record carries a status field. The device’s OTA agent reports transitions back via POST /v1/ota/status/{deployment_id}.

StatusMeaning
scheduledThe deployment belongs to a later batch of a batched rollout that has not started yet. The device cannot see it. The status becomes pending when its batch starts. If the rollout is aborted first, it becomes failed.
pendingServer created the deployment; device has not started yet. The heartbeat response will advertise an available update.
downloadingDevice acknowledged the update and is transferring the artifact.
downloadedArtifact transfer complete; device has the file locally but has not started writing yet.
installingDevice is writing the artifact to the inactive partition.
installedDevice successfully applied the update and rebooted into the new firmware. The device’s firmware_version is updated to the new version.
failedThe update did not complete successfully. The error_message field contains a description.
rolled_backAn operator triggered a rollback after the update reached installing, installed, or failed.

Operators with at least member role can intervene while a deployment is in flight.

Pause — available from pending or downloading. The server inserts a pause control directive that expires after 1 hour; the paused_at timestamp is recorded. A pause is a temporary hold, not an indefinite stop: when the hour is up the server stops sending the directive and the agent continues the update on its own. The console shows the expected auto-resume time next to the Paused badge. To stop an update for longer than an hour, cancel it instead.

Resume — clears the pause directive and paused_at. The agent receives a continue directive on its next poll.

Cancel — available at any status. Sets deployment to failed (“Cancelled by operator”) and inserts force_cancel. Cancelling an already-failed deployment is a no-op.

Only allowed from failed. The deployment resets to pending, retry_count increments, and the error is cleared. Maximum five retries; further attempts return 422.

Allowed from installing, installed, or failed. The server sets status = rolled_back and inserts a force_cancel directive. The agent receives the directive on its next poll and uses the bootloader to revert to the previously active partition. Already-rolled-back deployments are a no-op.

Every pause, resume, cancel, retry, and rollback action is written to the audit log with the operator’s user ID and a timestamp.

Every deployment ships an artifact — see Artifacts for the file format and signing requirements.