# REST API reference

The admin REST API served under `/api/v1` on the same port as MCP and the dashboard when `--admin` is on (84 operations), generated from `HTTP_ENDPOINTS` in `@browserhive/contracts/http`. Request and response schemas are in the OpenAPI 3.1 document the server serves at `/api/v1/openapi.json`, with an interactive reference UI at `/api/v1/docs`.

Summaries come from `packages/contracts/generated/openapi.json`.

## Conventions

- **Authentication:** `cookie` is the dashboard session cookie `browserhive_session` (from `POST /api/v1/auth/login`); `bearer` is `Authorization: Bearer <token>` (operator API tokens or agent tokens); `grant` is a short-lived `?grant=<token>` accepted only on trace and screenshot downloads; `public` needs nothing.
- **Authorization:** the scope column is checked for the caller. Operators hold every scope; agent tokens hold `mcp:tools` only.
- **Wire format:** JSON bodies are snake_case; timestamps are epoch milliseconds.
- **Errors:** `application/problem+json` with a registry `code`; see the [error reference](/docs/reference/errors/).
- **Collections:** `{ data, page: { next_cursor, prev_cursor?, limit, total? }, facets?, applied, meta }`, cursor-paginated; unknown query keys are rejected with 400.
- **Concurrency:** vault bindings and group policies take `If-Match: <version>`; a stale version returns 409 `CONFLICT`. Bulk operations accept `Idempotency-Key`.
- **Realtime:** `GET /api/v1/ws` upgrades to the WebSocket protocol described in the [WebSocket reference](/docs/reference/websocket/).

## Scopes

`sessions:read` · `sessions:write` · `sessions:takeover` · `attention:read` · `attention:resolve` · `vault:read` · `vault:write` · `vault:confirm` · `blocklist:read` · `blocklist:write` · `system:read` · `system:write` · `logs:read` · `notifications:read` · `notifications:write` · `preferences:write` · `mcp:tools`

## Health

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/health` | `getHealth` | — | public | Liveness/readiness; 200 only when ready (also served at `/health`). |

## Authentication

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| POST | `/api/v1/auth/login` | `login` | — | public | Log in with the operator password; sets the session cookie. |
| POST | `/api/v1/auth/logout` | `logout` | — | cookie, bearer | Destroy the current session and clear the cookie. |
| GET | `/api/v1/auth/me` | `getMe` | — | cookie, bearer | The authenticated principal. |
| POST | `/api/v1/auth/change-password` | `changePassword` | — | cookie, bearer | Change the operator password; revokes every other session. |
| GET | `/api/v1/auth/sessions` | `listAuthSessions` | — | cookie, bearer | The caller's operator sessions. |
| DELETE | `/api/v1/auth/sessions/{id_prefix}` | `revokeAuthSession` | — | cookie, bearer | Revoke one operator session by id prefix. |
| POST | `/api/v1/auth/sessions/revoke-all` | `revokeAllAuthSessions` | — | cookie, bearer | Revoke every session of the caller except the current one. |
| GET | `/api/v1/auth/tokens` | `listTokens` | — | cookie, bearer | Issued API tokens (never the secret). |
| POST | `/api/v1/auth/tokens` | `createToken` | — | cookie, bearer | Issue an API token; the token is shown once. |
| DELETE | `/api/v1/auth/tokens/{credential_id}` | `revokeToken` | — | cookie, bearer | Revoke an API token. |
| POST | `/api/v1/auth/grants` | `createGrant` | — | cookie, bearer | Mint a single-use, 10-minute grant: `trace` → resource_id is the session id, `screenshot` → the event id. |

## Sessions

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/sessions` | `listSessions` | `sessions:read` | cookie, bearer | List sessions with facets; the live registry overlays stored rows. |
| POST | `/api/v1/sessions/bulk` | `bulkSessions` | `sessions:write` | cookie, bearer | Archive, unarchive, terminate or delete up to 100 sessions (per-item results). |
| GET | `/api/v1/sessions/{session_id}` | `getSession` | `sessions:read` | cookie, bearer | One session with trace/data-dir descriptors and counters (no embedded arrays). |
| GET | `/api/v1/sessions/{session_id}/tool-calls` | `listSessionToolCalls` | `sessions:read` | cookie, bearer | Tool calls of one session (`?expand=detail` adds args/result). |
| GET | `/api/v1/sessions/{session_id}/tool-calls/{event_id}` | `getSessionToolCall` | `sessions:read` | cookie, bearer | One tool call with args, result and its screenshot. |
| GET | `/api/v1/sessions/{session_id}/pages` | `listSessionPages` | `sessions:read` | cookie, bearer | Pages visited by one session. |
| GET | `/api/v1/sessions/{session_id}/attention` | `listSessionAttention` | `attention:read` | cookie, bearer | Attention requests of one session. |
| GET | `/api/v1/sessions/{session_id}/vault-access` | `listSessionVaultAccess` | `vault:read` | cookie, bearer | Vault access audit rows of one session. |
| GET | `/api/v1/sessions/{session_id}/blocked` | `listSessionBlocked` | `blocklist:read` | cookie, bearer | Blocked requests of one session. |
| GET | `/api/v1/sessions/{session_id}/screenshots` | `listSessionScreenshots` | `sessions:read` | cookie, bearer | Screenshots of one session (image URLs accept grants). |
| GET | `/api/v1/sessions/{session_id}/timeline` | `getSessionTimeline` | `sessions:read` | cookie, bearer | Merged timeline of tool calls, pages, attention, vault and blocked rows. |
| GET | `/api/v1/sessions/{session_id}/screenshots/{event_id}` | `getScreenshotImage` | `sessions:read` | cookie, bearer, grant | Screenshot bytes (cookie, bearer or `?grant=` for route `screenshot` = event id). |
| GET | `/api/v1/sessions/{session_id}/trace.zip` | `getTraceZip` | `sessions:read` | cookie, bearer, grant | The session trace (single `Range` supported; `?grant=` for route `trace` = session id). |
| HEAD | `/api/v1/sessions/{session_id}/trace.zip` | `headTraceZip` | `sessions:read` | cookie, bearer, grant | Trace size probe. |
| GET | `/api/v1/sessions/{session_id}/trace` | `getSessionTrace` | `sessions:read` | cookie, bearer | Trace descriptor. `viewer_url` embeds the trace.zip URL; the client appends `?grant=` to that inner URL. |
| POST | `/api/v1/sessions/{session_id}/data-dir/reveal` | `revealSessionDataDir` | `sessions:read` | cookie, bearer | Open the session's data directory in the host file manager (honest result). |
| POST | `/api/v1/sessions/{session_id}/terminate` | `terminateSession` | `sessions:write` | cookie, bearer | Close a live session (operator reason). |
| POST | `/api/v1/sessions/{session_id}/archive` | `archiveSession` | `sessions:write` | cookie, bearer | Archive a finished session (exempt from retention). |
| POST | `/api/v1/sessions/{session_id}/unarchive` | `unarchiveSession` | `sessions:write` | cookie, bearer | Unarchive a session. |
| DELETE | `/api/v1/sessions/{session_id}` | `deleteSession` | `sessions:write` | cookie, bearer | Terminate if live, then delete rows and artifacts. |
| POST | `/api/v1/sessions/{session_id}/viewport` | `setSessionViewport` | `sessions:write` | cookie, bearer | Resize the active page viewport; not attention-gated (D-10). |
| POST | `/api/v1/sessions/{session_id}/input` | `sendSessionInput` | `sessions:takeover` | cookie, bearer | Operator takeover input; each input re-checks the open takeover attention request (per-item results). |
| GET | `/api/v1/sessions/{session_id}/export` | `exportSession` | `sessions:read` | cookie, bearer | Streamed timeline export (NDJSON or CSV by `Accept`), capped at 100k rows. |

## Activity and metrics

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/tool-calls` | `listToolCalls` | `sessions:read` | cookie, bearer | Tool calls across sessions (live feed seed, fleet error views). |
| GET | `/api/v1/activity` | `getActivity` | `sessions:read` | cookie, bearer | Gap-filled activity buckets (≤ 720) and headline counters. |
| GET | `/api/v1/metrics/tools` | `getToolMetrics` | `sessions:read` | cookie, bearer | Per-tool call counts, error rate and latency percentiles. |

## Websites (pages)

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/pages` | `listPages` | `sessions:read` | cookie, bearer | Pages across sessions (navigation history) with category facets. |
| GET | `/api/v1/pages/recent` | `listRecentPages` | `sessions:read` | cookie, bearer | Most recent page visits across sessions. |
| GET | `/api/v1/pages/domains` | `listPageDomains` | `sessions:read` | cookie, bearer | Most visited domains (all-time when no window). |

## Attention

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/attention` | `listAttention` | `attention:read` | cookie, bearer | Attention requests (open and history) with the live open count and status/mode facets. |
| POST | `/api/v1/attention/{request_id}/resolve` | `resolveAttention` | `attention:resolve` | cookie, bearer | Resolve or reject an open attention request. |
| POST | `/api/v1/attention/bulk` | `bulkAttention` | `attention:resolve` | cookie, bearer | Resolve or reject several attention requests (per-item results). |

## Vault

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/vault/confirm` | `listVaultConfirm` | `vault:read` | cookie, bearer | Vault fill confirmations (open and history). |
| POST | `/api/v1/vault/confirm/{request_id}/resolve` | `resolveVaultConfirm` | `vault:confirm` | cookie, bearer | Approve or deny a pending vault fill (`reason` is audit-only). |
| POST | `/api/v1/vault/confirm/bulk` | `bulkVaultConfirm` | `vault:confirm` | cookie, bearer | Approve or deny several vault confirmations (per-item results). |
| GET | `/api/v1/vault` | `getVault` | `vault:read` | cookie, bearer | Backend capabilities, unlock descriptor and counts (never shells out). |
| GET | `/api/v1/vault/status` | `getVaultStatus` | `vault:read` | cookie, bearer | Lock state (may call the backend). |
| POST | `/api/v1/vault/unlock` | `unlockVault` | `vault:write` | cookie, bearer | Unlock with the secret `unlock.mode` names (Bitwarden: a session token, never the master password). |
| POST | `/api/v1/vault/lock` | `lockVault` | `vault:write` | cookie, bearer | Forget the backend session. |
| POST | `/api/v1/vault/sync` | `syncVault` | `vault:write` | cookie, bearer | Refresh the backend's local cache. |
| GET | `/api/v1/vault/groups` | `listVaultGroups` | `vault:read` | cookie, bearer | Backend groups with item/binding coverage, policies and same-name duplicates. |
| PUT | `/api/v1/vault/groups/{group_id}/policy` | `putVaultGroupPolicy` | `vault:write` | cookie, bearer | Create or update a group policy (`If-Match: <version>` on update). |
| GET | `/api/v1/vault/items` | `listVaultItems` | `vault:read` | cookie, bearer | Backend items with derived handles and binding coverage. |
| GET | `/api/v1/vault/bindings` | `listVaultBindings` | `vault:read` | cookie, bearer | Stored bindings, ordered by handle. |
| PUT | `/api/v1/vault/bindings/{handle}` | `putVaultBinding` | `vault:write` | cookie, bearer | Create (item_name required) or update a binding (`If-Match: <version>`). |
| DELETE | `/api/v1/vault/bindings/{handle}` | `deleteVaultBinding` | `vault:write` | cookie, bearer | Remove a binding. |
| POST | `/api/v1/vault/bindings/resolve` | `resolveVaultBindings` | `vault:read` | cookie, bearer | Dry-run the fill gates of every binding against a URL. |
| GET | `/api/v1/vault/log` | `listVaultLog` | `vault:read` | cookie, bearer | Vault access audit log. |
| GET | `/api/v1/vault/export` | `exportVault` | `vault:read` | cookie, bearer | Export bindings and policies as the v3 document. |
| POST | `/api/v1/vault/import` | `importVault` | `vault:write` | cookie, bearer | Import a v3 document (`?mode=merge\|replace`). |

## Blocklist

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/blocklist` | `getBlocklist` | `blocklist:read` | cookie, bearer | Loaded patterns with hit counts, skipped lines and window stats. |
| POST | `/api/v1/blocklist/reload` | `reloadBlocklist` | `blocklist:write` | cookie, bearer | Re-read the blocklist file; on failure the previous list stays active. |
| GET | `/api/v1/blocklist/attempts` | `listBlockedAttempts` | `blocklist:read` | cookie, bearer | Blocked request audit (served even when no blocklist is configured). |

## System and configuration

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/system` | `getSystem` | `system:read` | cookie, bearer | Server facts, runtime, capacity, retention, storage, telemetry and open degradations. |
| GET | `/api/v1/system/config` | `getSystemConfig` | `system:read` | cookie, bearer | Every config key with its value, source and shadowed values (secrets redacted). |
| GET | `/api/v1/system/realtime` | `getSystemRealtime` | `system:read` | cookie, bearer | Open realtime connections with topics, screencasts and backpressure counters. |
| PATCH | `/api/v1/system/log-level` | `setLogLevel` | `system:write` | cookie, bearer | Change the log level spec at runtime (`info,sessions=debug`). |
| GET | `/api/v1/system/events` | `listSystemEvents` | `system:read` | cookie, bearer | Degradations (`resolved=open` by default). |

## Logs

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/logs` | `listLogs` | `logs:read` | cookie, bearer | Records from the in-process ring buffer: newest first by default (`dir=desc`, the cursor pages to older records); `dir=asc` pages oldest to newest; `after_seq` bounds to newer records. |
| GET | `/api/v1/logs/export` | `exportLogs` | `logs:read` | cookie, bearer | Every matching ring-buffer record as NDJSON. |

## API description

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/openapi.json` | `getOpenApi` | — | public | This OpenAPI 3.1 document. |
| GET | `/api/v1/docs` | `getDocs` | — | public | API reference UI (admin surface only). |

## Notifications and preferences

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/notifications` | `listNotifications` | `notifications:read` | cookie, bearer | Notifications newest first with the unread count. |
| POST | `/api/v1/notifications/{notification_id}/read` | `markNotificationRead` | `notifications:write` | cookie, bearer | Mark one notification read. |
| POST | `/api/v1/notifications/read-all` | `markAllNotificationsRead` | `notifications:write` | cookie, bearer | Mark every notification read. |
| DELETE | `/api/v1/notifications/{notification_id}` | `dismissNotification` | `notifications:write` | cookie, bearer | Dismiss one notification. |
| POST | `/api/v1/notifications/dismiss-all` | `dismissAllNotifications` | `notifications:write` | cookie, bearer | Dismiss every notification. |
| GET | `/api/v1/me/preferences` | `getPreferences` | — | cookie, bearer | The caller's stored preferences (known keys only). |
| PUT | `/api/v1/me/preferences` | `putPreferences` | `preferences:write` | cookie, bearer | Replace the preferences document (≤ 64 KiB; unknown keys rejected). |

## Search

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/search` | `search` | `sessions:read` | cookie, bearer | Entity search for the command palette. |

## Client errors

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| POST | `/api/v1/client-errors` | `reportClientError` | — | cookie, bearer | Record an uncaught dashboard error (rate-limited 30/min). |

## Realtime

| Method | Path | operationId | Scope | Auth | Summary |
|---|---|---|---|---|---|
| GET | `/api/v1/ws` | `wsUpgrade` | — | cookie, bearer | Realtime WebSocket (`Sec-WebSocket-Protocol: browserhive.v1`). Auth failures upgrade then close 4401; see the WS protocol. |