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

# Devices

> ESP32 hardware devices — pairing, sessions, and authentication.

## What is a device?

A device is a physical ESP32-based recorder. Once paired with a user account, it can start/stop sessions and upload audio independently of the web app. Devices authenticate with a device-scoped API key rather than a user JWT.

## Pairing flow

Pairing links a physical device to a user account in two steps:

1. **Device publishes a pairing code** — generates a short token and displays a QR code
2. **User confirms pairing** — scans the QR code in the web app, which calls the confirm endpoint

See the [Device Pairing Flow](/flows/device-pairing) diagram for the full sequence.

### Endpoints (device side, no auth required)

| Endpoint                               | Purpose                                          |
| -------------------------------------- | ------------------------------------------------ |
| `POST /api/devices/pairing-codes`      | Device publishes its MAC + token                 |
| `GET /api/devices/pair/status?mac=...` | Device polls to check if pairing is confirmed    |
| `POST /api/devices/pair/claim-auth`    | Device exchanges pairing token for an API key    |
| `POST /api/devices/register`           | Optional — pre-registers a device before pairing |

### Endpoints (user side, JWT required)

| Endpoint                                           | Purpose                    |
| -------------------------------------------------- | -------------------------- |
| `POST /api/devices/pair/confirm?mac=...&token=...` | User confirms pairing      |
| `GET /api/devices`                                 | List user's paired devices |
| `GET /api/devices/{device_id}`                     | Device details             |

## Pairing token rules

* Expires **15 minutes** after creation
* One-time use — invalidated once pairing is confirmed
* Unique per device at any given time

## Device API key

After the user confirms pairing (`paired: true` on the status poll), the device calls `POST /api/devices/pair/claim-auth`:

```json theme={null}
{
  "mac_address": "AA:BB:CC:DD:EE:FF",
  "pairing_token": "ABC123"
}
```

Returns:

```json theme={null}
{
  "device_auth_token": "sk_...",
  "expires_at": "2026-08-01T12:00:00Z"
}
```

The device stores this token and sends it as `X-API-Key: {device_auth_token}` on all protected calls. When the key expires, repeat the `claim-auth` call.

## Protected device endpoints (API key required)

| Endpoint                                       | Purpose                       |
| ---------------------------------------------- | ----------------------------- |
| `POST /api/devices/{device_id}/heartbeat`      | Keep device marked as online  |
| `POST /api/devices/{device_id}/session/start`  | Start a new recording session |
| `POST /api/devices/{device_id}/session/stop`   | Stop the current session      |
| `POST /api/sessions/{session_id}/upload-audio` | Upload a full audio file      |

<Warning>
  A device API key is scoped to that device. Using device A's key for a session started by device B will return `403 Forbidden`.
</Warning>

## Heartbeat

Devices should call `POST /api/devices/{device_id}/heartbeat` periodically (every \~30s) while online. This updates `last_seen` and keeps the device marked as active in the dashboard.
