curl "https://api.bota.dev/v1/devices/dev_abc123/ota/history?limit=5" \
-H "Authorization: Bearer sk_live_..."
const { data: history } = await fetch(
'https://api.bota.dev/v1/devices/dev_abc123/ota/history?limit=5',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
const failures = history.filter((a) => a.status === 'failed');
import requests
history = requests.get(
'https://api.bota.dev/v1/devices/dev_abc123/ota/history',
params={'limit': 5},
headers={'Authorization': 'Bearer sk_live_...'},
).json()['data']
failures = [a for a in history if a['status'] == 'failed']
{
"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
},
{
"id": "ota_2XvB4nT6yL8cW1kQ9mZa3Rp7",
"device_id": "dev_abc123",
"firmware_release_id": "fw_9mZa3Rp2XvB4nT6yL8cW1kQ8",
"status": "cancelled",
"assigned_at": "2026-08-12T09:40:00Z",
"delivered_at": null,
"applied_at": null,
"error_message": null
}
]
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Firmware
List Device OTA History
Every firmware assignment a device has had, newest first
GET
/
devices
/
{id}
/
ota
/
history
curl "https://api.bota.dev/v1/devices/dev_abc123/ota/history?limit=5" \
-H "Authorization: Bearer sk_live_..."
const { data: history } = await fetch(
'https://api.bota.dev/v1/devices/dev_abc123/ota/history?limit=5',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
const failures = history.filter((a) => a.status === 'failed');
import requests
history = requests.get(
'https://api.bota.dev/v1/devices/dev_abc123/ota/history',
params={'limit': 5},
headers={'Authorization': 'Bearer sk_live_...'},
).json()['data']
failures = [a for a in history if a['status'] == 'failed']
{
"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
},
{
"id": "ota_2XvB4nT6yL8cW1kQ9mZa3Rp7",
"device_id": "dev_abc123",
"firmware_release_id": "fw_9mZa3Rp2XvB4nT6yL8cW1kQ8",
"status": "cancelled",
"assigned_at": "2026-08-12T09:40:00Z",
"delivered_at": null,
"applied_at": null,
"error_message": null
}
]
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Every assignment this device has had, newest first — including the ones that
were applied, failed, or called off. Use it to answer “what has this device been
through” rather than “what is happening now”.
Authentication
Requires an API key withdevices:read scope.
Path Parameters
string
required
The device’s unique identifier (e.g.,
dev_abc123).Query Parameters
number
default:"10"
How many assignments to return, 1–100.
curl "https://api.bota.dev/v1/devices/dev_abc123/ota/history?limit=5" \
-H "Authorization: Bearer sk_live_..."
const { data: history } = await fetch(
'https://api.bota.dev/v1/devices/dev_abc123/ota/history?limit=5',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
const failures = history.filter((a) => a.status === 'failed');
import requests
history = requests.get(
'https://api.bota.dev/v1/devices/dev_abc123/ota/history',
params={'limit': 5},
headers={'Authorization': 'Bearer sk_live_...'},
).json()['data']
failures = [a for a in history if a['status'] == 'failed']
{
"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
},
{
"id": "ota_2XvB4nT6yL8cW1kQ9mZa3Rp7",
"device_id": "dev_abc123",
"firmware_release_id": "fw_9mZa3Rp2XvB4nT6yL8cW1kQ8",
"status": "cancelled",
"assigned_at": "2026-08-12T09:40:00Z",
"delivered_at": null,
"applied_at": null,
"error_message": null
}
]
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Response Fields
Each entry has the same shape as Get Device OTA Status, where thestatus
values are also documented.
Notes
failedcovers two different things: the device reported a failure, and the assignment was settled because the device moved between projects. Where a reason exists it is inerror_message.cancelledmeans an operator called the assignment off. It is deliberately distinct fromfailedso a real failure is not confused with a decision.
Was this page helpful?

