Skip to content

The Loops API lets you schedule, list, update, delete, and immediately fire recurring self-prompting loops on an agent session. Use these endpoints to give an agent a heartbeat — a prompt that re-fires at a fixed interval with cost, wall-clock, and run-count budgets — or to manually kick off the next iteration. Loop firings run on a dedicated loop lane that is separate from the interactive command channel.

Returns every recurring loop scheduled for a session as the standard kit list envelope { items, meta: { total } }. The page / limit window is applied gateway-side.

GET /api/v1/agent/sessions/{id}/loops

NameInTypeRequiredDescription
idpathstringYesPath identifier.
pagequeryintegerNo1-based page number for pagination.
limitqueryintegerNoMaximum items per page (0 = no pagination).
X-Hoody-CwdheaderstringNoPer-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-ContainerheaderstringNoPer-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector (§6.1): “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 (§6.1) — 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.
{
"items": [
{
"loop_id": "lp_8c1f2a9d4e0b7c5f",
"session_id": "sess_3a91e6c2b1d04f87",
"prompt": "Check the latest CI run and summarize failures.",
"interval": "30m",
"max_runs": 0,
"max_cost_usd": 5.0,
"max_wall_ms": 0,
"paused": false,
"run_count": 14,
"next_run_at": "2026-01-15T18:30:00Z"
},
{
"loop_id": "lp_2d4e6f8a1c3b5d7e",
"session_id": "sess_3a91e6c2b1d04f87",
"prompt": "Poll the staging deploy webhook and post a status update.",
"interval": "5m",
"max_runs": 100,
"max_cost_usd": 0,
"max_wall_ms": 0,
"paused": true,
"run_count": 42,
"expires_in": "2h"
}
],
"meta": {
"total": 2,
"page": 1,
"limit": 50
}
}
const page = await client.agent.loops.listLoopsIterator({ id: "sess_3a91e6c2b1d04f87", page: 1, limit: 50 });
for await (const loop of page) {
console.log(loop.loop_id, loop.prompt);
}

Creates a recurring loop on a session with a prompt, interval, and optional budgets and stop predicate. Loop firings run on a dedicated loop lane, not the command channel.

POST /api/v1/agent/sessions/{id}/loops

NameInTypeRequiredDescription
idpathstringYesPath identifier.
X-Hoody-CwdheaderstringNoPer-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-ContainerheaderstringNoPer-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector (§6.1): “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 (§6.1) — 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.
NameTypeRequiredDescription
promptstringYesThe prompt fired each loop run.
intervalstringNoRun interval (duration token, e.g. "30m").
max_runsintegerNoStop after this many runs (0 = unlimited).
stop_whenstringNoOptional stop predicate evaluated each run.
max_cost_usdnumberNoCost ceiling (0 = unlimited).
max_wall_msintegerNoWall-clock ceiling in ms (0 = unlimited).
{
"loop_id": "lp_8c1f2a9d4e0b7c5f",
"session_id": "sess_3a91e6c2b1d04f87",
"prompt": "Check the latest CI run and summarize failures.",
"interval": "30m",
"max_runs": 0,
"max_cost_usd": 5.0,
"max_wall_ms": 0,
"paused": false,
"created_at": "2026-01-15T18:00:00Z"
}
const created = await client.agent.loops.createLoop({
id: "sess_3a91e6c2b1d04f87",
data: {
prompt: "Check the latest CI run and summarize failures.",
interval: "30m",
max_cost_usd: 5.0,
},
});
console.log(created.loop_id);

Fires the loop’s next run immediately on the dedicated loop lane, independent of its scheduled interval.

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

NameInTypeRequiredDescription
idpathstringYesPath identifier.
loopIdpathstringYesPath identifier.
X-Hoody-CwdheaderstringNoPer-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-ContainerheaderstringNoPer-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector (§6.1): “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 (§6.1) — 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.

This endpoint accepts an optional JSON object forwarded to the daemon loops.run_now RPC. The session id and loop id come from the path; the body is optional. No fields are required.

{
"loop_id": "lp_8c1f2a9d4e0b7c5f",
"session_id": "sess_3a91e6c2b1d04f87",
"triggered_at": "2026-01-15T18:05:32Z",
"run_id": "run_77a2b1f0c9d34e58"
}
const result = await client.agent.loops.runLoopNow({
id: "sess_3a91e6c2b1d04f87",
loopId: "lp_8c1f2a9d4e0b7c5f",
});
console.log(result.run_id);

Updates a loop by body shape. Each request must express exactly one intent — mixing more than one is rejected with 400:

  • Run state — set paused (true pauses, false resumes) → loops.set_state.
  • Expiry — set expires_in (a duration-from-now token; "" / "never" clears) → loops.set_expiry.
  • Budget revive — set max_cost_usd and/or max_wall_ms (revives or updates the budget ceiling) → loops.update. This intent does not change interval or prompt in v1.

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

NameInTypeRequiredDescription
idpathstringYesPath identifier.
loopIdpathstringYesPath identifier.
X-Hoody-CwdheaderstringNoPer-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-ContainerheaderstringNoPer-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector (§6.1): “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 (§6.1) — 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.
NameTypeRequiredDescription
pausedbooleanNoRun-state intent → loops.set_state (true pauses, false resumes).
expires_instringNoExpiry intent → loops.set_expiry (duration-from-now token, e.g. "2h"; ""/"never" clears).
max_cost_usdnumberNoBudget intent → loops.update (cost ceiling; 0 = unlimited).
max_wall_msintegerNoBudget intent → loops.update (wall-clock ceiling in ms, or a duration token; 0 = unlimited).
{
"loop_id": "lp_8c1f2a9d4e0b7c5f",
"session_id": "sess_3a91e6c2b1d04f87",
"paused": true,
"updated_at": "2026-01-15T18:10:00Z"
}
// Pause the loop
await client.agent.loops.updateLoop({
id: "sess_3a91e6c2b1d04f87",
loopId: "lp_8c1f2a9d4e0b7c5f",
data: { paused: true },
});

Permanently removes a recurring loop from a session.

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

NameInTypeRequiredDescription
idpathstringYesPath identifier.
loopIdpathstringYesPath identifier.
X-Hoody-CwdheaderstringNoPer-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-ContainerheaderstringNoPer-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector (§6.1): “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 (§6.1) — 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.

This endpoint accepts an optional JSON object forwarded to the daemon loops.delete RPC. The session id and loop id come from the path; the body is optional. No fields are required.

{
"loop_id": "lp_8c1f2a9d4e0b7c5f",
"session_id": "sess_3a91e6c2b1d04f87",
"deleted": true,
"deleted_at": "2026-01-15T18:20:00Z"
}
await client.agent.loops.deleteLoop({
id: "sess_3a91e6c2b1d04f87",
loopId: "lp_8c1f2a9d4e0b7c5f",
});