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

# REST API

> Reference for FERAL's HTTP API — dashboard, skills, memory, identity, hardware, and channels endpoints.

## Base URL

```
http://localhost:9090/api
```

All endpoints require the brain to be running (`feral start`). Responses are JSON.

## Dashboard

| Method | Path                      | Description                                      |
| ------ | ------------------------- | ------------------------------------------------ |
| `GET`  | `/api/dashboard/status`   | Brain status, uptime, connected devices          |
| `GET`  | `/api/dashboard/context`  | Current context window (health, scene, calendar) |
| `GET`  | `/api/dashboard/timeline` | Recent events and conversations                  |
| `POST` | `/api/dashboard/command`  | Send a text command to the brain                 |

### Example: Send a command

```bash theme={null}
curl -X POST http://localhost:9090/api/dashboard/command \
  -H "Content-Type: application/json" \
  -d '{ "text": "What is my heart rate?" }'
```

```json theme={null}
{
  "response": "Your heart rate is 72 bpm, measured 3 seconds ago from your wristband.",
  "sdui": null,
  "sources": ["wristband_a3f2"]
}
```

## Skills

| Method   | Path                      | Description                            |
| -------- | ------------------------- | -------------------------------------- |
| `GET`    | `/api/skills`             | List all registered skills             |
| `GET`    | `/api/skills/:id`         | Get skill manifest and status          |
| `POST`   | `/api/skills/:id/execute` | Manually trigger a skill               |
| `POST`   | `/api/skills/install`     | Install a skill from URL or local path |
| `DELETE` | `/api/skills/:id`         | Uninstall a skill                      |

### Example: List skills

```bash theme={null}
curl http://localhost:9090/api/skills
```

```json theme={null}
{
  "skills": [
    { "id": "weather", "name": "Weather", "version": "1.2.0", "status": "active" },
    { "id": "calendar", "name": "Google Calendar", "version": "2.0.1", "status": "active" },
    { "id": "smart_home", "name": "Smart Home Control", "version": "1.0.0", "status": "active" }
  ]
}
```

## Memory

| Method   | Path                       | Description                             |
| -------- | -------------------------- | --------------------------------------- |
| `GET`    | `/api/memory/episodes`     | List episodic memories (paginated)      |
| `GET`    | `/api/memory/search`       | Semantic search across all memory tiers |
| `POST`   | `/api/memory/note`         | Store a note in the knowledge base      |
| `GET`    | `/api/memory/knowledge`    | Query the knowledge graph               |
| `DELETE` | `/api/memory/episodes/:id` | Delete a specific episode               |

### Example: Semantic search

```bash theme={null}
curl "http://localhost:9090/api/memory/search?q=last+time+I+went+running&limit=5"
```

```json theme={null}
{
  "results": [
    {
      "id": "ep_2026_0410_morning",
      "timestamp": "2026-04-10T07:15:00Z",
      "summary": "Morning 5K run in Prospect Park. Avg HR 145 bpm, pace 5:20/km.",
      "relevance": 0.94
    }
  ]
}
```

## Identity

| Method | Path                     | Description                     |
| ------ | ------------------------ | ------------------------------- |
| `GET`  | `/api/identity/user`     | Get current USER.md content     |
| `PUT`  | `/api/identity/user`     | Update USER.md                  |
| `GET`  | `/api/identity/soul`     | Get SOUL.md (system persona)    |
| `GET`  | `/api/identity/twin`     | Query the digital twin          |
| `POST` | `/api/identity/twin/ask` | Ask the digital twin a question |

### Example: Ask the digital twin

```bash theme={null}
curl -X POST http://localhost:9090/api/identity/twin/ask \
  -H "Content-Type: application/json" \
  -d '{ "question": "Would I enjoy this job offer?" }'
```

```json theme={null}
{
  "answer": "Based on your preference for remote work, interest in systems programming, and past dissatisfaction with large-company bureaucracy, this startup role aligns well. However, the on-call rotation conflicts with your sleep optimization goals.",
  "confidence": 0.82
}
```

## Devices & Hardware

| Method   | Path                           | Description                                                                 |
| -------- | ------------------------------ | --------------------------------------------------------------------------- |
| `GET`    | `/api/devices/connected`       | List all connected devices with types and metrics                           |
| `GET`    | `/api/devices/paired`          | List paired devices (claimed by default; `include_unclaimed=true` optional) |
| `POST`   | `/api/devices/pair`            | Mint daemon/browser pairing token                                           |
| `GET`    | `/api/devices/pair/url`        | Mint one-time pair URL payload                                              |
| `GET`    | `/api/devices/pair/qr`         | Render pair payload as QR image                                             |
| `GET`    | `/api/devices/pair/check`      | Check token state (and PIN requirement)                                     |
| `POST`   | `/api/devices/pair/verify_pin` | Verify pairing PIN                                                          |
| `POST`   | `/api/devices/pair/complete`   | Complete phone/browser pairing                                              |
| `POST`   | `/api/devices/pair/prune`      | Revoke unclaimed tokens in bulk                                             |
| `POST`   | `/api/devices/handoff`         | Initiate a session handoff between devices                                  |
| `DELETE` | `/api/devices/{device_id}`     | Revoke (un-pair) a device                                                   |
| `GET`    | `/api/nodes/health`            | All node health status with heartbeat freshness                             |
| `GET`    | `/api/commands/recent`         | Recent commands with full lifecycle state                                   |
| `GET`    | `/api/commands/{command_id}`   | Single command detail including state history                               |
| `POST`   | `/api/proactive/dismiss`       | Dismiss a proactive alert (learns from it)                                  |

## Access Mode & Remote Reachability

| Method | Path                      | Description                                      |
| ------ | ------------------------- | ------------------------------------------------ |
| `GET`  | `/api/access/status`      | Current pairing mode + Tailscale/Funnel status   |
| `POST` | `/api/access/remote-up`   | Enable remote mode and Tailscale Funnel          |
| `POST` | `/api/access/remote-down` | Disable remote mode and return to localhost mode |

## Approvals (Execution Inbox)

The approval inbox lets a non-chat client (web UI, CLI, mobile, automation) resolve pending tool-execution approvals without having to type an acknowledgement into a chat session. A pending request is created by the orchestrator whenever `enforce_safety()` blocks a tool call (see [Autonomy Levels](/guides/autonomy)).

| Method | Path                                  | Description                                                                                                      |
| ------ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/api/approvals`                      | List pending tool-approval requests. Optional query params: `session_id` (filter), `limit` (1–500, default 100). |
| `POST` | `/api/approvals/{request_id}/approve` | Approve a pending request and execute the tool.                                                                  |
| `POST` | `/api/approvals/{request_id}/reject`  | Reject a pending request without executing.                                                                      |

Both `approve` and `reject` accept an optional JSON body:

```json theme={null}
{ "session_id": "s-abc123" }
```

When `session_id` is provided it must match the session that originated the request, or the call returns `409 session_mismatch`.

### Example: list pending approvals

```bash theme={null}
curl "http://localhost:9090/api/approvals?session_id=s-abc123&limit=50"
```

```json theme={null}
{
  "count": 1,
  "approvals": [
    {
      "request_id": "apr_01HXYZ...",
      "session_id": "s-abc123",
      "tool_name": "browser__navigate",
      "args": { "url": "https://example.com" },
      "safety_level": "privileged",
      "created_at": 1746230412.81,
      "status": "pending"
    }
  ]
}
```

### Example: approve

```bash theme={null}
curl -X POST "http://localhost:9090/api/approvals/apr_01HXYZ.../approve" \
  -H "Content-Type: application/json" \
  -d '{ "session_id": "s-abc123" }'
```

```json theme={null}
{
  "success": true,
  "status": "approved",
  "request_id": "apr_01HXYZ...",
  "session_id": "s-abc123",
  "tool_name": "browser__navigate",
  "summary": "Navigated to https://example.com",
  "result": { "ok": true }
}
```

### Example: reject

```bash theme={null}
curl -X POST "http://localhost:9090/api/approvals/apr_01HXYZ.../reject" \
  -H "Content-Type: application/json" \
  -d '{ "session_id": "s-abc123" }'
```

```json theme={null}
{
  "success": true,
  "status": "rejected",
  "request_id": "apr_01HXYZ...",
  "session_id": "s-abc123",
  "tool_name": "browser__navigate"
}
```

### Status codes

| Code  | Meaning                                                                        |
| ----- | ------------------------------------------------------------------------------ |
| `200` | Resolved (approved or rejected).                                               |
| `404` | Unknown `request_id` (already resolved, expired, or never existed).            |
| `409` | `session_mismatch` — supplied `session_id` does not match the pending request. |
| `503` | Orchestrator or tool runner not initialised yet.                               |

## Channels

| Method   | Path                             | Description                                          |
| -------- | -------------------------------- | ---------------------------------------------------- |
| `GET`    | `/api/channels`                  | List all channel statuses                            |
| `GET`    | `/api/channels/:name/status`     | Status of a specific channel (telegram, slack, etc.) |
| `POST`   | `/api/channels/push/register`    | Register a push notification device token            |
| `GET`    | `/api/channels/push/devices`     | List registered push devices                         |
| `DELETE` | `/api/channels/push/devices/:id` | Unregister a push device                             |
| `POST`   | `/api/channels/push/test`        | Send a test push notification                        |

## Error Format

All errors follow a consistent shape:

```json theme={null}
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Skill 'invalid_id' not found.",
    "details": null
  }
}
```

| HTTP Code | Meaning                                                   |
| --------- | --------------------------------------------------------- |
| `400`     | Bad request — invalid parameters                          |
| `401`     | Unauthorized — missing or invalid token                   |
| `404`     | Resource not found                                        |
| `409`     | Conflict — device already paired, skill already installed |
| `500`     | Internal brain error                                      |
