curl "https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url" \
-H "Authorization: Bearer sk_live_..."
import { createHash } from 'node:crypto';
const meta = await fetch(
'https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
const image = Buffer.from(await fetch(meta.download_url).then((r) => r.arrayBuffer()));
if (createHash('sha256').update(image).digest('hex') !== meta.sha256) {
throw new Error('Firmware image did not match its checksum');
}
import hashlib
import requests
meta = requests.get(
'https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url',
headers={'Authorization': 'Bearer sk_live_...'},
).json()
image = requests.get(meta['download_url']).content
if hashlib.sha256(image).hexdigest() != meta['sha256']:
raise ValueError('Firmware image did not match its checksum')
{
"download_url": "https://bota-firmware-prod.s3.us-west-2.amazonaws.com/...&X-Amz-Expires=3600",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"file_size_bytes": 1048576,
"expires_at": "2026-08-14T11:02:00Z"
}
{
"error": {
"code": "not_found",
"message": "FirmwareRelease not found"
}
}
Firmware
Get Firmware Download URL
A short-lived URL for one of a release’s firmware images
GET
/
firmware-releases
/
{id}
/
download-url
curl "https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url" \
-H "Authorization: Bearer sk_live_..."
import { createHash } from 'node:crypto';
const meta = await fetch(
'https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
const image = Buffer.from(await fetch(meta.download_url).then((r) => r.arrayBuffer()));
if (createHash('sha256').update(image).digest('hex') !== meta.sha256) {
throw new Error('Firmware image did not match its checksum');
}
import hashlib
import requests
meta = requests.get(
'https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url',
headers={'Authorization': 'Bearer sk_live_...'},
).json()
image = requests.get(meta['download_url']).content
if hashlib.sha256(image).hexdigest() != meta['sha256']:
raise ValueError('Firmware image did not match its checksum')
{
"download_url": "https://bota-firmware-prod.s3.us-west-2.amazonaws.com/...&X-Amz-Expires=3600",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"file_size_bytes": 1048576,
"expires_at": "2026-08-14T11:02:00Z"
}
{
"error": {
"code": "not_found",
"message": "FirmwareRelease not found"
}
}
Returns a presigned URL for one of the two images in a release, along with the
checksum and size to verify it against.
Verify what you download before installing it. Compare the SHA-256 of the
bytes you received against the
sha256 in this response; a truncated or
corrupted image that reaches a device is not recoverable over the air.
Which image
type | File | Used by |
|---|---|---|
bin (default) | .bin | The device downloading over WiFi or cellular — the update path available to your integration |
ufw | .ufw | Bota’s own tooling. Bluetooth firmware transfer is not a supported integration path; see Firmware Updates. |
Authentication
Requires an API key withdevices:read scope.
Path Parameters
string
required
The release identifier (e.g.,
fw_3kQ9mZa7Rp2XvB4nT6yL8cW1).Query Parameters
string
default:"bin"
Which image to sign —
bin or ufw. Use bin.curl "https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url" \
-H "Authorization: Bearer sk_live_..."
import { createHash } from 'node:crypto';
const meta = await fetch(
'https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url',
{ headers: { 'Authorization': 'Bearer sk_live_...' } },
).then((r) => r.json());
const image = Buffer.from(await fetch(meta.download_url).then((r) => r.arrayBuffer()));
if (createHash('sha256').update(image).digest('hex') !== meta.sha256) {
throw new Error('Firmware image did not match its checksum');
}
import hashlib
import requests
meta = requests.get(
'https://api.bota.dev/v1/firmware-releases/fw_3kQ9mZa7Rp2XvB4nT6yL8cW1/download-url',
headers={'Authorization': 'Bearer sk_live_...'},
).json()
image = requests.get(meta['download_url']).content
if hashlib.sha256(image).hexdigest() != meta['sha256']:
raise ValueError('Firmware image did not match its checksum')
{
"download_url": "https://bota-firmware-prod.s3.us-west-2.amazonaws.com/...&X-Amz-Expires=3600",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"file_size_bytes": 1048576,
"expires_at": "2026-08-14T11:02:00Z"
}
{
"error": {
"code": "not_found",
"message": "FirmwareRelease not found"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
download_url | string | Presigned URL. Requires no authentication header of its own. |
sha256 | string | Checksum of the image this URL serves |
file_size_bytes | number | Size of the image in bytes |
expires_at | string | ISO 8601. The URL stops working at this time — do not cache it past it. |
Notes
- You do not need this endpoint for the WiFi and cellular update path. A device with an assignment receives its own download URL in the heartbeat response — see Assign Firmware to Device.
- Request a fresh URL rather than storing one. The lifetime is short by design,
and
expires_attells you exactly when it lapses.
Was this page helpful?

