Skip to content

These workspace-scoped session endpoints operate on a specific session within a workspace, providing session inspection, autonomous loop control, background job management, and CLI agent run capabilities. All endpoints require a workspaceID and sessionID in the path. Streaming endpoints return text/event-stream; the rest return JSON.

Inspect the runtime state of a session: effective permission rulesets, individual tool-call results, and live message updates.

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/permissions

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/permissions”

Return the static merged ruleset that PermissionNext.ask() passes to evaluate() at tool-invocation time. Tool dispatch merges agent.permission with session.permission — not raw config.permission — because each agent definition pre-bakes defaults, YOLO overrides, agent-specific rules, and user cfg.permission into its own ruleset. The response includes the resolved agent name, the agent’s pre-baked agentRuleset, the session-scoped sessionRuleset, and the effective ruleset (the exact array passed to PermissionNext.evaluate() with last-match-wins semantics).

This endpoint reflects the persisted ruleset only. Transient "always" approvals from PermissionNext.reply that have not yet been written to session.permission are NOT included. Pass ?agent= to inspect the ruleset under a non-default agent.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier (project ID or workspace entry ID; 24-char lowercase hex)
sessionIDpathstringYesSession identifier
agentquerystringNoAgent name to evaluate the ruleset under (defaults to the configured default agent or build)
Terminal window
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/permissions?agent=build" \
-H "Authorization: Bearer <token>"
{
"agent": "build",
"agentRuleset": [
{ "permission": "bash", "pattern": "*", "action": "allow" }
],
"sessionRuleset": [
{ "permission": "bash", "pattern": "rm -rf *", "action": "deny" }
],
"effective": [
{ "permission": "bash", "pattern": "rm -rf *", "action": "deny" },
{ "permission": "bash", "pattern": "*", "action": "allow" }
]
}

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/messages/{messageID}/tools/{callID}

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/messages/{messageID}/tools/{callID}”

Return the ToolPart on a message that matches callID — without forcing the SDK consumer to fetch and scan the full message. Returns 404 if the message does not belong to this session, the message is missing, or the message has no ToolPart with that callID.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier (project ID or workspace entry ID; 24-char lowercase hex)
sessionIDpathstringYesSession identifier
messageIDpathstringYesMessage identifier
callIDpathstringYesTool-call ID
Terminal window
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/messages/msg_01HXYZABCDEFGHJKMNPQRSTVW/tools/call_01HXYZABCDEFGHJKMNPQRSTVW" \
-H "Authorization: Bearer <token>"
{
"id": "prt_01HXYZABCDEFGHJKMNPQRSTVW",
"sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
"messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
"type": "tool",
"callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
"tool": "bash",
"state": {
"status": "completed",
"input": { "command": "ls -la" },
"output": "total 12\ndrwxr-xr-x 3 user user 4096 ...\n",
"title": "List directory",
"metadata": {},
"time": {
"start": 1700000000000,
"end": 1700000000100
}
},
"metadata": {}
}

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/messages/{messageID}/stream

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/messages/{messageID}/stream”

Server-Sent Events stream of MessageV2 updates scoped to a single (sessionID, messageID). The server emits a snapshot event first with the current message and parts, then part.updated, part.removed, message.updated, and message.removed events as they happen on the bus. Heartbeats arrive every 30 seconds. The stream closes automatically once the assistant message becomes terminal (message.time.completed is set), sending a done event with the final message info just before close. Returns 404 if the message is missing in this session.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier (project ID or workspace entry ID; 24-char lowercase hex)
sessionIDpathstringYesSession identifier
messageIDpathstringYesMessage identifier
Terminal window
curl -N -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/messages/msg_01HXYZABCDEFGHJKMNPQRSTVW/stream" \
-H "Authorization: Bearer <token>" \
-H "Accept: text/event-stream"
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: snapshot
data: {"type":"snapshot","message":{"id":"msg_01HXYZ...","role":"assistant","time":{"created":1700000000000}},"parts":[]}
event: part.updated
data: {"type":"part.updated","part":{"id":"prt_01HX...","type":"text","text":"Let me think..."}}
event: done
data: {"type":"done","message":{"id":"msg_01HXYZ...","time":{"completed":1700000005000}}}

Drive the session’s prompt loop as a repeating directive. While a loop is active, the frontend disables the chat input.

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop”

Return the active loop directive for the session, or null if no loop is active.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier (project ID or workspace entry ID; 24-char lowercase hex)
sessionIDpathstringYesSession identifier
Terminal window
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/loop" \
-H "Authorization: Bearer <token>"
{
"prompt": "Run the test suite and fix any failures.",
"iters": 5,
"iter": 1,
"started_at": 1700000000000,
"launch_id": "launch_01HXYZABCDEFGHJKMNPQRSTVW",
"agent": "build",
"model": {
"providerID": "anthropic",
"modelID": "claude-3-5-sonnet-20241022"
},
"system": "You are a careful engineer."
}

When no loop is active, the response is the JSON literal null.

POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop

Section titled “POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop”

Install a loop directive on the session. The session re-enters its prompt loop for iters iterations, synthesizing the same prompt as the user turn each iteration. Stop with DELETE /loop. Returns 409 if a loop is already active or the session is busy.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier (project ID or workspace entry ID; 24-char lowercase hex)
sessionIDpathstringYesSession identifier
NameTypeRequiredDescription
promptstringYesLoop prompt (1–8000 chars)
itersintegerYesTotal iterations (1–100)
Terminal window
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/loop" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Run the test suite and fix any failures.",
"iters": 5
}'
{
"prompt": "Run the test suite and fix any failures.",
"iters": 5,
"iter": 0,
"started_at": 1700000000000,
"launch_id": "launch_01HXYZABCDEFGHJKMNPQRSTVW",
"agent": "build",
"model": {
"providerID": "anthropic",
"modelID": "claude-3-5-sonnet-20241022"
},
"system": "You are a careful engineer."
}

DELETE /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop

Section titled “DELETE /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop”

Clear the loop directive AND cancel the running prompt. Idempotent — safe to call when no loop is active.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier (project ID or workspace entry ID; 24-char lowercase hex)
sessionIDpathstringYesSession identifier
Terminal window
curl -X DELETE "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/loop" \
-H "Authorization: Bearer <token>"
true

Manage background jobs created on a session — RSI, self-tuning, cli_agent, bash, webfetch. Jobs run with a wakePolicy (when the result is delivered back into the conversation) and a durability (how long they are kept).

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs”

Return background jobs created on this session. Filter by status with ?status=active|completed|all. Paginate with ?limit&cursor — when limit is set and more results exist after the page, the response includes nextCursor (use it as cursor on the next call). Jobs are sorted ascending by creation time, so nextCursor resumes after the last seen job ID.

NameInTypeRequiredDescription
statusquerystringNoStatus filter. Allowed values: active, completed, all. Default: "all".
limitqueryintegerNoMax jobs to return per page. Defaults to no cap (returns the full filtered set).
cursorquerystringNoPagination cursor. Pass the previous response’s nextCursor to fetch the next page.
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
Terminal window
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs?status=all&limit=20" \
-H "Authorization: Bearer <token>"
{
"jobs": [
{
"id": "job_01HXYZABCDEFGHJKMNPQRSTVW",
"sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
"messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
"callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
"tool": "bash",
"status": "completed",
"input": { "command": "npm test" },
"progress": {
"message": "Running tests...",
"percent": 75
},
"output": "All tests passed.",
"fullOutput": "All tests passed.\n",
"attachments": [
{ "mime": "text/plain", "url": "https://files.hoody.com/..." }
],
"metadata": {},
"time": {
"created": 1700000000000,
"started": 1700000000050,
"completed": 1700000005000
},
"wakePolicy": "auto",
"durability": "session",
"groupID": "grp_01HXYZABCDEFGHJKMNPQRSTVW",
"timeout": 60000,
"retries": { "max": 2, "count": 0 },
"completionProcessed": true,
"parentJobId": null,
"childJobIds": [],
"totalChildren": 0,
"finishedChildJobIds": []
}
],
"nextCursor": "job_01HXYZABCDEFGHJKMNPQRSTVW"
}

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}”

Fetch a single job’s full info: status, progress, output, error, timing. Use this to poll a job started by RSI, self-tuning, or cli_agent if the SSE stream is unavailable.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
jobIdpathstringYesJob identifier
Terminal window
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW" \
-H "Authorization: Bearer <token>"
{
"id": "job_01HXYZABCDEFGHJKMNPQRSTVW",
"sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
"messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
"callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
"tool": "bash",
"status": "running",
"input": { "command": "npm test" },
"progress": {
"message": "Running test suite...",
"percent": 42
},
"output": "Test 1 passed\nTest 2 passed\n",
"fullOutput": "Test 1 passed\nTest 2 passed\nTest 3 running...\n",
"metadata": {},
"time": {
"created": 1700000000000,
"started": 1700000000050
},
"wakePolicy": "auto",
"durability": "session",
"timeout": 60000,
"retries": { "max": 2, "count": 0 },
"completionProcessed": false
}

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/output

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/output”

Return the complete output buffer of a background job. Job.Info.output may be truncated for index payloads; this endpoint returns the full uncapped output.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
jobIdpathstringYesJob identifier
Terminal window
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW/output" \
-H "Authorization: Bearer <token>"
{
"output": "Test 1 passed\nTest 2 passed\nTest 3 passed\nTest 4 passed\nAll tests passed.\n"
}

POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/cancel

Section titled “POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/cancel”

Request cancellation of a running background job. Already-terminal jobs are returned with their existing status; running jobs are signalled to stop.

NameInTypeRequiredDescription
workspaceIDpathstringYesworkspaceID path parameter
sessionIDpathstringYessessionID path parameter
jobIdpathstringYesjobId path parameter
Terminal window
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW/cancel" \
-H "Authorization: Bearer <token>"
{
"job": {
"id": "job_01HXYZABCDEFGHJKMNPQRSTVW",
"sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
"messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
"callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
"tool": "bash",
"status": "cancelled",
"input": { "command": "npm test" },
"time": {
"created": 1700000000000,
"started": 1700000000050,
"completed": 1700000005000
},
"wakePolicy": "auto",
"durability": "session",
"timeout": 60000,
"retries": { "max": 2, "count": 0 },
"completionProcessed": false
},
"message": "Job cancelled"
}

POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/cancel

Section titled “POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/cancel”

Bulk-cancel a set of jobs in one call. Each ID is processed independently — unknown IDs and any other per-job errors are reported in failed[] rather than aborting the batch. Already-terminal jobs are reported in failed[] with reason already_terminal. Returns 200 even when every job fails — inspect cancelled and failed to interpret the outcome.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
NameTypeRequiredDescription
jobIdsarray of stringYesJob IDs to cancel. Minimum 1, maximum 1000 per call. Unknown IDs are reported in failed, not 404.
Terminal window
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/cancel" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"jobIds": [
"job_01HXYZABCDEFGHJKMNPQRSTVW",
"job_02HXYZABCDEFGHJKMNPQRSTVW",
"job_03HXYZABCDEFGHJKMNPQRSTVW"
]
}'
{
"cancelled": 2,
"failed": [
{
"jobId": "job_03HXYZABCDEFGHJKMNPQRSTVW",
"reason": "already_terminal"
}
]
}

POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/retry

Section titled “POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/retry”

Re-run a job that ended in a non-success terminal state. Only failed, expired, and cancelled jobs are eligible; completed jobs return 409. Child jobs (those with parentJobId) cannot be retried in isolation — retry the top-level parent instead. The retry creates a NEW job with a new ID, copying the original tool, input, messageID, callID, wakePolicy, durability, and timeout. The original job record is left intact for audit.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
jobIdpathstringYesJob identifier
Terminal window
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW/retry" \
-H "Authorization: Bearer <token>"
{
"jobID": "job_02HXYZABCDEFGHJKMNPQRSTVW",
"sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
"status": "queued",
"retriedFrom": "job_01HXYZABCDEFGHJKMNPQRSTVW"
}

POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/inject

Section titled “POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/inject”

Mirror the agent prompt-loop’s own injection step: pull terminal, unprocessed jobs and attach summary parts to the last user message (or mint one if none). Pass jobIds: [...] to inject a specific subset; pass jobIds: [] to inject nothing explicitly; omit the field to drain all manual-policy unprocessed jobs. Returns 409 if the prompt loop is active.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
NameTypeRequiredDescription
jobIdsarray of stringNoOptional subset of job IDs to inject. Omit the field to drain all manual-policy unprocessed jobs. Pass [] to explicitly inject nothing. Max 1000 IDs per call.
Terminal window
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/inject" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"jobIds": [
"job_01HXYZABCDEFGHJKMNPQRSTVW",
"job_02HXYZABCDEFGHJKMNPQRSTVW"
]
}'
{
"injected": 2,
"failed": 0,
"message": "Injected 2 job result(s) into session context"
}

Spawn and stream an external CLI agent (gemini, codex, claude) as a side job on a session. The session’s existing prompt loop is untouched — CLI agent runs are independent and their results can later be injected via POST /jobs/inject.

POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/cli-agent

Section titled “POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/cli-agent”

Run an external CLI agent against the session’s working directory in the background. Returns a queued jobID; subscribe to /cli-agent/runs/{jobID}/stream for progress and final output.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
NameTypeRequiredDescription
agentstringYesConfigured CLI agent name (case-insensitive). e.g. Gemini Flash, Codex.
promptstringYesPrompt to send to the CLI agent. Max 100K chars.
modelstringNoOverride the agent’s default model.
gitbooleanNoRequest git access (only honoured if agent’s allow_git is true, or codex-rw).
timeoutintegerNoOverride timeout in ms; clamped to the agent’s configured ceiling.
Terminal window
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/cli-agent" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"agent": "Codex",
"prompt": "Investigate the failing login test and propose a fix.",
"model": "gpt-4o",
"git": false,
"timeout": 300000
}'
{
"jobID": "job_01HXYZABCDEFGHJKMNPQRSTVW",
"sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
"status": "queued"
}

GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/cli-agent/runs/{jobID}/stream

Section titled “GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/cli-agent/runs/{jobID}/stream”

Server-Sent Events stream of progress events for a CLI agent job. The server emits a snapshot first (so late subscribers see the current state), then live events; if the job is already terminal, it emits the completion event and closes.

NameInTypeRequiredDescription
workspaceIDpathstringYesWorkspace identifier
sessionIDpathstringYesSession identifier
jobIDpathstringYesCLI agent job identifier
Terminal window
curl -N -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/cli-agent/runs/job_01HXYZABCDEFGHJKMNPQRSTVW/stream" \
-H "Authorization: Bearer <token>" \
-H "Accept: text/event-stream"
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: snapshot
data: {"type":"snapshot","job":{"id":"job_01H...","status":"running","progress":{"message":"Reading files...","percent":20}}}
event: progress
data: {"type":"progress","message":"Analyzing test output","percent":55}
event: complete
data: {"type":"complete","job":{"id":"job_01H...","status":"completed","output":"Found the issue: ..."}}