> ## 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.

# Assign Firmware to Device

> Queue a published firmware release for one device

Queues a release for a single device. The device picks the update up on its next
heartbeat and downloads it itself, over WiFi or cellular. No further call is
needed — track it with [Get Device OTA Status](/api-reference/firmware/get-ota).

Allow up to a couple of minutes between assigning and delivery; the device only
learns about it when it next checks in.

<Note>
  A device holds one assignment at a time. If one is already in flight this
  returns `409` — cancel it first with
  [Cancel Device OTA Assignment](/api-reference/firmware/cancel-ota).
</Note>

## Authentication

Requires an [API key](/authentication) with `devices:write` scope.

## Path Parameters

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

## Request Body

<ParamField body="firmware_release_id" type="string" required>
  The release to install. Must be published and belong to your organization —
  an unpublished release answers `404`.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.bota.dev/v1/devices/dev_abc123/ota \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.bota.dev/v1/devices/dev_abc123/ota', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_live_...',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ firmware_release_id: 'fw_3kQ9mZa7Rp2XvB4nT6yL8cW1' }),
  });

  if (response.status === 409) {
    // Something is already in flight for this device.
  }

  const assignment = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.bota.dev/v1/devices/dev_abc123/ota',
      headers={
          'Authorization': 'Bearer sk_live_...',
          'Content-Type': 'application/json',
      },
      json={'firmware_release_id': 'fw_3kQ9mZa7Rp2XvB4nT6yL8cW1'},
  )

  if response.status_code == 409:
      ...  # Something is already in flight for this device.

  assignment = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json 201 (Created) theme={null}
  {
    "id": "ota_7Rp2XvB4nT6yL8cW1kQ9mZa3",
    "device_id": "dev_abc123",
    "firmware_release_id": "fw_3kQ9mZa7Rp2XvB4nT6yL8cW1",
    "status": "pending",
    "assigned_at": "2026-08-14T12:00:00Z",
    "delivered_at": null,
    "applied_at": null,
    "error_message": null
  }
  ```

  ```json 404 (Unknown device, or no published release with that id) theme={null}
  {
    "error": {
      "code": "not_found",
      "message": "FirmwareRelease not found"
    }
  }
  ```

  ```json 409 (Something is already in flight) theme={null}
  {
    "error": {
      "code": "resource_conflict",
      "message": "Device already has a pending OTA assignment"
    }
  }
  ```
</ResponseExample>

## Response Fields

See [Get Device OTA Status](/api-reference/firmware/get-ota) for the assignment
shape and the meaning of each `status`.

## Notes

* A device connected to the mobile app over Bluetooth pauses its own heartbeat,
  so it will not receive an assignment while that connection is open.
* Manual assignment takes precedence over any automatic update the device would
  otherwise be offered.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.