Agent: Headless
Section titled “Agent: Headless”The headless agent endpoint drives a single, one-shot agent loop over an ephemeral, gateway-owned session. Submit a prompt (and optional target) and the gateway returns either an async job (default text/json form) or a Server-Sent Events stream (stream-json / stream:true). The endpoint is intended for automation, CI pipelines, and scripted workflows rather than interactive sessions.
Create a headless run
Section titled “Create a headless run”Submits a prompt and (optionally) a workflow to the agent. The response shape depends on the output format:
text/json(default) — Async job. The endpoint returns202with ajob_id; pollGET /jobs/{id}(andGET /jobs/{id}/resultfor the capturedResult) for the outcome. A server-side failure surfaces as{status:"failed", error}in the result, not as an HTTP status on this call.stream-json/stream:true— SSE stream. The endpoint returns200and streamsstart → result/error → endframes overtext/event-stream.
POST /api/v1/agent/headless/runs
Section titled “POST /api/v1/agent/headless/runs”Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths). |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: "global" or a 24-hex id (also accepted as ?realm=). Rejected (realm_scope_unsupported) on active-only / no-realm routes. |
realm | query | string | No | Per-request realm selector — the in:query alias of the X-Hoody-Realm header (read only when the header is absent). Same semantics. |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | The prompt to drive the ephemeral session. |
workflow | string | No | Optional workflow name to run instead of / alongside the prompt. |
model | string | No | Optional model spec for the run. |
format | string | No | Output rendering: text | json | stream-json. stream-json (or stream:true) streams the run over SSE; otherwise the run is an async job. |
stream | boolean | No | Force SSE streaming (equivalent to format:stream-json). |
timeout_ms | integer | No | Optional run timeout in milliseconds (clamped to the hard ceiling). |
SDK Example
Section titled “SDK Example”await client.agent.headless.createHeadlessRun({ prompt: "Refactor src/auth/session.ts to use the new token store and report which tests broke.", format: "json", timeout_ms: 120000});cURL Example
Section titled “cURL Example”curl -X POST "https://prj_abc123-cnt_xyz789-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/headless/runs" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Refactor src/auth/session.ts to use the new token store and report which tests broke.", "format": "json", "timeout_ms": 120000 }'Responses
Section titled “Responses”Returned when format: stream-json or stream: true. The body is a text/event-stream of the run.
event: startdata: {"run_id":"run_7f3a2b1c","model":"claude-sonnet-4.5"}
event: resultdata: {"type":"text","text":"Refactored 3 files; 12 tests pass, 2 fail."}
event: enddata: {"ok":true}Async job acknowledgement. Poll GET /jobs/{id}/result for the captured Result (or a {status:"failed", error} daemon failure).
{ "job_id": "job_01HMZ7XQ9K4D5N6P8R2T3V1WYA"}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (not via the Hoody proxy) | The request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy (e.g. hoody agent … → platform → proxy), not by connecting to the container directly. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge (http.MaxBytesReader) before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |