> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bota.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Device Command

> Send a command to a device to start recording, stop recording, or trigger upload of pending recordings

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](/authentication) with `devices:write` scope.

<RequestExample>
  ```bash cURL (Start Recording) theme={null}
  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"
        }
      }
    }'
  ```

  ```bash cURL (Stop Recording) theme={null}
  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"
    }'
  ```

  ```bash cURL (Trigger Upload) theme={null}
  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"
    }'
  ```

  ```javascript Node.js theme={null}
  // 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();
  ```

  ```python Python theme={null}
  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()
  ```
</RequestExample>

## Path Parameters

<ParamField path="id" type="string" required>
  The device's unique identifier (e.g., `dev_abc123`).
</ParamField>

## Request Body

<ParamField body="type" type="string" required>
  The command type. One of:

  * `start_recording` - Start a new recording
  * `stop_recording` - Stop the current recording
  * `trigger_upload` - Upload any pending recordings stored on the device
</ParamField>

<ParamField body="params" type="object">
  Optional parameters for the command.
</ParamField>

<ParamField body="params.max_duration_sec" type="number">
  Maximum recording duration in seconds (1-86400). Default: 3600 (1 hour).
</ParamField>

<ParamField body="params.upload_immediately" type="boolean">
  Whether to upload immediately after recording stops. Default: true.
</ParamField>

<ParamField body="params.metadata" type="object">
  Custom metadata to attach to the recording.
</ParamField>

<ParamField body="idempotency_key" type="string">
  Unique key to prevent duplicate command creation.
</ParamField>

<ParamField body="ttl_seconds" type="number">
  Time-to-live for the command in seconds. Command expires if not executed within this time. Default: 300 (5 minutes).
</ParamField>

## Response

Returns the command object with `status` set to `pending`.

<ResponseExample>
  ```json 201 (Created) theme={null}
  {
    "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"
  }
  ```

  ```json 400 (Already Recording) theme={null}
  {
    "error": {
      "code": "already_recording",
      "message": "Device is already recording"
    }
  }
  ```

  ```json 400 (Not Recording) theme={null}
  {
    "error": {
      "code": "not_recording",
      "message": "Device is not currently recording"
    }
  }
  ```

  ```json 404 theme={null}
  {
    "error": {
      "code": "not_found",
      "message": "Device not found"
    }
  }
  ```
</ResponseExample>

## 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_token` is 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
