pleach
Build

API routes

The HTTP + SSE wire contract — eight routes that any runtime client speaks, with request/response shapes for each.

API routes are one surface in the frontend integration thematic island — siblings of react, server, query, and devtools.

The eight routes below are the canonical HTTP + SSE wire protocol that runtime clients speak. The Next.js handlers in @pleach/core are the reference implementation, but the shapes are language-agnostic — an independent native client consumes them in production, which is what keeps the wire honest.

Mount the routes under /api/harness/* (the path prefix is convention, not contract — re-mount wherever you like; the relative shapes are what matters).

The shipped, published route factory is createPleachRoute from @pleach/core/quickstart — a Web-standard POST handler for the execute path:

// app/api/harness/sessions/[id]/execute/route.ts (Next.js)
import { createPleachRoute } from "@pleach/core/quickstart";

// Provider auto-detected from the environment; pass
// { provider, plugins, tools, storage } to override.
export const POST = createPleachRoute();

The full eight-route handler set documented below (HarnessServer + ROUTES) is an internal substrate surface — see Server. It is NOT a published @pleach/core/server subpath today, so mount it from the reference Next.js handlers or implement the wire contract directly. createPleachRoute above is the one published factory and covers the single-POST execute path.

Route catalog

RouteMethodPurpose
/api/harness/healthGETHealth check + component diagnostics
/api/harness/sessionsGETList sessions for the authenticated user
/api/harness/sessionsPOSTCreate a new session
/api/harness/sessions/[id]GETRetrieve session state
/api/harness/sessions/[id]PUTUpdate session state
/api/harness/sessions/[id]DELETEDelete a session
/api/harness/sessions/[id]/syncPOSTVersion-vector sync (conflict detection)
/api/harness/sessions/[id]/executePOSTExecute a message (SSE streaming response)

GET /api/harness/health

Health probe + component-level diagnostics. Cheap; safe to hit from a load balancer.

Response 200

{
  "ok": true,
  "version": "1.1.0",
  "components": {
    "storage":      { "ok": true },
    "checkpointer": { "ok": true },
    "providers":    { "ok": true, "configured": ["anthropic"] }
  }
}

Response 503 — runtime disabled via FEATURE_HARNESS_V2_RUNTIME=false.

GET /api/harness/sessions

List sessions owned by the authenticated user.

Query params

ParamTypePurpose
limitnumberPage size (default 50, max 200)
cursorstringOpaque pagination cursor
archivedbooleanInclude archived sessions (default false)

Response 200

{
  "sessions": [
    { "id": "018f...", "createdAt": "...", "lastActiveAt": "...", "title": "..." }
  ],
  "nextCursor": "01jc..."
}

POST /api/harness/sessions

Create a new session.

Request body

{
  "provider": { "type": "anthropic" },
  "model":    { "id": "claude-sonnet-4-5" },
  "tools":    { "enabled": ["search", "calculator"] }
}

All fields optional; defaults match SessionRuntime.createSession.

Response 201

{ "session": { "id": "018f...", "createdAt": "...", "..." } }

GET /api/harness/sessions/[id]

Retrieve a session's full state.

Response 200 — the SessionState shape (messages, pendingToolCalls, completedToolCalls, pendingJobs, artifacts, versionVector, etc.).

Response 404 — session not found or not visible to the authenticated user. Code 2001.

PUT /api/harness/sessions/[id]

Update session state. The request body is the next SessionState; the server validates the version vector and rejects stale writes with 3001.

Request body

{
  "version": 7,
  "versionVector": { "client_abc": 4, "server": 3 },
  "messages": [/* ... */],
  "tools": { "enabled": ["search"] }
}

Response 200 — the updated state.

Response 409 — version conflict. Code 3001. Body:

{
  "code": "3001",
  "error": "Version conflict",
  "remote": { /* the server's current state */ }
}

The client should resolve the conflict (typically by surfacing a chooser UI) and re-PUT with the merged result.

DELETE /api/harness/sessions/[id]

Delete a session. Cascades into the checkpointer; audit-ledger rows are preserved (append-only).

Response 204 — no content.

POST /api/harness/sessions/[id]/sync

Version-vector sync. The client pushes its local vector + queued changes; the server returns missing-from-remote changes plus the merged vector.

Request body

{
  "clientId": "client_abc",
  "vector":   { "client_abc": 5 },
  "changes":  [/* SessionChange[] */]
}

Response 200

{
  "mergedVector":  { "client_abc": 5, "server": 3 },
  "remoteChanges": [/* changes the client hasn't seen yet */],
  "applied":       3,
  "rejected":      0
}

Response 409 — concurrent vector that the merger couldn't resolve. Code 3001 or 3002.

POST /api/harness/sessions/[id]/execute

Execute a message. SSE streaming response. Each event from runtime.executeMessage becomes one SSE data: frame.

Request body

{
  "content": "Summarize the latest changelog entries.",
  "fileReferences": []
}

Response 200Content-Type: text/event-stream. Each line:

data: {"type":"message.delta","delta":"The"}\n\n
data: {"type":"tool.started","toolCall":{...}}\n\n
data: {"type":"tool.completed","toolCall":{...},"result":{...}}\n\n
data: {"type":"message.complete","message":{...}}\n\n
data: {"type":"step.end","step":"post-turn"}\n\n

See Stream events for the full event catalog.

Aborting mid-stream

Close the SSE connection (EventSource.close() or fetch AbortController.abort()). The runtime cancels in-flight provider calls and flushes a final ledger row with outcome.status: "user-aborted".

Reconnection

The runtime emits stream.reconnecting and stream.disconnected events on retry. A reference client reconnects on EventSource.error and replays from the last seen event id. The server is idempotent on messageId — re-submitting the same message after a reconnect doesn't fork the turn.

Auth headers

The reference handlers expect:

HeaderPurpose
Authorization: Bearer <token>User auth — surfaces as runtime.userId
X-Organization-IdMulti-tenant scoping
X-Client-IdSync version-vector key for this client

Mock-mode (HARNESS_MOCK_MODE=true) accepts all requests as anonymous for local development.

Error response shape

Every non-2xx body follows the same shape:

{
  "code": "2001",
  "error": "Session not found",
  "recoveryHint": "The session id may be stale; try refreshing the session list."
}

code is the structured error code — see Error codes for the catalog.

Where to go next

On this page