curl https://api.bota.dev/v1/devices/dev_abc123/ota \
-H "Authorization: Bearer sk_live_..."
const { data: assignment } = await fetch(
'https://api.bota.dev/v1/devices/dev_abc123/ota',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
if (!assignment) {
// Never assigned firmware.
} else if (assignment.status === 'applied') {
// The update is installed.
} else if (assignment.status === 'pending' || assignment.status === 'delivered') {
// Still in flight.
}
import requests
assignment = requests.get(
'https://api.bota.dev/v1/devices/dev_abc123/ota',
headers={'Authorization': 'Bearer sk_live_...'},
).json()['data']
if assignment is None:
... # Never assigned firmware.
elif assignment['status'] == 'applied':
... # The update is installed.
{
"data": {
"id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
"device_id": "dev_abc123",
"firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
"status": "delivered",
"assigned_at": "2026-08-14T12:00:00Z",
"delivered_at": "2026-08-14T12:01:30Z",
"applied_at": null,
"error_message": null
}
}
{
"data": {
"id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
"device_id": "dev_abc123",
"firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
"status": "applied",
"assigned_at": "2026-08-14T12:00:00Z",
"delivered_at": "2026-08-14T12:01:30Z",
"applied_at": "2026-08-14T12:06:12Z",
"error_message": null
}
}
{
"data": null
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Firmware
Get Device OTA Status
The device’s most recent firmware assignment and what became of it
GET
/
devices
/
{id}
/
ota
curl https://api.bota.dev/v1/devices/dev_abc123/ota \
-H "Authorization: Bearer sk_live_..."
const { data: assignment } = await fetch(
'https://api.bota.dev/v1/devices/dev_abc123/ota',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
if (!assignment) {
// Never assigned firmware.
} else if (assignment.status === 'applied') {
// The update is installed.
} else if (assignment.status === 'pending' || assignment.status === 'delivered') {
// Still in flight.
}
import requests
assignment = requests.get(
'https://api.bota.dev/v1/devices/dev_abc123/ota',
headers={'Authorization': 'Bearer sk_live_...'},
).json()['data']
if assignment is None:
... # Never assigned firmware.
elif assignment['status'] == 'applied':
... # The update is installed.
{
"data": {
"id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
"device_id": "dev_abc123",
"firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
"status": "delivered",
"assigned_at": "2026-08-14T12:00:00Z",
"delivered_at": "2026-08-14T12:01:30Z",
"applied_at": null,
"error_message": null
}
}
{
"data": {
"id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
"device_id": "dev_abc123",
"firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
"status": "applied",
"assigned_at": "2026-08-14T12:00:00Z",
"delivered_at": "2026-08-14T12:01:30Z",
"applied_at": "2026-08-14T12:06:12Z",
"error_message": null
}
}
{
"data": null
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Returns the device’s most recent assignment, whatever became of it. Read
status to see whether the update landed.
data is null only when the device has never been assigned firmware.
Authentication
Requires an API key withdevices:read scope.
Path Parameters
string
required
The device’s unique identifier (e.g.,
dev_abc123).curl https://api.bota.dev/v1/devices/dev_abc123/ota \
-H "Authorization: Bearer sk_live_..."
const { data: assignment } = await fetch(
'https://api.bota.dev/v1/devices/dev_abc123/ota',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
if (!assignment) {
// Never assigned firmware.
} else if (assignment.status === 'applied') {
// The update is installed.
} else if (assignment.status === 'pending' || assignment.status === 'delivered') {
// Still in flight.
}
import requests
assignment = requests.get(
'https://api.bota.dev/v1/devices/dev_abc123/ota',
headers={'Authorization': 'Bearer sk_live_...'},
).json()['data']
if assignment is None:
... # Never assigned firmware.
elif assignment['status'] == 'applied':
... # The update is installed.
{
"data": {
"id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
"device_id": "dev_abc123",
"firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
"status": "delivered",
"assigned_at": "2026-08-14T12:00:00Z",
"delivered_at": "2026-08-14T12:01:30Z",
"applied_at": null,
"error_message": null
}
}
{
"data": {
"id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
"device_id": "dev_abc123",
"firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
"status": "applied",
"assigned_at": "2026-08-14T12:00:00Z",
"delivered_at": "2026-08-14T12:01:30Z",
"applied_at": "2026-08-14T12:06:12Z",
"error_message": null
}
}
{
"data": null
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Assignment identifier (ota_*) |
device_id | string | The device this was assigned to |
firmware_release_id | string | The release that was assigned |
status | string | See the table below |
assigned_at | string | ISO 8601 |
delivered_at | string | null | When the device was handed the update |
applied_at | string | null | When the device reported running the assigned version |
error_message | string | null | Why it failed, when status is failed |
Assignment status
| Status | Meaning |
|---|---|
pending | Assigned. The device has not been handed the update yet. |
delivered | The device received the update in a heartbeat and is installing it. |
applied | The device reported running the assigned version. Done. |
failed | The device reported a failure, or the assignment was settled when the device moved between projects. Read error_message. |
cancelled | Called off through Cancel Device OTA Assignment. |
pending and delivered are the in-flight states, and either one blocks a new
assignment. The other three are terminal.
Notes
- This is one assignment, not a history. Use List Device OTA History to see earlier ones.
- A device reports its firmware version in every heartbeat; when it matches the
assigned release, the assignment moves to
appliedon its own.
Was this page helpful?

