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

# Device Heartbeat

> Report device health status for fleet monitoring

Report device health status including battery level, storage usage, and signal strength. Health data is stored on the device record and returned in [Get Device](/api-reference/devices/get) and [List Devices](/api-reference/devices/list) responses.

## Authentication

This endpoint accepts two authentication methods depending on the device connectivity:

| Path               | Auth                            | Use case                                                          |
| ------------------ | ------------------------------- | ----------------------------------------------------------------- |
| **4G/WiFi direct** | [Device token](/authentication) | Device reports its own status directly                            |
| **BLE relay**      | [API key](/authentication)      | Developer's backend relays status read from device over Bluetooth |

<RequestExample>
  ```bash Bluetooth relay (API key) theme={null}
  curl -X POST https://api.bota.dev/v1/devices/dev_abc123/heartbeat \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "battery_percent": 85,
      "storage_used_mb": 512,
      "storage_total_mb": 32768,
      "device_state": "idle",
      "pending_recordings": 3,
      "connection_type": ["ble"]
    }'
  ```

  ```bash 4G direct (device token) theme={null}
  curl -X POST https://api.bota.dev/v1/devices/me/heartbeat \
    -H "Authorization: Bearer dtok_..." \
    -H "Content-Type: application/json" \
    -d '{
      "battery_percent": 85,
      "battery_mv": 3820,
      "is_charging": false,
      "storage_used_mb": 512,
      "storage_total_mb": 32768,
      "signal_strength_dbm": -75,
      "firmware_version": "1.2.3",
      "device_state": "idle",
      "pending_recordings": 3,
      "flags": 8,
      "connection_type": ["wifi", "4g"],
      "lte_status": "connected",
      "wifi_status": "connected"
    }'
  ```

  ```javascript Node.js (BLE relay) theme={null}
  // Developer's backend relays device status read over BLE
  app.post('/devices/:id/heartbeat', auth, async (req, res) => {
    const response = await fetch(
      `https://api.bota.dev/v1/devices/${req.params.id}/heartbeat`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${BOTA_API_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          ...req.body,
          connection_type: ['ble'],
        }),
      }
    );

    res.json(await response.json());
  });
  ```
</RequestExample>

## Path Parameters

<ParamField path="id" type="string" required>
  The device's unique identifier (e.g., `dev_abc123`), or `me` when using a device token. The `me` alias resolves to the device ID associated with the token, so the device doesn't need to know its own Bota ID. When using an explicit device ID with a device token, it must match the token's device.
</ParamField>

## Request Body

All fields are optional -- send only the metrics your device can report.

<ParamField body="battery_percent" type="integer">
  Current battery level (0-100).
</ParamField>

<ParamField body="battery_mv" type="integer">
  Battery voltage in millivolts (e.g., 3700 = 3.7V). Useful for diagnosing power issues.
</ParamField>

<ParamField body="is_charging" type="boolean">
  Whether the device is currently charging.
</ParamField>

<ParamField body="storage_used_mb" type="integer">
  Storage used in megabytes.
</ParamField>

<ParamField body="storage_total_mb" type="integer">
  Total storage capacity in megabytes.
</ParamField>

<ParamField body="signal_strength_dbm" type="integer">
  Cellular signal strength in dBm (typically -50 to -120, where higher/less negative is better).
</ParamField>

<ParamField body="firmware_version" type="string">
  Current firmware version string (max 32 characters).
</ParamField>

<ParamField body="device_name" type="string">
  Device name (max 64 characters). Updated in the device record when provided.
</ParamField>

<ParamField body="device_state" type="string">
  Current device operational state (max 32 characters). One of: `idle`, `recording`, `syncing`, `uploading`, `charging`, `lowBattery`, `storageFull`, `error`.
</ParamField>

<ParamField body="pending_recordings" type="integer">
  Number of recordings on the device pending sync/upload.
</ParamField>

<ParamField body="flags" type="integer">
  Device status flags as a 1-byte bitmask (0-255). Bit 0: charging, Bit 1: low battery, Bit 2: storage full, Bit 3: WiFi connected, Bit 4: LTE connected, Bit 5: sync active.
</ParamField>

<ParamField body="lte_status" type="string">
  LTE/4G modem status. One of: `off`, `searching`, `registered`, `connected`, `denied`, `no_sim`, `error`, `low_voltage`, `disabled`.
</ParamField>

<ParamField body="wifi_status" type="string">
  WiFi radio status. One of: `off`, `scanning`, `connecting`, `connected`, `connect_failed`, `no_credentials`, `disabled`, `error`.
</ParamField>

<ParamField body="connection_type" type="string[]">
  Active device connections. Array of: `wifi`, `4g`, `ble`.
</ParamField>

<ParamField body="upload_channel" type="string">
  The channel currently being used to upload recordings. One of: `wifi`, `4g`, `ble`. Set to `null` to clear. Omit if no upload is in progress.
</ParamField>

## Response

Returns `{ "status": "ok" }` on success.

<ResponseExample>
  ```json 200 theme={null}
  {
    "status": "ok"
  }
  ```

  ```json 403 theme={null}
  {
    "error": {
      "code": "forbidden",
      "message": "Device token does not match device ID"
    }
  }
  ```
</ResponseExample>

## Response Fields

| Field    | Type   | Description     |
| -------- | ------ | --------------- |
| `status` | string | `ok` on success |

## Recommended Heartbeat Interval

| Scenario                  | Interval            |
| ------------------------- | ------------------- |
| Normal operation          | Every 15-30 minutes |
| Low battery (\< 20%)      | Every 5 minutes     |
| Charging                  | Every 30-60 minutes |
| Poor signal (\< -100 dBm) | Every 5-10 minutes  |

## Field Constraints

| Field                 | Type           | Range                                                                                                 |
| --------------------- | -------------- | ----------------------------------------------------------------------------------------------------- |
| `battery_percent`     | integer        | 0-100                                                                                                 |
| `battery_mv`          | integer        | >= 0 (millivolts)                                                                                     |
| `is_charging`         | boolean        | `true` / `false`                                                                                      |
| `storage_used_mb`     | integer        | >= 0                                                                                                  |
| `storage_total_mb`    | integer        | >= 0                                                                                                  |
| `signal_strength_dbm` | integer        | \<= 0 (typically -50 to -120)                                                                         |
| `firmware_version`    | string         | max 32 characters                                                                                     |
| `device_name`         | string         | max 64 characters                                                                                     |
| `device_state`        | string         | max 32 characters                                                                                     |
| `pending_recordings`  | integer        | >= 0                                                                                                  |
| `flags`               | integer        | 0-255                                                                                                 |
| `lte_status`          | string         | `off`, `searching`, `registered`, `connected`, `denied`, `no_sim`, `error`, `low_voltage`, `disabled` |
| `wifi_status`         | string         | `off`, `scanning`, `connecting`, `connected`, `connect_failed`, `no_credentials`, `disabled`, `error` |
| `connection_type`     | string\[]      | Array of `wifi`, `4g`, `ble`                                                                          |
| `upload_channel`      | string \| null | `wifi`, `4g`, or `ble`; `null` to clear                                                               |
