Skip to content
stage

Other endpoints

Images

Attachment: { "id": 4, "name": "error.png", "mimeType": "image/png", "sizeBytes": 48211 }.

Types: image/png|jpeg|gif|webp (sniffed from the bytes), max 10 MB each, max 8 per task or follow-up. Stored in the database. An upload is staged until a task create/update attaches it or a follow-up sends it; staged uploads older than 24h are deleted hourly.

POST /api/uploads

Multipart form, field file (one image). 201 Attachment (staged). 400 not an accepted image type, empty, over 10 MB.

GET /api/attachments/:id

200 raw image bytes (Content-Type = mimeType, immutable cache, nosniff). 404.

DELETE /api/attachments/:id

Deletes a staged upload, or an image of a backlog task. 204. 404. 409 when the task has left backlog (images are read-only after launch).

Snippets

Reusable prompt text, inserted into prompts, follow-ups and notes by typing / in the web app. projectId null = global (shown in every project); set = only in that project's tasks.

Snippet:

{
  "id": 1,
  "name": "plan-first",
  "body": "Before editing, write a plan and wait for approval.",
  "projectId": null,
  "createdAt": "2026-11-08T10:00:00Z",
  "updatedAt": "2026-11-08T10:00:00Z"
}
  • GET /api/snippets → 200 { "snippets": Snippet[] }: global first, then by project title, then name.
  • POST /api/snippets body { "name": string, "body": string, "projectId"?: number|null } → 201 Snippet. 400 blank name/body or unknown projectId. 409 duplicate name (names are unique across all scopes).
  • GET /api/snippets/:id → 200 Snippet. 404.
  • PATCH /api/snippets/:id partial of the POST body → 200 Snippet. 400, 404, 409 as above.
  • DELETE /api/snippets/:id → 204. 404.

Preferences

New-task model defaults and the global notification filter. A new task prefills from pinned, else lastUsed, else the built-in default (T3 default model, full-access, default).

ModelDefaults:

{ "providerInstanceId": "claude", "model": "claude-haiku-5-5", "modelOptions": [{ "id": "effort", "value": "low" }], "runtimeMode": "full-access", "interactionMode": "default" }

providerInstanceId / model null = T3 default (set together).

GET /api/preferences

200 { "pinned": ModelDefaults | null, "lastUsed": ModelDefaults | null, "notifyEvents": NotificationEvent[] }. lastUsed is written by every POST /api/tasks (its model fields). notifyEvents (default all: ["needs_input","failed","done","settled"], in that order) is the global filter of Notification rules.

PUT /api/preferences/pinned

Body: ModelDefaults (pin; runtimeMode / interactionMode default to full-access / default when empty) or null (unpin). Empty body 400.

200 Preferences. 400 invalid enum or provider/model not set together.

PUT /api/preferences/notifications

Body: { "events": NotificationEvent[] } (needs_input|failed|done|settled; duplicates dropped; [] = all off). Applies to every device.

200 Preferences. 400 unknown event or missing events.

Stats

GET /api/stats

Query: from, to (default last 30 days: to = now, from = now - 30d), tz (IANA zone for day buckets, default UTC), subagents (include|exclude, default include; exclude drops subagent threads from all thread counts). Thread counts use mirrored threads by createdAt in range; task counts are current totals (not range-filtered).

200 Stats:

{
  "from": "2026-10-09T10:00:00Z",
  "to": "2026-11-08T10:00:00Z",
  "tz": "UTC",
  "threadsByDay": [{ "day": "2026-11-08", "count": 14 }],
  "threadsByProject": [{ "projectId": 1, "t3ProjectId": "proj_abc", "title": "stage", "count": 9 }],
  "threadsByStatus": [{ "status": "completed", "count": 20 }],
  "tasksByState": [{ "state": "running", "count": 2 }],
  "needsInput": 1
}

threadsByDay ascending, days with zero omitted. threadsByProject count desc; projectId null and title = t3ProjectId when no app project. needsInput = tasks in needs_input. 400 bad tz or subagents.

Agent interface

stage subcommands (CLI, --json), stage mcp (stdio MCP) and POST|GET /mcp (streamable HTTP MCP, stateless, served by the app) expose the same operations, all through this REST API (HTTP MCP calls the app's own /api on loopback). Guide: Agents.

Tools: get_context list_projects list_models list_tasks get_task create_task update_task delete_task start_task stop_task followup_task duplicate_task link_task order_tasks unlink_task wait_task list_schedules get_schedule create_schedule update_schedule delete_schedule run_schedule preview_schedule list_sessions search_sessions get_session get_session_questions.

  • get_session_questions / stage sessions questions ID read pending questions; agents cannot answer them.
  • search_sessions (stage sessions search QUERY [--project P] [--limit N]) wraps GET /api/threads/search and returns { results: [{ t3ThreadId, title, match, snippet?, project?, state?, taskId?, lastWordPreview?, t3WebUrl? }], t3 }.
  • Results are the REST shapes (lists as compact rows; list_tasks and list_sessions rows carry lastWordPreview, get_task and get_session carry the excerpt in thread.lastWord, and the CLI prints it as "last word"; get_task and get_session also carry pullRequests and thread.recentRuns, which the CLI prints as a prs block and a runs block: newest 10, (10 of N runs), --json has every stored run); errors are tool errors carrying the API error.
  • Caller: T3CODE_THREAD_ID and cwd from the environment (CLI, stdio), or tool args callerThreadId / cwd (override; required for context over HTTP).
  • Project: explicit project (id or exact title) > the caller session's project (GET /api/threads/:id) > the project whose workspaceRoot is the longest prefix of cwd.
  • Model: explicit (provider inferred from model via the catalog) > preferences.pinned > preferences.lastUsed > T3 default.
  • get_session returns createdTasks (see Session batch); get_task returns batch. order_tasks takes taskIds and returns { "tasks": Task[] }.
  • create_task / create_schedule set originThreadId to the caller session. afterMe = afterThreadId of the caller session (error without one).
  • create_task, update_task and link_task take optional handoff (CLI task add|link --handoff=false, task edit --handoff=true|false); omitted keeps the task's value (default true). get_task returns handoff and currentHandoff; stage task show prints handoff on|off for linked tasks and the current attempt's record.
  • create_task, create_schedule, update_task, update_schedule and link_task take optional base (ref|predecessor; CLI task add|edit --base). On create it needs baseRef (the fallback); on update_task / link_task without baseRef it keeps the task's current worktree (error for a root task). get_task returns currentLaunchBase; stage task show prints worktree from #12's branch (fallback origin/main, branch -) and, once launched, base feat/api (from #12) or base origin/main (fallback: <reason>).
  • imagePaths reads local files and uploads them (POST /api/uploads); CLI and stdio only (HTTP MCP rejects them).
  • wait_task polls GET /api/tasks/:id every 3s until the state is in until (default needs_input done settled failed interrupted cancelled) or timeoutSeconds (default 600, max 3600); CLI task wait exits 3 on timeout.