curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "start_recording",
"params": {
"max_duration_sec": 3600,
"upload_immediately": true,
"metadata": {
"session_id": "meeting-123"
}
}
}'
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "stop_recording"
}'
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "trigger_upload"
}'
// Start recording
const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/commands', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'start_recording',
params: {
max_duration_sec: 3600,
upload_immediately: true,
},
}),
});
const command = await response.json();
import requests
# Start recording
response = requests.post(
'https://api.bota.dev/v1/devices/dev_abc123/commands',
headers={
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
json={
'type': 'start_recording',
'params': {
'max_duration_sec': 3600,
'upload_immediately': True,
},
},
)
command = response.json()
{
"id": "cmd_xyz789",
"device_id": "dev_abc123",
"type": "start_recording",
"status": "pending",
"params": {
"max_duration_sec": 3600,
"upload_immediately": true,
"metadata": {
"session_id": "meeting-123"
}
},
"grant_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"result": null,
"error": null,
"expires_at": "2025-01-20T11:00:00Z",
"delivered_at": null,
"executed_at": null,
"created_at": "2025-01-20T10:55:00Z"
}
{
"error": {
"code": "already_recording",
"message": "Device is already recording"
}
}
{
"error": {
"code": "not_recording",
"message": "Device is not currently recording"
}
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Devices
Create Device Command
Send a command to a device to start recording, stop recording, or trigger upload of pending recordings
POST
/
devices
/
{id}
/
commands
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "start_recording",
"params": {
"max_duration_sec": 3600,
"upload_immediately": true,
"metadata": {
"session_id": "meeting-123"
}
}
}'
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "stop_recording"
}'
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "trigger_upload"
}'
// Start recording
const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/commands', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'start_recording',
params: {
max_duration_sec: 3600,
upload_immediately: true,
},
}),
});
const command = await response.json();
import requests
# Start recording
response = requests.post(
'https://api.bota.dev/v1/devices/dev_abc123/commands',
headers={
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
json={
'type': 'start_recording',
'params': {
'max_duration_sec': 3600,
'upload_immediately': True,
},
},
)
command = response.json()
{
"id": "cmd_xyz789",
"device_id": "dev_abc123",
"type": "start_recording",
"status": "pending",
"params": {
"max_duration_sec": 3600,
"upload_immediately": true,
"metadata": {
"session_id": "meeting-123"
}
},
"grant_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"result": null,
"error": null,
"expires_at": "2025-01-20T11:00:00Z",
"delivered_at": null,
"executed_at": null,
"created_at": "2025-01-20T10:55:00Z"
}
{
"error": {
"code": "already_recording",
"message": "Device is already recording"
}
}
{
"error": {
"code": "not_recording",
"message": "Device is not currently recording"
}
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Send a remote command to a device. Commands are delivered via Bluetooth (through the mobile app) or via heartbeat response (for 4G/WiFi devices).
Authentication
Requires an API key withdevices:write scope.
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "start_recording",
"params": {
"max_duration_sec": 3600,
"upload_immediately": true,
"metadata": {
"session_id": "meeting-123"
}
}
}'
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "stop_recording"
}'
curl -X POST https://api.bota.dev/v1/devices/dev_abc123/commands \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "trigger_upload"
}'
// Start recording
const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/commands', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'start_recording',
params: {
max_duration_sec: 3600,
upload_immediately: true,
},
}),
});
const command = await response.json();
import requests
# Start recording
response = requests.post(
'https://api.bota.dev/v1/devices/dev_abc123/commands',
headers={
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
json={
'type': 'start_recording',
'params': {
'max_duration_sec': 3600,
'upload_immediately': True,
},
},
)
command = response.json()
Path Parameters
string
required
The device’s unique identifier (e.g.,
dev_abc123).Request Body
string
required
The command type. One of:
start_recording- Start a new recordingstop_recording- Stop the current recordingtrigger_upload- Upload any pending recordings stored on the device
object
Optional parameters for the command.
number
Maximum recording duration in seconds (1-86400). Default: 3600 (1 hour).
boolean
Whether to upload immediately after recording stops. Default: true.
object
Custom metadata to attach to the recording.
string
Unique key to prevent duplicate command creation.
number
Time-to-live for the command in seconds. Command expires if not executed within this time. Default: 300 (5 minutes).
Response
Returns the command object withstatus set to pending.
{
"id": "cmd_xyz789",
"device_id": "dev_abc123",
"type": "start_recording",
"status": "pending",
"params": {
"max_duration_sec": 3600,
"upload_immediately": true,
"metadata": {
"session_id": "meeting-123"
}
},
"grant_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"result": null,
"error": null,
"expires_at": "2025-01-20T11:00:00Z",
"delivered_at": null,
"executed_at": null,
"created_at": "2025-01-20T10:55:00Z"
}
{
"error": {
"code": "already_recording",
"message": "Device is already recording"
}
}
{
"error": {
"code": "not_recording",
"message": "Device is not currently recording"
}
}
{
"error": {
"code": "not_found",
"message": "Device not found"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Command identifier |
device_id | string | Target device (dev_*) |
type | string | start_recording, stop_recording, or trigger_upload |
status | string | Command status (see Command Lifecycle below) |
params | object | Command parameters |
grant_token | string | Signed authorization token for the device |
result | object | null | Execution result (populated after execution) |
error | object | null | Error details (if failed) |
expires_at | string | null | Expiration timestamp (ISO 8601) |
delivered_at | string | null | When delivered to device (ISO 8601) |
executed_at | string | null | When executed on device (ISO 8601) |
created_at | string | Creation timestamp (ISO 8601) |
Command Lifecycle
Commands go through the following states:| Status | Description |
|---|---|
pending | Command created, waiting to be delivered to device |
delivered | Command received by device, execution in progress |
executed | Command successfully executed |
failed | Command execution failed |
expired | Command expired before delivery |
cancelled | Command was cancelled via API |
Notes
- Commands are delivered to devices via Bluetooth (through the mobile app) or WiFi/4G (direct push)
- If the device is offline, commands will be queued and delivered when the device comes online
- The
grant_tokenis a signed JWT that authorizes the device to execute the command - For BLE-connected devices, the mobile app must relay the command to the device
Was this page helpful?

