Skip to main content
Generated from openapi.json and the file-based TanStack Start routes under apps/web/src/routes/api/. Regenerate the OpenAPI document with pnpm generate:docs. Base URL: http://localhost:3000 All request bodies are validated with zod schemas in apps/web/src/lib/pi/chat-protocol.zod.ts. Responses are JSON unless otherwise noted; /api/chat returns an NDJSON event stream.

Chat

POST /api/chat

Send a chat message and receive a streaming response. Request body
Responses
  • 200 — NDJSON stream of ChatStreamEvent lines. See stream events.
  • 400 — Validation error.

POST /api/chat/abort

Abort the active chat session. Request body
Responses
  • 200{ "aborted": true | false }
  • 500{ "message": "<error>" }

GET /api/chat/models

List available chat models. Responses
  • 200{ models, selectedModelKey, defaultProvider, defaultModel, defaultThinkingLevel, diagnostics }
  • 500{ message }

GET /api/chat/providers · POST /api/chat/providers

Read or update provider credential state. GET reports which providers have credentials configured (best-effort, based on env-var presence — actual auth is verified at runtime). POST updates the matching env var entry through the in-app config panel. GET response
Known providers: amazon-bedrock, openai, anthropic, google-vertex, google-genai (Gemini), mistral, groq, ollama.

GET /api/chat/commands

List slash commands available in the chat input. Populates the /-triggered command menu with built-ins plus any active skills and prompts. See slash commands for the UX and keyboard shortcuts. Response
source is one of builtin, skill, prompt, or extension. Skill and prompt entries are omitted when enableSkillCommands is disabled in Pi settings, and diagnostics will explain why. Responses
  • 200{ commands, diagnostics }
  • 500{ message }

POST /api/chat/new

Create a new chat session. Responses
  • 200{ sessionFile, sessionId }
  • 500{ message }

GET /api/chat/provenance

Fetch per-run provenance records. Responses
  • 200 — provenance payload
  • 500{ message }

POST /api/chat/question

Answer a questionnaire follow-up question raised by the assistant. Request body
Responses
  • 200{ ok, message, mode, planAction }
  • 400 — Bad request.
  • 404{ ok: false, message }

GET /api/chat/resources

List available chat resources (skills, prompts, extensions, themes, packages, AGENTS.md files). Responses
  • 200{ packages, skills, prompts, extensions, themes, agentsFiles, diagnostics }
  • 500{ message }

POST /api/chat/resume

Resume an existing chat session. Request body
Responses
  • 200{ session, messages, sessionReset }
  • 500{ message }

POST /api/chat/run

Execute a single recorded run.

GET /api/chat/runs

List recorded runs for a session.

GET /api/chat/session · DELETE /api/chat/session

Hydrate or delete a chat session by query parameters. DELETE removes the caller’s owned mirror row in Neon (cascading run, tool, and file mutation rows) and best-effort cleans up the ephemeral JSONL file. Requires an authenticated Better Auth session and passes ownership verification against the Neon mirror. GET responses
  • 200{ session, messages, sessionReset }
  • 500{ message }
DELETE responses
  • 200{ ok: true, sessionId, sessionFile }
  • 401 — Unauthorized.
  • 403 — Session is not owned by the caller.
  • 404 — Session not owned or missing ({ ok: false, reason: "session-not-owned-or-missing" }).
  • 501 — Mirror disabled (FLEET_PI_CHAT_DATABASE_URL not set).
  • 503 — Mirror unavailable.

DELETE /api/chat/account

Erase all mirrored Pi data for the signed-in user: every row in pi_sessions (cascading transcripts, runs, tool output, and file mutations) and every BYOK provider credential in pi_user_providers. Ownership is enforced under Neon RLS and only the caller’s data is touched. Better Auth identity rows are not deleted — coordinate account deletion in Better Auth separately if required. Workspace files on disk are not reverted. Responses
  • 200{ ok: true, scope: "pi-mirror", erasedSessions, erasedProviders, message }
  • 401 — Unauthorized.
  • 500{ ok: false, reason, message }

GET /api/chat/sessions

List all chat sessions. Responses
  • 200{ sessions }
  • 500{ message }

GET /api/chat/settings · POST /api/chat/settings

Read or patch effective ChatPiSettings: compaction, retry, default model and thinking level, enabled models, extensions, packages, prompts, skills, themes, transport, and steering / follow-up delivery modes. POST request body
POST response

Workspace

Workspace endpoints read and write the durable layer described by the adaptive workspace contract.
Every /api/workspace/* route resolves the caller’s user-scoped workspace and rejects unauthenticated callers with 401 Unauthorized on protected deployments (Vercel, Neon Managed Auth, Neon Function surface, or FLEET_PI_CHAT_RUNTIME_REQUIRE_AUTH=1). Send either a Neon Auth Authorization: Bearer <token> header or an authenticated session cookie. Local development without auth configured continues to serve the repo-scoped workspace.

GET /api/workspace/health

Workspace contract health (manifest version, missing canonical paths, projection state).

GET /api/workspace/file

Read a canonical workspace file.

GET /api/workspace/item · GET /api/workspace/items

Fetch a single workspace item or list items.

GET /api/workspace/tree

Browse the workspace tree.

GET /api/workspace/search

Search workspace items via the projection index in agent-workspace/indexes/.

GET /api/workspace/reindex · POST /api/workspace/reindex

Rebuild the projection index. The canonical files are not modified — the index is recomputed. On protected deployments, POST /api/workspace/reindex is protected by a double-submit CSRF token and a per-user rate limit:
  • Rate limit — up to 5 requests per minute per user. Excess calls return 429 Too Many Requests with Retry-After: 60.
  • CSRF token — cookie-authenticated callers must first GET /api/workspace/reindex to receive a fleet_pi_csrf cookie (HttpOnly, SameSite=Strict) plus a csrfToken in the response body, then echo the token back on the POST as the x-fleet-csrf-token header. Same-origin Origin (or Referer) is also required. Bearer-token callers skip CSRF because they are not vulnerable to cross-site cookie replay.
Example
Responses
  • 200 — reindex completed.
  • 403{ "message": "Invalid CSRF protection" } when the CSRF token, cookie, or same-origin check fails.
  • 429{ "message": "Workspace reindex rate limit exceeded" } with Retry-After: 60.

Outbound network safety

The chat runtime’s web_fetch tool only accepts public HTTPS URLs. Requests to localhost, loopback, RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), link-local (169.254.0.0/16), or IPv6 unique-local (fc00::/7) addresses are rejected before any connection is opened, and DNS lookups are pinned so a public hostname cannot rebind to a private address between resolution and connect. GitHub blob/ URLs are automatically rewritten to raw content URLs. Only text/* and application/json responses are returned; binary payloads are refused.

Sandbox

Daytona-backed user sandboxes. Each authenticated user gets one isolated sandbox in their Daytona account (BYOK). Tool calls run inside the container, and a persistent volume mounted at /home/daytona/agent-workspace keeps the user’s workspace across restarts. All routes require Better Auth.

GET /api/sandbox/preview

Return a signed preview URL into the calling user’s active Daytona sandbox. Responses
  • 200{ url } — short-lived preview URL into the sandbox.
  • 400{ error: "Invalid port" }
  • 401{ error: "Authentication required" }
  • 404{ error: "No active sandbox for user" }
  • 503{ error: "Sandbox not available" } — Daytona is not enabled for the calling user. On Vercel this typically means the user has not saved a daytona provider secret; locally it usually means DAYTONA_API_KEY is unset.

POST /api/webhooks/daytona

Receive lifecycle events from Daytona (sandbox start, stop, archive, delete). Side effects are applied only when the x-daytona-signature header verifies against DAYTONA_WEBHOOK_SECRET; otherwise the call is accepted and logged but ignored. Headers
  • x-daytona-signature — HMAC signature of the request body using DAYTONA_WEBHOOK_SECRET.
Responses
  • 200{ received: true }
  • 500{ error: "Webhook processing failed" }
See the configuration reference for the required environment variables.

Authentication

Fleet Pi routes follow a deployment-based auth policy:
  • Local development (no NEON_AUTH_BASE_URL / NEON_AUTH_URL and not running on Vercel): chat and workspace endpoints allow anonymous access so the app works without login.
  • Vercel, Neon Managed Auth, or dual-host chat runtime: session, catalog, and workspace routes require authentication. This covers /api/chat/* streaming and session routes, the catalog endpoints (/api/chat/models, /api/chat/resources, /api/chat/commands, /api/chat/models/discover), and every route under /api/workspace/*. Unauthenticated requests receive 401.
  • /api/sandbox/preview always requires authentication.
Neon Managed Auth clients mint a JWT via authClient.token() and send Authorization: Bearer <jwt> on every request. Better Auth clients use signed session cookies.

/api/auth/*

Proxies Neon Managed Auth when NEON_AUTH_BASE_URL (or the Vercel↔Neon NEON_AUTH_URL) is set. Otherwise it serves Better Auth when BETTER_AUTH_SECRET is set. See configuration and the Neon Managed Auth reference for required environment variables.

Health

GET /api/health

Smoke endpoint used by the quickstart. Responses
  • 200{ "status": "ok" }

Stream events

/api/chat emits one JSON object per newline. Every event has a type discriminator. The full union is ChatStreamEvent in chat-protocol.ts. Plan events carry a ChatPlanState shape:

Chat modes

Which tools each mode unlocks.

Architecture

How requests flow from the browser through the configured model provider and back.
Last modified on August 9, 2026