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
{
  "sessionFile": "<string>",
  "sessionId": "<string>",
  "message": "<user message>",
  "model": "<provider/model id or { provider, id, thinkingLevel }>",
  "mode": "agent | plan | harness",
  "planAction": "execute | refine",
  "streamingBehavior": "steer | followUp"
}
Responses
  • 200 — NDJSON stream of ChatStreamEvent lines. See stream events.
  • 400 — Validation error.

POST /api/chat/abort

Abort the active chat session. Request body
{ "sessionFile": "<string>", "sessionId": "<string>" }
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
{
  "providers": [
    { "id": "google-genai", "name": "Google Gemini", "envVarName": "GEMINI_API_KEY", "configured": true },
    { "id": "openai", "name": "OpenAI", "envVarName": "OPENAI_API_KEY", "configured": false }
  ]
}
Known providers: amazon-bedrock, openai, anthropic, google-vertex, google-genai (Gemini), mistral, groq, ollama.

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
{
  "sessionFile": "<string>",
  "sessionId": "<string>",
  "toolCallId": "<string>",
  "answer": {
    "kind": "single | multi | text | skip",
    "questionId": "<string>",
    "selectedIds": ["<id>"],
    "text": "<string>"
  }
}
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
{ "sessionFile": "<string>", "sessionId": "<string>" }
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

Hydrate a chat session by query parameters.
NameInRequiredDescription
sessionFilequeryNoSession file path
sessionIdqueryNoSession ID
Responses
  • 200{ session, messages, sessionReset }
  • 500{ 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
{ "settings": { "<ChatPiSettingsUpdate>": "..." } }
POST response
{
  "diagnostics": [],
  "effective": { "<ChatPiSettings>": "..." },
  "project": { "<ChatPiSettingsUpdate>": "..." },
  "projectPath": "<string>",
  "updateImpact": {
    "newSessionRecommended": false,
    "resourceReloadRequired": false
  }
}

Workspace

Workspace endpoints read and write the durable layer described by the adaptive workspace contract.

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/.

POST /api/workspace/reindex

Rebuild the projection index. The canonical files are not modified — the index is recomputed.

Sandbox

Daytona-backed user sandboxes. Authenticated users can be assigned an isolated sandbox for cross-surface preview. All routes require Better Auth.

GET /api/sandbox/preview

Return a signed preview URL into the calling user’s active Daytona sandbox.
NameInRequiredDescription
portqueryNoSandbox port to proxy. Defaults to 3000. Must be an integer in 1..65535.
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 disabled (no DAYTONA_API_KEY) or feature gated off for the user.

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

/api/auth/*

Mounted by Better Auth only when BETTER_AUTH_SECRET is set. Endpoints follow Better Auth’s standard surface (/sign-in/*, /sign-out, /session, etc.). See configuration 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.
typeShapeMeaning
start{ id, runId, sessionFile?, sessionId, sessionReset?, diagnostics? }Stream opened. Use runId to correlate with provenance.
delta{ text, messageId? }Streaming assistant text chunk.
thinking{ text, messageId? }Streaming chain-of-thought when thinkingLevel is enabled.
tool{ part, messageId? }Tool call or tool result rendered in the transcript.
plan{ mode, executing, completed, total, message?, state }Plan-mode progress with structured state.
state{ state }Generic chat state update.
queue{ steering: string[], followUp: string[] }Steering / follow-up prompts queued during streaming.
compaction{ phase: "start" | "end", reason, aborted?, willRetry?, errorMessage? }Session compaction lifecycle.
retry{ phase, attempt, maxAttempts?, delayMs?, success?, finalError?, errorMessage? }Retry attempt fired around a Bedrock invocation.
done{ runId, message, sessionFile?, sessionId, sessionReset? }Assistant turn finished cleanly.
error{ message, runId? }Terminal stream error.
Plan events carry a ChatPlanState shape:
type ChatPlanState = {
  mode: "agent" | "plan" | "harness"
  executing: boolean
  pendingDecision: boolean
  completed: number
  total: number
  todos: Array<{ step: number; text: string; completed: boolean }>
  message?: string
}

Chat modes

Which tools each mode unlocks.

Architecture

How requests flow from the browser through the configured model provider and back.