REST API
Base path /api. JSON in and out (Content-Type: application/json). Field names camelCase. The TypeScript mirror lives in web/src/api/types.ts in the source repo.
The web app, the stage CLI and both MCP servers use this API. See Agents for the agent front ends.
| Page | Covers |
|---|---|
| Projects | project mirror of T3 projects |
| Tasks | tasks, state mapping, link rules, handoff, predecessor base, session batch |
| Schedules | recurring schedules and schedule rules |
| Threads | history, search, transcript, pending questions, queued continuations |
| Notifications | Web Push, notifications inbox, notification rules |
| Other endpoints | images, snippets, preferences, stats, agent interface |
Conventions
- Timestamps: ISO 8601 strings (UTC, e.g.
"2026-11-08T10:00:00Z"). Nullable fields arenull, never omitted. - IDs: app ids (
id,projectId,taskId,afterTaskId,nextTaskId) are JSON numbers. T3 ids (t3ProjectId,t3ThreadId) are strings. - Lists are wrapped:
{ "projects": [...] },{ "tasks": [...] }, ... - PATCH bodies are partial: omitted fields are unchanged;
nullclears a nullable field. - Repeatable list query params accept repetition or commas:
?state=running&state=needs_input==?state=running,needs_input. from/toquery params: ISO date (2026-11-01) or datetime.frominclusive,toexclusive. Filter oncreatedAtunless stated.- Errors: non-2xx responses have body
{ "error": string }. Some 409s add a machine-readable"code"(see Pending questions).
| Status | Meaning |
|---|---|
| 400 | invalid body or query (bad enum, missing required field, bad JSON) |
| 404 | resource not found |
| 409 | conflict: action not allowed in current state, duplicate unique value |
| 502 | T3 call failed (T3 error message in error) |
| 503 | T3 not connected (no valid token) or DB unavailable |
| 500 | unexpected |
Enums
| Name | Values |
|---|---|
| TaskState | backlog launching running needs_input done settled failed interrupted cancelled (done: the run finished, not yet settled in T3; T3 calls this completed) |
| ThreadStatus (T3) | idle preparing queued starting running waiting completed interrupted failed cancelled rolled_back |
| RuntimeMode | approval-required auto-accept-edits auto full-access (task default full-access) |
| InteractionMode | default plan (default default) |
| SendMode | auto queue steer restart (default auto) |
| ProjectSource | app (created via stage) t3 (imported from T3) |
| ThreadSource | app (thread belongs to a task) t3 (any other thread) |
Shared shapes
// ModelOption: T3 ProviderOptionSelection
{ "id": "effort", "value": "low" } // value: string | boolean
// Workspace
{ "type": "root" }
{ "type": "worktree", "baseRef": "origin/main", "branch": "feat-x", "base": "ref" } // branch: string | null (null = T3 picks)Workspace maps to T3 workspaceStrategy: root → {type:"root"}; worktree → {type:"worktree", baseRef, branch?, startFromOrigin: baseRef has a "<remote>/" prefix} (remote = origin or upstream; feature/x is treated as local).
base (worktree only, always emitted; ref when omitted on input): ref starts from baseRef; predecessor starts a linked task's worktree from its predecessor's branch, with baseRef as the fallback (see Predecessor base). Invalid values, predecessor with root, and predecessor on a schedule are 400.
Health
GET /api/health
200 / 503:
{ "status": "ok", "db": "ok" }T3 connection, sync, catalog
GET /api/t3/status
200 T3Status:
{
"connected": true,
"t3Url": "http://127.0.0.1:3773",
"publicUrl": "http://t3.example.com:3773",
"environmentId": "env_123",
"clientId": "mcp_client_abc",
"tokenExpiresAt": "2026-12-08T10:00:00Z",
"adminTokenConfigured": true,
"lastError": null,
"lastFullSyncAt": "2026-11-08T10:00:00Z"
}environmentId, clientId, tokenExpiresAt, lastError, lastFullSyncAt are nullable.
POST /api/t3/pair
Paste fallback: exchange a pairing code from t3 pair for a token.
Body: { "code": "ABCD-1234" }. 200 T3Status. 400 empty code. 502 pairing failed.
POST /api/sync
Run a full sync now (all T3 projects + threads into the mirror; creates app projects for new T3 projects; project title, root and archive state follow T3). Also reads top-level session details to populate normalized state and run metadata independently of legacy task state. Synchronous.
200 SyncResult:
{ "projects": 12, "threads": 776, "finishedAt": "2026-11-08T10:00:00Z" }409 sync already running. 503 not connected. 502 T3 error.
GET /api/catalog
Cached orchestrator_capabilities (refreshed every 10 min). Only providers with no T3 constraints are listed.
200 Catalog:
{
"providers": [
{
"providerInstanceId": "claude",
"driverKind": "claude",
"displayName": "Claude",
"models": [
{
"id": "claude-haiku-5-5",
"label": "Haiku 5.5",
"options": [{ "id": "effort", "values": ["low", "medium", "high"], "default": "medium" }]
}
],
"runtimeModes": ["approval-required", "auto-accept-edits", "auto", "full-access"]
}
],
"fetchedAt": "2026-11-08T10:00:00Z"
}displayName falls back to providerInstanceId; label falls back to id; options is [] when none; option default is nullable; runtimeModes is null when T3 does not report it. 503 not connected and no cache.