Skip to content
Hoody.com

The agent’s log API exposes a redacted, cursor-paginated log stream plus stats, sources, and a download endpoint. Use these routes to surface logs in dashboards, build alerts, or back the Logs tab.

All routes are active-only: supplying X-Hoody-Realm or ?realm= returns 400 realm_scope_unsupported because logs have no realm dimension. Log entries are always redacted at the source. Read endpoints return the verbatim daemon action’s own object — treat the response shape as free-form.

Queries the active log stream (logs.query). Filters ride as query params (source, level, host, since, until). ?limit=N caps the result set (daemon default 200) and is forwarded; the stream paginates by cursor (since_seq/before_seq), not by page. For the live tail, use GET /api/v1/agent/logs/stream.

NameInTypeRequiredDescription
sourcequerystringNoFilter to a log source/facet (see logsSources).
levelquerystringNoFilter to a minimum log level.
hostquerystringNoFilter to a host.
sincequerystringNoLower time/cursor bound (since_seq cursor passes through verbatim).
untilquerystringNoUpper time bound.
limitqueryintegerNoCaps the result set (daemon default 200). A non-numeric value is rejected 400.
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves.
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 routes.
realmquerystringNoIn-query alias of X-Hoody-Realm, read only when the header is absent. Same rejection semantics.
Terminal window
curl -G \
"https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/logs" \
-H "Authorization: Bearer $HOODY_TOKEN" \
--data-urlencode "source=daemon" \
--data-urlencode "level=info" \
--data-urlencode "limit=50"
{
"entries": [
{
"seq": 48912,
"ts": "2025-03-14T19:02:11.482Z",
"level": "info",
"source": "daemon",
"host": "node-us",
"comp": "logs.query",
"message": "served 47 entries since_seq=48905"
},
{
"seq": 48911,
"ts": "2025-03-14T19:02:10.001Z",
"level": "warn",
"source": "tool",
"host": "node-us",
"tool": "fs.write",
"session_id": "sess_8a7c",
"message": "permission prompt required"
}
],
"next_seq": 48913,
"gap": false
}

Reads one log entry by ref, always redacted (logs.read_entry). The entry ref is the seq returned by queryLogs or carried in the SSE entry frame’s id field.

NameInTypeRequiredDescription
refpathstringYesEntry reference (seq id).
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector. Rejected (400 realm_scope_unsupported) on active-only routes.
realmquerystringNoIn-query alias of X-Hoody-Realm, read only when the header is absent. Same rejection semantics.
Terminal window
curl \
"https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/logs/entries/48911" \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"seq": 48911,
"ts": "2025-03-14T19:02:10.001Z",
"level": "warn",
"source": "tool",
"host": "node-us",
"comp": "tool.invoke",
"session_id": "sess_8a7c",
"tool": "fs.write",
"attrs": {
"path": "/srv/work/.hoody/state.json",
"bytes": 1842,
"token": "[REDACTED]"
},
"message": "permission prompt required"
}

Lists the available log sources/facets (logs.sources). Use this to populate filter dropdowns.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector. Rejected (400 realm_scope_unsupported) on active-only routes.
realmquerystringNoIn-query alias of X-Hoody-Realm, read only when the header is absent. Same rejection semantics.
Terminal window
curl \
"https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/logs/sources" \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"sources": [
{ "name": "daemon", "count": 184233 },
{ "name": "session", "count": 12044 },
{ "name": "tool", "count": 88412 },
{ "name": "llm", "count": 23011 },
{ "name": "activity", "count": 14902 },
{ "name": "events", "count": 2104 },
{ "name": "proxy", "count": 8744 }
]
}

Returns log volume and level statistics (logs.stats).

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector. Rejected (400 realm_scope_unsupported) on active-only routes.
realmquerystringNoIn-query alias of X-Hoody-Realm, read only when the header is absent. Same rejection semantics.
Terminal window
curl \
"https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/logs/stats" \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"total": 328550,
"by_level": {
"debug": 122004,
"info": 180221,
"warn": 18412,
"error": 7913
},
"by_source": {
"daemon": 184233,
"session": 12044,
"tool": 88412,
"llm": 23011,
"activity": 14902,
"events": 2104,
"proxy": 8744
},
"window": "all"
}

Tails the active log stream over Server-Sent Events (logs.tail). The tail paginates with a cursor it advances each round (since_seq is the previous reply’s next_seq), so no row is skipped under load. The tail rides the same seq/replay convention the session stream uses:

  • an entry frame per redacted row carrying id: <seq> (a reconnecting client resumes via the Last-Event-ID header, which overrides ?since_seq);
  • a lagged frame with {code: "replay_gap"} when the caller’s cursor fell behind the ring;
  • periodic heartbeats (comment lines beginning :);
  • end on disconnect.

Filters ride query params (source, level, host, since_seq); ?limit caps each poll batch. Local sources only.

NameInTypeRequiredDescription
sourcequerystringNoFilter the tail to a log source/facet.
levelquerystringNoFilter to a minimum log level.
hostquerystringNoFilter to a host.
since_seqqueryintegerNoInitial resume cursor (the Last-Event-ID header overrides it). A non-numeric value is rejected 400.
limitqueryintegerNoCaps each poll batch. A non-numeric value is rejected 400.
Last-Event-IDheaderstringNoSSE resume cursor: the gateway int64 seq to resume from; OVERRIDES the ?since_seq query param. Sent automatically by an SSE client on reconnect.
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector. Rejected (400 realm_scope_unsupported) on active-only routes.
realmquerystringNoIn-query alias of X-Hoody-Realm, read only when the header is absent. Same rejection semantics.
Terminal window
curl -N \
"https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/logs/stream?source=daemon&level=info&limit=100" \
-H "Authorization: Bearer $HOODY_TOKEN"
event: entry
id: 48912
data: {"seq":48912,"ts":"2025-03-14T19:02:11.482Z","level":"info","source":"daemon","host":"node-us","message":"served 47 entries since_seq=48905"}
event: entry
id: 48913
data: {"seq":48913,"ts":"2025-03-14T19:02:13.014Z","level":"debug","source":"daemon","host":"node-us","message":"ringbuf snapshot taken"}
: heartbeat
event: lagged
data: {"code":"replay_gap","since_seq":48913,"replay_seq":49001}
event: end
data: {"reason":"client_disconnect"}

Streams the filtered, always-redacted log set as a download (Content-Disposition: attachment). format=jsonl (default, one wire row per line) or txt (human-readable).

Local sources export a point-in-time-consistent snapshot of the in-daemon ring, bounded at the seq observed when the export starts. source=activity|events|proxy exports one platform query page (default limit 2000) and marks the export partial when more remained. Every export ends with an in-band terminator:

  • jsonl: a final {"_hoody_export":{...}} line carrying rows/gap/partial — its absence means the download was truncated.
  • txt: a trailing # export: ... comment carrying the same fields.

Filters mirror the Logs tab exactly (source, min_level, comp, session_id, text, since, until, event, tool, model, status, method, min_status, max_status, errors_only, event_type, resource_type, container, kind, host); ?since_seq exports incrementally; ?limit caps total rows; ?filename overrides the download name (reduced to a safe basename).

NameInTypeRequiredDescription
sourcequerystringNoLog source to export (see logsSources; one local source, one platform source, or omitted for all local sources).
min_levelquerystringNoMinimum log level (debug|info|warn|error).
compquerystringNoComponent filter (daemon source).
session_idquerystringNoSession id filter.
textquerystringNoCase-insensitive substring filter over message+attrs.
sincequerystringNoLower time bound (RFC3339 or relative like 1h/7d).
untilquerystringNoUpper time bound (RFC3339 or relative).
eventquerystringNoSession lifecycle event filter (session source).
toolquerystringNoTool name filter (tool source).
modelquerystringNoModel filter (llm source).
statusquerystringNoTool outcome filter: ok|error|cancelled (tool source).
methodquerystringNoHTTP method filter (activity source).
min_statusqueryintegerNoMinimum HTTP status (activity source).
max_statusqueryintegerNoMaximum HTTP status (activity source).
errors_onlyquerybooleanNoOnly error rows (activity source; true/false).
event_typequerystringNoEvent type filter (events source).
resource_typequerystringNoResource type filter (events source).
containerquerystringNoContainer filter (proxy source; empty = all running realm containers). Maps to the daemon’s container_id filter.
kindquerystringNoProxy row kind: request|response|event (proxy source).
hostquerystringNoProxy URL host filter (exact or dot-aligned suffix).
since_seqqueryintegerNoExclusive lower seq bound for incremental exports (local sources).
limitqueryintegerNoTOTAL row cap across the export (default: everything the snapshot matches; platform default 2000).
formatquerystringNoExport format: jsonl (default) or txt.
filenamequerystringNoDownload filename override (reduced to a safe basename).
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector. Rejected (400 realm_scope_unsupported) on active-only routes.
realmquerystringNoIn-query alias of X-Hoody-Realm, read only when the header is absent. Same rejection semantics.
Terminal window
curl -G \
"https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/logs/export" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-o logs-tool-2025-03-14.jsonl \
--data-urlencode "source=tool" \
--data-urlencode "min_level=warn" \
--data-urlencode "since=2025-03-14T00:00:00Z" \
--data-urlencode "format=jsonl" \
--data-urlencode "filename=tool-warnings.jsonl"
{"seq":48912,"ts":"2025-03-14T19:02:11.482Z","level":"info","source":"daemon","host":"node-us","message":"served 47 entries since_seq=48905"}
{"seq":48911,"ts":"2025-03-14T19:02:10.001Z","level":"warn","source":"tool","host":"node-us","tool":"fs.write","session_id":"sess_8a7c","message":"permission prompt required"}
{"seq":48910,"ts":"2025-03-14T19:02:08.770Z","level":"error","source":"tool","host":"node-us","tool":"net.fetch","status":"error","message":"dial tcp: lookup api.example.com: no such host"}
{"_hoody_export":{"rows":3,"gap":false,"partial":false}}