Skip to content
Hoody.com

The headless agent endpoint drives the full agent loop once over an ephemeral, gateway-owned session. It accepts a prompt (and optional workflow, model, output format, and timeout) and returns the result either as an async job to poll or as a Server-Sent Events stream. This route is intended for one-shot automation, CI pipelines, and integration scripts that need a single autonomous run without interactive approvals.

Drives the full agent loop once over an ephemeral gateway-owned session and returns text, json, or stream-json. The default (json / text) form is an async job: the gateway returns 202 with a job_id and the caller polls GET /api/v1/agent/jobs/{id} / GET /api/v1/agent/jobs/{id}/result for the captured Result. Setting format: stream-json (or stream: true) instead streams the run over SSE, emitting start, result / error, and end frames.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer, record cwd, and tool / workflow cwd. Required by routes that resolve a cwd (for example, POST /todos; todos.create also accepts a body cwd).
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector: "global" or a 24-hex id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes.
realmquerystringNoPer-request realm selector, the in: query alias of the X-Hoody-Realm header (read only when the header is absent): "global" or a 24-hex id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes.
FieldTypeRequiredDescription
promptstringNoThe prompt to drive the ephemeral session. Required unless workflow is set; with a workflow it is the optional $(workflow.prompt) text.
workflowstringNoOptional workflow name to run instead of a plain prompt turn.
inputsobjectNoOptional run-time values for the workflow’s declared input parameters (declared name → string value; resolves to $(input.<name>) in every step). Only accepted with workflow. A workflow with a required declared parameter cannot run without these. Values must be strings; a non-string value is a 400.
modelstringNoOptional model spec for the run.
formatstringNoOutput rendering: text | json | stream-json. stream-json (or stream: true) streams the run over SSE; otherwise the run is an async job.
streambooleanNoForce SSE streaming (equivalent to format: stream-json).
timeout_msintegerNoOptional run timeout in milliseconds (clamped to the hard ceiling).
{
"prompt": "Refactor src/parser.ts to use the new lexer and report a diff summary.",
"workflow": "refactor-and-summarize",
"inputs": {
"target_path": "src/parser.ts",
"summary_format": "diff"
},
"model": "claude-opus-4.5",
"format": "json",
"timeout_ms": 300000
}

The gateway returns one of the following:

SSE stream of the headless run (format: stream-json / stream: true). Frames are emitted in order: start → result or error → end.

event: start
data: {"run_id":"run_01HV8A2C3K7Q","model":"claude-opus-4.5"}
event: result
data: {"text":"Refactored 14 statements; 1 statement left on disk.\n","tokens":{"input":1284,"output":412}}
event: end
data: {"status":"ok"}

The SDK exposes this operation through two named methods. They differ only in what they set on the body:

  • client.agent.headless.start(data, options?) — sets body.stream = false and limits the body format field to "text" | "json". Use this for the default async-job flow.
  • client.agent.headless.stream(data, options?) — sets body.stream = true. Use this to receive the run as an SSE stream.
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.headless.start(
{
prompt: 'Refactor src/parser.ts to use the new lexer and report a diff summary.',
workflow: 'refactor-and-summarize',
inputs: {
target_path: 'src/parser.ts',
summary_format: 'diff',
},
model: 'claude-opus-4.5',
format: 'json',
timeout_ms: 300000,
},
// Optional trailing options (header / query params). Omit if unused.
{
XHoodyRealm: 'global',
}
);