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

# HTTP API reference

> Reference for the Fleet Pi local web app HTTP API — the /api/chat NDJSON event stream, request and response schemas, and supporting workspace endpoints.

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](https://zod.dev/) 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**

```json theme={null}
{
  "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](#stream-events).
* **400** — Validation error.

### POST /api/chat/abort

Abort the active chat session.

**Request body**

```json theme={null}
{ "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**

```json theme={null}
{
  "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**

```json theme={null}
{
  "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**

```json theme={null}
{ "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.

| Name          | In    | Required | Description       |
| ------------- | ----- | -------- | ----------------- |
| `sessionFile` | query | No       | Session file path |
| `sessionId`   | query | No       | Session 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**

```json theme={null}
{ "settings": { "<ChatPiSettingsUpdate>": "..." } }
```

**POST response**

```json theme={null}
{
  "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](/fleet-pi/adaptive-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/`.

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

| Name   | In    | Required | Description                                                                  |
| ------ | ----- | -------- | ---------------------------------------------------------------------------- |
| `port` | query | No       | Sandbox 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](/fleet-pi/configuration#daytona-backed-user-sandboxes) for the required environment variables.

## Authentication

### `/api/auth/*`

Mounted by [Better Auth](https://www.better-auth.com/) only when `BETTER_AUTH_SECRET` is set. Endpoints follow Better Auth's standard surface (`/sign-in/*`, `/sign-out`, `/session`, etc.). See [configuration](/fleet-pi/configuration#authentication-better-auth) 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`](https://github.com/Qredence/fleet-pi/blob/main/apps/web/src/lib/pi/chat-protocol.ts).

| `type`       | Shape                                                                              | Meaning                                                     |
| ------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| `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:

```ts theme={null}
type ChatPlanState = {
  mode: "agent" | "plan" | "harness"
  executing: boolean
  pendingDecision: boolean
  completed: number
  total: number
  todos: Array<{ step: number; text: string; completed: boolean }>
  message?: string
}
```

## Related

<CardGroup cols={2}>
  <Card title="Chat modes" icon="sliders-vertical" href="/fleet-pi/chat-modes">
    Which tools each mode unlocks.
  </Card>

  <Card title="Architecture" icon="sitemap" href="/fleet-pi/architecture">
    How requests flow from the browser through the configured model provider and back.
  </Card>
</CardGroup>
