curl -X POST https://api.bota.dev/v1/devices/dev_abc123/bind \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"end_user_id": "eu_xyz789",
"pk_d": "'$PK_D'"
}'
const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/bind', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
end_user_id: 'eu_xyz789',
pk_d: devicePublicKey,
}),
});
const device = await response.json();
// Store device.device_token securely on the device
import requests
response = requests.post(
'https://api.bota.dev/v1/devices/dev_abc123/bind',
headers={
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
json={
'end_user_id': 'eu_xyz789',
'pk_d': device_public_key,
},
)
device = response.json()
# Store device['device_token'] securely on the device
{
"id": "dev_abc123",
"serial_number": "SN-2025-001234",
"model": "bota_pin",
"firmware_version": "1.2.0",
"status": "bound",
"end_user_id": "eu_xyz789",
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T11:00:00Z",
"device_token": "dtok_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"cert": {
"cert_pem": "-----BEGIN CERTIFICATE-----\n...",
"privkey_pem": "-----BEGIN PRIVATE KEY-----\n...",
"serial_hex": "01ab...",
"expires_at": "2028-08-19T07:00:00.000Z"
}
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
{
"error": {
"code": "device_already_bound",
"message": "Device is already bound to another user. Unbind it first."
}
}
Devices
Bind Device
Bind a device to an end user and receive a device token for 4G uploads
POST
/
devices
/
{id}
/
bind
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/bind \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"end_user_id": "eu_xyz789",
"pk_d": "'$PK_D'"
}'
const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/bind', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
end_user_id: 'eu_xyz789',
pk_d: devicePublicKey,
}),
});
const device = await response.json();
// Store device.device_token securely on the device
import requests
response = requests.post(
'https://api.bota.dev/v1/devices/dev_abc123/bind',
headers={
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
json={
'end_user_id': 'eu_xyz789',
'pk_d': device_public_key,
},
)
device = response.json()
# Store device['device_token'] securely on the device
{
"id": "dev_abc123",
"serial_number": "SN-2025-001234",
"model": "bota_pin",
"firmware_version": "1.2.0",
"status": "bound",
"end_user_id": "eu_xyz789",
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T11:00:00Z",
"device_token": "dtok_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"cert": {
"cert_pem": "-----BEGIN CERTIFICATE-----\n...",
"privkey_pem": "-----BEGIN PRIVATE KEY-----\n...",
"serial_hex": "01ab...",
"expires_at": "2028-08-19T07:00:00.000Z"
}
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
{
"error": {
"code": "device_already_bound",
"message": "Device is already bound to another user. Unbind it first."
}
}
Bind a device to an end user. A device can only be bound to one user at a time. If you need to reassign a device, unbind it first.
For nonce-bound BLE authorization, read the device public key (
PK_D) with the
mobile SDK and include it as pk_d. Bota One uses this flow before persisting
the returned device token to the device.
When a device is bound, a device token (dtok_*) is returned. This token enables 4G-equipped devices to upload recordings directly to the Bota API without requiring a companion mobile app.
The device token is only shown once in the bind response. Store it securely on the device. If lost, you must unbind and rebind the device to get a new token.
Authentication
Requires an API key withdevices:write scope.
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/bind \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"end_user_id": "eu_xyz789",
"pk_d": "'$PK_D'"
}'
const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/bind', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
end_user_id: 'eu_xyz789',
pk_d: devicePublicKey,
}),
});
const device = await response.json();
// Store device.device_token securely on the device
import requests
response = requests.post(
'https://api.bota.dev/v1/devices/dev_abc123/bind',
headers={
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
json={
'end_user_id': 'eu_xyz789',
'pk_d': device_public_key,
},
)
device = response.json()
# Store device['device_token'] securely on the device
Path Parameters
string
required
The device’s unique identifier (e.g.,
dev_abc123).Request Body
string
required
The ID of the end user to bind this device to.
string
Device secp256r1 public key as 64 raw bytes (
x || y) encoded as exactly 128
lowercase hexadecimal characters. Read it from the connected device through
the mobile SDK. Required for nonce-bound deprovision and reset grants.Never place a Bota project API key in the mobile app. The app sends
device_id,
end_user_id, and pk_d to your backend; your backend calls this endpoint and
returns only the bind result needed by the app.Response
Returns the device object with adevice_token field. When device-certificate
issuance is enabled for the project, the response can also include one-time
cert material for delivery to compatible firmware.
{
"id": "dev_abc123",
"serial_number": "SN-2025-001234",
"model": "bota_pin",
"firmware_version": "1.2.0",
"status": "bound",
"end_user_id": "eu_xyz789",
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T11:00:00Z",
"device_token": "dtok_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"cert": {
"cert_pem": "-----BEGIN CERTIFICATE-----\n...",
"privkey_pem": "-----BEGIN PRIVATE KEY-----\n...",
"serial_hex": "01ab...",
"expires_at": "2028-08-19T07:00:00.000Z"
}
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
{
"error": {
"code": "device_already_bound",
"message": "Device is already bound to another user. Unbind it first."
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Device identifier (dev_*) |
serial_number | string | Physical serial number |
model | string | Device model (bota_pin or bota_note) |
firmware_version | string | null | Current firmware version |
status | string | bound (always bound after successful bind) |
end_user_id | string | Bound end user (eu_*) |
device_token | string | Device authentication token (dtok_*). Shown only once — store it securely. |
cert | object | null | Optional one-time device certificate and private-key delivery material. Supported by the demo integration; confirm SDK, firmware, and gateway mTLS policy before enabling. |
battery_percent | integer | null | Battery level (0-100) |
storage_used_mb | integer | null | Storage used in MB |
storage_total_mb | integer | null | Total storage capacity in MB |
signal_strength_dbm | integer | null | Signal strength in dBm |
last_heartbeat_at | string | null | Last heartbeat timestamp (ISO 8601) |
recording_state | object | null | Current recording state |
metadata | object | Custom key-value metadata |
created_at | string | Creation timestamp (ISO 8601) |
updated_at | string | Last update timestamp (ISO 8601) |
Device Token
Thedevice_token returned in the response is a limited-permission credential that allows the device to:
- Create recordings (
POST /v1/recordings) - Get upload URLs (
POST /v1/recordings/:id/upload-url) - Mark uploads complete (
POST /v1/recordings/:id/upload-complete) - Report device status (
POST /v1/devices/:id/heartbeat) - Refresh its token (
POST /v1/devices/:id/token/refresh)
Device tokens have very limited permissions compared to API keys. They cannot list recordings, access other users’ data, or perform administrative operations.
A device can only be bound to one end user at a time. Attempting to bind an already-bound device returns a
409 Conflict error. Use the Unbind Device endpoint first.Treat
cert.privkey_pem as a secret. Do not persist it in the app, analytics, or
logs. Deliver it immediately through the SDK. The current demo treats delivery
failure as non-fatal because device-token TLS remains the compatibility path;
projects enforcing mTLS must instead fail or recover binding explicitly.Was this page helpful?

