---
title: Devices API
description: "Manage paired devices with pairing, approval, denial, and revocation actions"
---

# Devices API

Manage device pairing for your agent. Devices can be self-paired (auto-approved) or created via a pairing code flow where they start in a `pending` state and can be approved, denied, or revoked.

<Note>All device endpoints require an authenticated session. Unauthenticated requests receive a `401` response.</Note>

## List devices

```http
GET /api/devices
```

Returns all pending and approved devices grouped by status.

### Response

```json
{
  "pending": [
    {
      "id": "dev_pending_1",
      "name": "iPhone 15 Pro — Atlas Mobile",
      "ip": "86.23.104.12",
      "firstSeen": "2026-03-27T14:22:00Z",
      "lastSeen": "2026-03-27T15:30:00Z",
      "status": "pending"
    }
  ],
  "approved": [
    {
      "id": "dev_approved_1",
      "name": "Docker Container — agentbot-prod",
      "ip": "10.0.1.42",
      "firstSeen": "2026-03-25T09:00:00Z",
      "lastSeen": "2026-03-27T15:29:00Z",
      "status": "approved"
    }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `pending` | array | Devices awaiting approval |
| `approved` | array | Devices that have been approved |

### Device object

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique device identifier |
| `name` | string | Human-readable device name |
| `ip` | string | IP address of the device |
| `firstSeen` | string | ISO 8601 timestamp when the device was first detected |
| `lastSeen` | string | ISO 8601 timestamp of the device's most recent activity |
| `status` | string | One of `pending`, `approved`, `denied`, or `revoked` |

### Errors

| Code | Description |
|------|-------------|
| 401 | `Unauthorized` — no authenticated session |

---

## Generate a pairing code

```http
PUT /api/devices
```

Creates a new device in `pending` status and returns a pairing code. Use this when you want another device to complete the pairing flow via a code or URL.

### Query parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `name` | string | No | `My Device` | Human-readable name for the device |

### Response

```json
{
  "success": true,
  "deviceId": "dev_abc123",
  "pairingCode": "X7K2M9",
  "pairingUrl": "/dashboard/devices?pair=X7K2M9&id=dev_abc123"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` when the pairing code was generated |
| `deviceId` | string | Identifier of the newly created device |
| `pairingCode` | string | Six-character alphanumeric code for pairing |
| `pairingUrl` | string | URL path to complete the pairing in the dashboard |

### Example

```bash
curl -X PUT "https://agentbot.sh/api/devices?name=Living+Room+iPad"
```

### Errors

| Code | Description |
|------|-------------|
| 401 | `Unauthorized` — no authenticated session |

---

## Self-pair a device

```http
POST /api/devices/pair
```

Pairs the current device directly with auto-approval. This is used for self-pairing flows such as the "Pair My iPhone" button, where the requesting device is the one being paired.

### Request body

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | No | `My iPhone` | Human-readable name for the device |

### Response

```json
{
  "success": true,
  "device": {
    "id": "dev_xyz789",
    "name": "My iPhone",
    "status": "approved",
    "pairedAt": "2026-04-09T16:20:00Z"
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` when the device was paired |
| `device` | object | The newly paired device |
| `device.id` | string | Unique device identifier |
| `device.name` | string | Human-readable device name |
| `device.status` | string | Always `approved` for self-paired devices |
| `device.pairedAt` | string | ISO 8601 timestamp of when the device was paired |

### Example

```bash
curl -X POST https://agentbot.sh/api/devices/pair \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My iPhone"
  }'
```

### Errors

| Code | Description |
|------|-------------|
| 401 | `Unauthorized` — no authenticated session |

---

## Manage a device

```http
POST /api/devices
```

Approve, deny, or revoke a device. The action must be valid for the device's current status:

- `approve` — only valid when the device is `pending`
- `deny` — only valid when the device is `pending`
- `revoke` — only valid when the device is `approved`

### Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `deviceId` | string | Yes | The identifier of the device to act on |
| `action` | string | Yes | One of `approve`, `deny`, or `revoke` |

### Response

```json
{
  "success": true,
  "pending": [],
  "approved": [
    {
      "id": "dev_approved_1",
      "name": "iPhone 15 Pro — Atlas Mobile",
      "ip": "86.23.104.12",
      "firstSeen": "2026-03-27T14:22:00Z",
      "lastSeen": "2026-03-27T15:35:00Z",
      "status": "approved"
    }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` when the action was applied |
| `pending` | array | Remaining pending devices |
| `approved` | array | Currently approved devices |

### Errors

| Code | Description |
|------|-------------|
| 401 | `Unauthorized` — no authenticated session |
| 400 | `Missing required fields: deviceId, action` — one or both required fields are missing |
| 400 | `Invalid action. Must be: approve, deny, or revoke` — the action is not recognized |
| 400 | `Device is not pending` — attempted to approve or deny a device that is not in `pending` status |
| 400 | `Device is not approved` — attempted to revoke a device that is not in `approved` status |
| 400 | `Invalid request body` — the request body is not valid JSON |
| 404 | `Device not found` — no device exists with the given identifier |

### Example: approve a device

```bash
curl -X POST https://agentbot.sh/api/devices \
  -H "Content-Type: application/json" \
  -d '{
    "deviceId": "dev_pending_1",
    "action": "approve"
  }'
```

### Example: revoke a device

```bash
curl -X POST https://agentbot.sh/api/devices \
  -H "Content-Type: application/json" \
  -d '{
    "deviceId": "dev_approved_1",
    "action": "revoke"
  }'
```

---

## OpenClaw devices proxy (deprecated)

<Warning>These proxy endpoints are deprecated and will be removed in a future release. Device management now uses the local endpoints documented above (`/api/devices` and `/api/devices/pair`) instead of proxying to the agent runtime.</Warning>

The following endpoints previously proxied device management requests to the agent's OpenClaw runtime. They have been replaced by the local device pairing API.

### List paired devices (runtime)

```http
GET /api/openclaw/devices
```

Returns the list of devices paired with the agent's runtime. Proxies to the agent's `GET /api/devices` endpoint.

#### Errors

| Code | Description |
|------|-------------|
| 401 | `Unauthorized` — no authenticated session |
| 404 | `No agent deployed` — the user has no running agent instance |
| 502 | `Agent unreachable` — the agent instance did not respond within 10 seconds |

### Manage paired devices (runtime)

```http
POST /api/openclaw/devices
```

Perform device actions on the agent's runtime. The `action` field determines the behavior.

#### Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `action` | string | Yes | One of `pair`, `unpair`, or `test-push` |
| `deviceId` | string | Conditional | Required for `unpair` and `test-push` actions |

#### Actions

| Action | Description | Proxies to |
|--------|-------------|------------|
| `pair` | Generate a QR code for pairing a new device | `POST /api/devices/pair` |
| `unpair` | Unpair an existing device | `POST /api/devices/unpair` |
| `test-push` | Send a test push notification to a paired device | `POST /api/devices/test-push` |

#### Example: pair a new device

```bash
curl -X POST https://agentbot.sh/api/openclaw/devices \
  -H "Content-Type: application/json" \
  -d '{
    "action": "pair"
  }'
```

#### Example: unpair a device

```bash
curl -X POST https://agentbot.sh/api/openclaw/devices \
  -H "Content-Type: application/json" \
  -d '{
    "action": "unpair",
    "deviceId": "device_abc123"
  }'
```

#### Example: test push notification

```bash
curl -X POST https://agentbot.sh/api/openclaw/devices \
  -H "Content-Type: application/json" \
  -d '{
    "action": "test-push",
    "deviceId": "device_abc123"
  }'
```

#### Errors

| Code | Description |
|------|-------------|
| 400 | `Invalid action` — the `action` field is not `pair`, `unpair`, or `test-push` |
| 400 | `Invalid request body` — the request body is not valid JSON |
| 401 | `Unauthorized` — no authenticated session |
| 404 | `No agent deployed` — the user has no running agent instance |
| 502 | `Agent unreachable` — the agent instance did not respond within 15 seconds |
