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

# HTTP API

> The brain's HTTP surface: the MCP endpoint, OAuth discovery, and the JSON routes the web UI uses — grouped, with auth requirements.

The brain is a FastAPI app. Agents use `POST /mcp`; the web UI uses the `/api/…` routes below with the same credentials. All project-scoped routes enforce membership; non-members get 404.

Auth legend — **A**: normal gateway auth (Supabase session JWT, `rr_sk_` key, or `mk_` key + membership; runner child sessions carry a `mas_` credential valid only on `/mcp`) · **PUB**: no auth · **TOK**: a one-time or HMAC token in the path is the credential · **OP**: operator `X-Admin-Token`.

## MCP and discovery

| Method     | Path                                        | Auth | Purpose                                                                               |
| ---------- | ------------------------------------------- | ---- | ------------------------------------------------------------------------------------- |
| POST       | `/mcp`                                      | A    | Streamable-HTTP MCP JSON-RPC. The only path that accepts OAuth audience-bound bearers |
| GET        | `/.well-known/oauth-authorization-server`   | PUB  | OAuth metadata (RFC 8414)                                                             |
| GET        | `/.well-known/oauth-protected-resource/mcp` | PUB  | Protected-resource metadata (RFC 9728)                                                |
| POST       | `/oauth/register`                           | PUB  | Dynamic client registration                                                           |
| GET / POST | `/oauth/authorize`                          | PUB  | Consent (PKCE)                                                                        |
| POST       | `/oauth/token`                              | PUB  | Token exchange and refresh                                                            |

OAuth routes exist only when the brain has an OAuth service configured (hosted and authenticated self-host); a loopback brain has none.

## Health and meta

| Method | Path                                                                                               | Auth                                                                                                          |
| ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| GET    | `/health`                                                                                          | PUB                                                                                                           |
| GET    | `/api/meta`                                                                                        | PUB — advertises auth requirements, Supabase URL/anon key for the UI, the client version floor, feature flags |
| GET    | `/api/activity`, `/api/debug/tool-calls`, `/api/debug/tool-calls/{call_id}`                        | A; `?project_id=` required; `mk_` keys get 403                                                                |
| POST   | `/api/debug/tool-calls/clear`, `/api/admin/cleanup`, GET `/api/admin/tenants/{tenant_id}/counters` | OP                                                                                                            |

Clients send `X-RP-Client-Version`; below the floor → HTTP 426, checked before auth.

## Projects, members, keys

| Method            | Path                                                                                    |
| ----------------- | --------------------------------------------------------------------------------------- |
| GET / POST        | `/api/projects`                                                                         |
| GET / PATCH / PUT | `/api/projects/{project_id}`                                                            |
| GET               | `/api/projects/{project_id}/home`, `…/status`                                           |
| GET / POST        | `/api/projects/{project_id}/members` — POST body `{"user_id": "…"}` or `{"email": "…"}` |
| DELETE            | `/api/projects/{project_id}/members/{user_id}`                                          |
| POST / GET        | `/api/projects/{project_id}/keys` — create (member) / list (owner-scoped)               |
| POST              | `/api/projects/{project_id}/keys/{key_id}/revoke`                                       |
| PUT / DELETE      | `/api/user/hf-token` — the caller's Hugging Face token for sandboxes                    |

## Research records

| Method     | Path                                                                                                                                                                             |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GET / POST | `/api/projects/{project_id}/claims`; GET / PATCH / PUT `…/claims/{claim_id}`                                                                                                     |
| GET / POST | `/api/projects/{project_id}/experiments` (`?status=`); GET `…/experiments/view`                                                                                                  |
| GET        | `…/experiments/{experiment_id}`, `…/status`, `…/figure`, `…/graph`                                                                                                               |
| POST       | `…/experiments/{experiment_id}/transition`                                                                                                                                       |
| GET        | `/api/projects/{project_id}/litreview` (ETag)                                                                                                                                    |
| GET        | `/api/projects/{project_id}/reflections`; `…/reflections/current/graph`; `…/reflections/{reflection_id}`, `…/graph`, `…/consolidation`                                           |
| GET        | `/api/projects/{project_id}/reviews` (filter with `?target_id=`; `?target_type=` alone returns the whole queue); POST `…/reviews/request`, `…/reviews/start`, `…/reviews/submit` |
| GET        | `/api/projects/{project_id}/events` (ETag; keep `?limit=` ≤ 500); `…/events/stream` (SSE; `?since=`, `?poll_ms=`, `?max_ms=`)                                                    |

Under auth, SSE can't carry the header — the UI falls back to ETag polling.

## Artifacts and storage

| Method     | Path                                                                                                                    | Auth                                               |
| ---------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| PUT        | `/api/artifacts/u/{token}`, `/api/artifacts/f/{token}`                                                                  | TOK — upload targets returned by `artifact.submit` |
| GET        | `/api/projects/{project_id}/artifacts`; `…/artifacts/{artifact_id}/content`, `…/file`, `…/figure?rel=` (`rel` required) | A                                                  |
| GET / POST | `/api/storage/u/{token}`, `/api/storage/u/{token}/complete`                                                             | TOK                                                |
| GET        | `/api/projects/{project_id}/storage` (`?kind=&status=&name=&include_expired=`); `…/storage/{object_id}`                 | A                                                  |
| POST       | `…/storage/{object_id}/download`, `…/pin`, `…/unpin`, `…/renew`; DELETE `…/storage/{object_id}`                         | A                                                  |

## Sandboxes and providers

| Method | Path                                                                                                                    | Auth                                                                        |
| ------ | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| GET    | `/api/sandboxes/health`; `/api/projects/{project_id}/sandboxes` (ETag); `…/compute-cost`                                | A                                                                           |
| GET    | `…/experiments/{experiment_id}/sandbox`, `…/sandbox/metrics`, `…/sandbox/terminal`; same by `…/sandboxes/{sandbox_uid}` | A                                                                           |
| POST   | `…/experiments/{experiment_id}/sandbox/release`; `…/sandboxes/{sandbox_uid}/release`                                    | A                                                                           |
| GET    | `/wait/{sandbox_uid}/{label}/{sig}`                                                                                     | TOK — the `wait_url` for [long runs](/docs/merv/clients/long-runs); rate-limited |
| GET    | `/api/projects/{project_id}/sandbox-providers`                                                                          | A                                                                           |
| PUT    | `…/sandbox-providers/{provider}`; POST `…/enabled`, `…/daily-limit`, `…/verify`                                         | A                                                                           |

Registered only when sandboxes are enabled.

## Feed

| Method | Path                                                                        | Auth               |
| ------ | --------------------------------------------------------------------------- | ------------------ |
| PUT    | `/api/feed/u/{token}`                                                       | TOK — media upload |
| GET    | `/api/projects/{project_id}/feed` (`?limit=` 1–100, default 30; `?cursor=`) | A                  |
| POST   | `…/feed/{post_id}/reactions`, `…/reply`, `…/feed/track`                     | A                  |
| GET    | `…/feed/{post_id}/image`, `…/link-image`, `…/embed`                         | A                  |

## Auto-run control plane

| Method            | Path                                                                                        | Auth                                        |
| ----------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------- |
| POST              | `/api/agent-runners/pairing`, `/api/agent-runners/pairing/token`                            | PUB — the runner's device-code pairing      |
| POST              | `/api/projects/{project_id}/agent-runners/pairings/approve`                                 | A (owner session; 10 misses / 10 min → 429) |
| POST              | `…/agent-runners/heartbeat`; PUT `…/agent-runners/settings`                                 | A                                           |
| POST              | `/api/agent-sessions/claim`; `…/{session_id}/attach`, `…/release`, `…/heartbeat`, `…/trace` | A (trace: owning runner only)               |
| GET               | `/api/projects/{project_id}/agent-sessions`; `…/agent-sessions/{session_id}/trace`          | A                                           |
| POST              | `…/agent-sessions/{session_id}/halt`; `…/agent-sessions/halt`                               | A                                           |
| POST / GET / POST | `…/consolidation/prepare`, `…/consolidation/pending`, `…/consolidation/settle`              | A                                           |

Pairing routes exist only on an authenticated brain — a loopback brain has no pairing.

## Off by default

MLflow tracking routes (`…/experiments/{experiment_id}/results/metrics`, `/api/projects/{project_id}/mlflow`, `/internal/auth/mlflow`) register only when tracking is enabled. `/mcp/tools` and `/mcp/call` are legacy shims; use `/mcp`.
