Skip to content
Hoody.com

Loops are recurring agent prompts scheduled on a session. Each loop fires its prompt on a dedicated lane (separate from the session’s command queue) at a fixed interval, until it stops on a predicate, hits max_runs, or is paused or stopped. Use the endpoints below to list, create, fire on demand, update, and delete loops on a session.

All loop routes live on the agent container host:

https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com

List the recurring agent loops of every live session the caller may see, oldest first. The response is the kit list envelope {items, meta:{total}}, with optional page/limit pagination applied gateway-side. A loop is listed only when its session passes the same realm and owner check as GET /api/v1/agent/sessions/{id}/loops, so loops in another realm’s or another account’s session are absent, never marked. Loops exist only as long as their session, so a closed session’s loops are gone. Each item carries its session_id; manage a loop through the per-session /sessions/{id}/loops routes.

NameInTypeRequiredDescription
pagequeryintegerNo1-based page number for pagination.
limitqueryintegerNoMaximum items per page (0 = no pagination).
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against.
realmquerystringNoThe realm to list: “global” (loops not tied to a realm), a 24-hex realm id, or “all” for every realm this login serves. Also accepted as the X-Hoody-Realm header, except “all”. Omitted, the agent’s current realm.
X-Hoody-RealmheaderstringNoPer-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
{
"items": [
{
"created_at": "2026-01-12T14:22:05Z",
"id": "loop_01HMZX7P5V3J8N9K4Q2WXABCDE",
"interval_ms": 1800000,
"last_run_at": "2026-01-13T09:00:00Z",
"max_runs": 50,
"next_in_ms": 1680000,
"paused": false,
"prompt": "Check the build and report anything newly broken.",
"run_count": 7,
"session_id": "67e89abc123def456789abcd-890abcdef12345678901cdef",
"skip_count": 0,
"state": "running"
}
],
"meta": {
"total": 1
}
}

The envelope has two top-level fields:

  • items: array of loop rows (each forwarded daemon row keeps its own shape).
  • meta: pagination metadata. total is always present; page and limit appear only when limit > 0.
Terminal window
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/loops?page=1&limit=20" \
-H "Authorization: Bearer <token>"

List the recurring agent loops scheduled for a session. The response is the kit list envelope {items, meta:{total}}, with optional page/limit pagination applied gateway-side.

NameInTypeRequiredDescription
idpathstringYesThe session id.
pagequeryintegerNo1-based page number for pagination.
limitqueryintegerNoMaximum items per page (0 = no pagination).
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against.
X-Hoody-RealmheaderstringNoPer-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
realmquerystringNoPer-request realm selector — the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
{
"items": [
{
"created_at": "2026-01-12T14:22:05Z",
"id": "loop_01HMZX7P5V3J8N9K4Q2WXABCDE",
"interval_ms": 1800000,
"last_run_at": "2026-01-13T09:00:00Z",
"max_runs": 50,
"next_in_ms": 1680000,
"paused": false,
"prompt": "Check the build and report anything newly broken.",
"run_count": 7,
"session_id": "67e89abc123def456789abcd-890abcdef12345678901cdef",
"skip_count": 0,
"state": "running"
}
],
"meta": {
"total": 1
}
}

The envelope has two top-level fields:

  • items: array of loop rows (each forwarded daemon row keeps its own shape).
  • meta: pagination metadata. total is always present; page and limit appear only when limit > 0.
Terminal window
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops?page=1&limit=20" \
-H "Authorization: Bearer <token>"

Create a recurring agent loop on a session. The loop fires its run on a dedicated loop lane, separate from the session’s command queue.

NameInTypeRequiredDescription
idpathstringYesThe session id.
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against.
X-Hoody-RealmheaderstringNoPer-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
realmquerystringNoPer-request realm selector — the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.

Loop definition. The session_id comes from the path.

NameTypeRequiredDescription
promptstringYesThe prompt fired each loop run.
intervalstringYesRun interval as a duration token, for example "30m". Required with no default. The minimum accepted interval is 60 seconds; a shorter one is rejected rather than rounded up.
max_runsintegerNoStop after this many runs. Default: 50. Omitted, 0, or negative applies the default ceiling of 50 runs; there are no unbounded loops.
stop_whenobjectNoOptional stop predicate, evaluated after each run. Omit the field entirely when there is no predicate: a string value is rejected, including the empty string. See the stop_when shape below.
max_cost_usdnumberNoCost ceiling (0 = unlimited).
max_wall_msintegerNoWall-clock ceiling in ms (0 = unlimited).
expires_instringNoOptional expiration as a duration from now, in the same grammar as interval (for example "2h" or "3 days"). Omitted, "", "never", "none", or "off" (case-insensitive) means no expiration. An expired loop stops firing. Without fire_now an expiry shorter than one interval is rejected, because the loop would never fire; with fire_now it must be at least 2 seconds, so the immediate first run can happen.
fire_nowbooleanNoRun the first fire on the next scheduler tick instead of one interval out; later fires keep the normal cadence. Default: false.

stop_when shape

NameTypeRequiredDescription
kindstringYes (inside stop_when)Which kind of predicate this is. One of expression, toolCall, judgeRef, convergence.
exprstringNoFor kind: expression: the expression to evaluate.
toolstringNoFor kind: toolCall: the tool to call.
checkstringNoFor kind: toolCall: the check applied to the tool result.
judgestringNoFor kind: judgeRef: the judge to consult.
argsobjectNoArguments passed to the tool or judge.
thresholdnumberNoFor kind: convergence: the convergence threshold.
min_itersintegerNoFor kind: convergence: the minimum iterations before the predicate can stop the loop.
allow_networkbooleanNoWhether the predicate may reach the network.
{
"loop": {
"created_at": "2026-01-12T14:22:05Z",
"id": "loop_01HMZX7P5V3J8N9K4Q2WXABCDE",
"interval_ms": 1800000,
"last_run_at": "2026-01-13T09:00:00Z",
"max_runs": 50,
"next_in_ms": 1680000,
"paused": false,
"prompt": "Check the build and report anything newly broken.",
"run_count": 7,
"session_id": "67e89abc123def456789abcd-890abcdef12345678901cdef",
"skip_count": 0,
"state": "running"
},
"status": "ok"
}
Terminal window
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Check the build and report anything newly broken.",
"interval": "30m",
"max_runs": 50
}'

POST /api/v1/agent/sessions/{id}/loops/{loopId}/run-now

Section titled “POST /api/v1/agent/sessions/{id}/loops/{loopId}/run-now”

Fire a loop’s next run immediately, off the dedicated loop lane. A loop you paused fires once and stays paused. A loop the circuit breaker paused after repeated errors is reset and resumes its schedule, since run-now is its retry. An unknown loop id (or a loop in another realm) returns 404 not_found.

NameInTypeRequiredDescription
idpathstringYesThe session id.
loopIdpathstringYesThe loop id.
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against.
X-Hoody-RealmheaderstringNoPer-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
realmquerystringNoPer-request realm selector — the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.

This endpoint reads no body fields; the session id and loop id come from the path.

{
"status": "ok"
}
Terminal window
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops/{loopId}/run-now" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{}'

PATCH /api/v1/agent/sessions/{id}/loops/{loopId}

Section titled “PATCH /api/v1/agent/sessions/{id}/loops/{loopId}”

Update a loop by body shape. Set exactly one intent per request: its run state via paused, its expiry via expires_in, or a budget revive via max_cost_usd/max_wall_ms. This endpoint does NOT change interval or prompt in v1.

NameInTypeRequiredDescription
idpathstringYesThe session id.
loopIdpathstringYesThe loop id.
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against.
X-Hoody-RealmheaderstringNoPer-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
realmquerystringNoPer-request realm selector — the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.

Exactly one update intent per request: run state, expiry, or budget.

NameTypeRequiredDescription
pausedbooleanNoRun-state intent (true pauses, false resumes).
expires_instringNoExpiry intent: a duration-from-now token, e.g. "2h"; "" or "never" clears it.
max_cost_usdnumberNoBudget intent: the cost ceiling (0 = unlimited).
max_wall_msintegerNoBudget intent: the wall-clock ceiling in ms, or a duration token; 0 = unlimited.
{
"status": "ok"
}

Pause a loop:

Terminal window
curl -X PATCH "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops/{loopId}" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "paused": true }'

Set an expiry:

Terminal window
curl -X PATCH "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops/{loopId}" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "expires_in": "2h" }'

Revive budgets (cost and wall):

Terminal window
curl -X PATCH "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops/{loopId}" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "max_cost_usd": 5, "max_wall_ms": 600000 }'

DELETE /api/v1/agent/sessions/{id}/loops/{loopId}

Section titled “DELETE /api/v1/agent/sessions/{id}/loops/{loopId}”

Delete a recurring loop from a session.

NameInTypeRequiredDescription
idpathstringYesThe session id.
loopIdpathstringYesThe loop id.
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against.
X-Hoody-RealmheaderstringNoPer-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.
realmquerystringNoPer-request realm selector — the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm.

The body is optional. Every _-prefixed key in the body is ignored, the request scope (cwd/config_dir) is added from the scope headers, and other keys are passed through verbatim. The session id and loop id come from the path.

{
"status": "ok"
}
Terminal window
curl -X DELETE "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/loops/{loopId}" \
-H "Authorization: Bearer <token>"