HTTP API reference¶
Oh My Subagents serves three deliberately separate HTTP surfaces:
- a loopback product API under
/api; - operational health reads at
/healthzand/readyz; and - an optional bearer-protected, nonbrowser support API under
/support.
Generated OpenAPI is the exact field-level contract:
The product document intentionally excludes support records and internal runtime IDs. Health routes are intentionally excluded from both generated documents.
Local admission¶
The shipped product binds to a configured loopback host. Requests must use an exact configured loopback Host authority. Browser requests must also use one of the configured absolute loopback Origin values.
There is no current general remote-deployment, multi-user authentication, or cross-machine browser contract. Do not expose the product API directly to another machine.
Health¶
| Route | Meaning |
|---|---|
GET /healthz |
Process liveness; returns 200 while the application responds. |
GET /readyz |
Database connectivity; returns 200 when ready or 503 with database_unavailable. |
Application startup also fails closed when exact schema validation or mandatory startup recovery cannot complete.
Product routes¶
Workflow library and authoring¶
| Method and path | Input and semantic result |
|---|---|
GET /api/workflows |
Search with q, opaque cursor, and limit; returns library items and next_cursor. |
GET /api/workflows/authoring-options |
Returns accepted fields, provider choices, sandbox pairs, capabilities, and configured default. |
GET /api/workflows/{workflow_id} |
Reads catalog/current publication and optional draft. Optional revision_no, include_revisions, revision_cursor, and revision_limit select publication/history detail. |
POST /api/workflow-drafts |
Opens an existing draft or creates one from a discriminated open/create JSON body. Returns ETag; creation returns 201 and Location. |
GET /api/workflow-drafts/{draft_id} |
Reads the current draft and returns its ETag. |
PATCH /api/workflow-drafts/{draft_id} |
Applies one typed draft edit using required If-Match. |
DELETE /api/workflow-drafts/{draft_id} |
Discards only the draft using required If-Match. |
POST /api/workflow-drafts/{draft_id}/validate |
Returns semantic validation and the current draft; does not publish. |
POST /api/workflow-drafts/{draft_id}/undo |
Consumes one receipt_id using required If-Match; returns the new ETag. |
POST /api/workflow-drafts/{draft_id}/publish |
Publishes the exact current draft using required If-Match; returns the immutable revision. |
Draft create, patch, and undo bodies must use Content-Type: application/json. A missing If-Match returns 428; a stale ETag returns 412 with current draft truth. Refetch and reconcile instead of retrying a guessed mutation.
Tasks, attention, and managed actions¶
| Method and path | Input and semantic result |
|---|---|
GET /api/tasks |
Search by q, semantic status, opaque cursor, and limit. |
POST /api/tasks |
Starts one Task asynchronously; returns 202 accepted receipt. |
GET /api/tasks/{task_id} |
Returns semantic status, team work, current plan, attention, legal actions, exact Result, and bounded recent Activity/Human Request/Command Run history. |
POST /api/tasks/{task_id}/members/{member_id}/steers |
Sends one exact message through a current Member's returned steer_action; confirmed delivery returns current Task truth and records visible Activity. |
GET /api/tasks/{task_id}/activities |
Returns a cursor page of semantic Activity. |
GET /api/tasks/{task_id}/activities/stream |
Streams activity and invalidating task_changed server-sent events. |
POST /api/tasks/{task_id}/controls/{action_id} |
Applies one current pause, resume, or cancel action with a confirmed body when required. |
GET /api/tasks/{task_id}/human-requests/{request_id} |
Reads one Human Request and its current action/resolution. |
POST /api/tasks/{task_id}/human-requests/{request_id}/responses |
Uses the current action_id to answer typed items or confirm cancellation. |
GET /api/tasks/{task_id}/command-runs/{command_id} |
Reads semantic Action state, output link, and current cancel action. |
GET /api/tasks/{task_id}/command-runs/{command_id}/output |
Reads one bounded sanitized output page. |
POST /api/tasks/{task_id}/command-runs/{command_id}/cancel |
Uses the current action_id and confirmed: true to request cancellation. |
Task detail is a semantic product readback. It does not expose Assignments, Attempts, provider-turn IDs, Waves, bindings, watchdogs, or raw events as an alternate control protocol.
Operator conversations¶
| Method and path | Input and semantic result |
|---|---|
GET /api/operator/status |
Reads Operator availability and setup action. |
GET /api/operator/conversations |
Lists conversations with opaque cursor and limit. |
POST /api/operator/conversations |
Creates one conversation from an empty JSON object. |
GET /api/operator/conversations/{conversation_id} |
Reads one bounded entry page using optional cursor and limit. |
POST /api/operator/conversations/{conversation_id}/messages |
Submits one nonblank text message. |
POST /api/operator/conversations/{conversation_id}/question-sets/{question_set_id}/answers |
Submits one to three option, custom, or skip answers for the current question set. |
Every Operator POST requires an Idempotency-Key header from 1 to 200 characters. Reusing the same key for the same accepted request converges; reusing it for different content conflicts. A question set is a two-turn interaction: read the typed questions, submit one explicit Continue answer request, then refetch conversation truth.
Task start¶
POST /api/tasks accepts the closed TaskStartRequest:
{
"workflow": "production-feature-delivery",
"prompt": "Implement and independently review the requested change.",
"workspace": "/absolute/path/to/project",
"files": [
{
"path": "docs/accepted-scope.md",
"description": "Accepted scope"
}
]
}
workflow and nonblank prompt are required. workspace may be omitted only when paths.workspace is configured. files defaults to an empty ordered list and every path must be an existing regular workspace-relative file with no symbolic-link component.
The 202 receipt includes a receipt ID, Task ID, Workflow ID and revision, workspace, manifest path, accepted status, and a reminder that work starts asynchronously and may need attention. A repeated POST is a new Task request; the Task-start route has no generic idempotency-key contract.
Currentness and legal actions¶
Product mutations use current controller readbacks:
- Workflow draft mutations use opaque ETags and
If-Match. - Task controls use the opaque
action_idreturned in the current Task view. - Human Request response and Command Run cancellation use the opaque
action_idreturned by their current readback. - Operator conversation POSTs use
Idempotency-Key, and question answers also identify the current question set.
Action IDs encode current legal action, not a stable button name. After 409, 412, or another currentness failure, refetch and use the actions currently offered. Do not synthesize IDs or replay stale confirmations.
Pagination¶
All cursors are opaque and scoped to their owning route/query. Start without a cursor, follow only the returned cursor, and restart the read after a cursor reset. Do not parse, alter, or carry a cursor between resources.
| Read | Default / maximum |
|---|---|
| Workflow library | limit=50, maximum 100 |
| Workflow revision history | revision_limit=20, maximum 100 |
| Task search | limit=50, maximum 100 |
| Task Activity page | limit=50, maximum 200 |
| Command Run output | limit=65536 bytes, maximum 65536 |
| Operator conversation list | limit=50, maximum 100 |
| Operator conversation entries | limit=100, maximum 100 |
| Support Task search | limit=50, maximum 200 |
| Support event page | limit=100, maximum 500 |
| Support trace | limit=50, maximum 200 |
Command output pages also report output_complete, is_missing, is_changed, and is_bounded. A later file mutation can therefore be disclosed rather than hidden behind the cursor.
Activity stream¶
Use the Activity page for bounded backfill and the SSE stream as a live invalidation channel. Each SSE frame has an opaque id. Reconnect with Last-Event-ID; on the product stream it is authoritative when both it and a query cursor are present.
With no cursor, the stream begins at the current head and emits future changes; it does not replay all history. The stream has no separate heartbeat contract. Clients should refetch Task truth after an event, reconnect, cursor reset, or uncertain connection state rather than reconstructing the Task solely from frames.
Failure contract¶
Ordinary product failures use:
{
"ok": false,
"code": "invalid_request",
"summary": "The request contains an unsupported or invalid field.",
"retryable": false,
"field_path": "files.0.path",
"suggested_next_step": "Correct the highlighted field and resend the request."
}
The closed product-safe codes are:
invalid_request;not_found;conflict;cursor_reset_required;access_denied;unavailable; andinternal_error.
Use the HTTP status, retryable, current controller readback, and suggested next step together. Do not turn every failure into an unconditional retry.
Support API¶
The read-only support routes are:
GET /support/openapi.json
GET /support/tasks
GET /support/tasks/{task_id}
GET /support/tasks/{task_id}/events
GET /support/tasks/{task_id}/events/stream
GET /support/tasks/{task_id}/trace
They require:
The configured token must contain at least 32 characters. Any browser Origin header is rejected.
Support Task search accepts q, status, cursor, and limit. Trace accepts q, cursor, limit, and sort=occurred_at_desc|occurred_at_asc. Event pages and streams expose raw controller audit records. For the support SSE stream, query cursor and Last-Event-ID must identify the same position when both are supplied.
Support snapshots, traces, and events are derived readbacks. They never mutate runtime state, select a successor, clear a wait, or override product truth. There is no generic HTTP file CRUD, managed Artifact API, or public raw-runtime mutation route.