Skip to content
Hoody.com

Use these endpoints to query, summarize, and live-tail the proxy access logs stored per container. GET /_logs searches historical entries, GET /_logs/stats returns aggregate counts, and GET /_logs/stream opens a Server-Sent Events connection for real-time tailing.

The container-scoped base URL follows the pattern https://{projectId}-{containerId}-logs-1.{server}.containers.hoody.com. Replace {projectId}, {containerId}, and {server} with your values.

Search and filter through the stored request, response, and event logs. Supports pagination, time-range filtering, and narrowing by service, kind, level, or source.

NameInTypeRequiredDescription
limitqueryintegerNoPage size. A value above 1000 is treated as 1000. Default: 200.
offsetqueryintegerNoEntries to skip: counted from the newest match, or with afterId from the oldest entry after the cursor. Ignored with last. Default: 0.
projectIdquerystringNoFilter to a single project.
containerIdquerystringNoFilter to a single container.
serviceNamequerystringNoFilter to one service. The per-container logs URL ignores it, so filter the returned entries client-side. GET /_logs/stream honours it.
levelquerystringNoLog level: exactly ONE of debug, info, warn, or error. A comma-separated value matches nothing.
includeRequestBodyquerybooleanNoInclude the request body payload in each entry. Default: false.
includeResponseBodyquerybooleanNoInclude the response body payload in each entry. Default: false.
lastqueryintegerNoReturn only the newest N matching entries, oldest first, in one response: total is the number returned and offset does not apply. Takes precedence over afterId.
afterIdqueryintegerNoReturn the entries whose row id (id) is greater than this, oldest first. total counts every such entry and offset pages through them.
kindquerystringNoEntry kind. One of request, response, or event.
methodquerystringNoFilter by HTTP method.
sourcequerystringNoFilter by origin. One of backend or edge.
sinceMsqueryintegerNoOnly entries whose tsMs is at least this (Unix milliseconds).
untilMsqueryintegerNoOnly entries whose tsMs is at most this (Unix milliseconds).
Terminal window
curl -G "https://{projectId}-{containerId}-logs-1.{server}.containers.hoody.com/_logs" \
-H "Authorization: Bearer $HOODY_TOKEN" \
--data-urlencode "limit=50" \
--data-urlencode "level=error" \
--data-urlencode "sinceMs=1730000000000"

Returned when the request is denied.

{
"error": "Forbidden",
"errorCode": "LOGS_FORBIDDEN"
}
FieldTypeDescription
errorstringHuman-readable error message.
errorCodestringMachine-readable error code.

Returns aggregate counts of stored log entries broken down by level, project, container, and service. Useful for dashboards and quick health checks.

This endpoint takes no parameters.

Terminal window
curl "https://{projectId}-{containerId}-logs-1.{server}.containers.hoody.com/_logs/stats" \
-H "Authorization: Bearer $HOODY_TOKEN"

Opens a persistent Server-Sent Events connection that streams new log entries as they arrive. Use this for live-tail UIs and alerting pipelines.

Each live log entry is one frame with a numeric id: line:

id: 12345
data: <LogEntry JSON>

Not every frame has an id:. A fresh connection (no Last-Event-ID) first receives one id-less data: frame holding a JSON array of recent matching entries, when any exist; with none, its first frame is a live entry with its id:. The gap event below has no id: either, and heartbeats are comments. Keep the last id you received; a frame without one does not change it.

An entry whose frame would exceed 256 KiB is sent as a stub that keeps its id and carries truncated: true and originalFrameBytes.

Clients MAY send Last-Event-ID: <id> on reconnect, with the id of the last frame they received. The server then replays the buffered frames newer than that id that match the stream’s filters, instead of the initial batch.

The replay buffer holds at most the 2,000 most recent frames and at most 8 MiB of them, whichever limit is reached first; the oldest frames are dropped first. Every stream on the server shares this buffer, so one container’s entries can leave it after far fewer than 2,000 of its own. The server can also end a stream once it has sent a per-connection byte budget; reconnecting with Last-Event-ID continues from where it stopped.

  • event: gap: sent first on a resume when the buffer no longer reaches back to your Last-Event-ID; the entries in between are not replayed. It has no id:. Its data is {"after": "<your Last-Event-ID>", "resumedFrom": "<oldest buffered id>"}. Read what you missed from GET /_logs (match on traceId and tsMs), then carry on with the frames that follow.
  • event: purged: carries an id: and data: {}. It stands in for a buffered entry whose logs were deleted, so your cursor still moves past it.
  • event: scope-destroyed: container destroyed; stream closes immediately after. Clients should exit cleanly.
  • event: reset: the stream is being reset and closes right after. Discard the last id you saw and reconnect without Last-Event-ID.

A restart ends every open stream without an event. Frame ids then resume at least 10,000 past the last value the server saved, and the replay buffer starts empty, so reconnecting with an id from before the restart gets a gap event followed by new frames.

A : keepalive\n\n comment heartbeat every 5 s keeps the connection alive. The server also sends one : open\n\n comment as soon as the stream is set up, after any initial batch, replay or gap event, so a stream with nothing to deliver yet still starts at once. Both are SSE comments, not frames: ignore them.

NameInTypeRequiredDescription
projectIdquerystringNoFilter to a single project.
containerIdquerystringNoFilter to a single container.
serviceNamequerystringNoFilter the stream to one service, e.g. tunnel. Honoured here, unlike GET /_logs.
kindquerystringNoFilter by entry kind. One of request, response, or event.
levelquerystringNoFilter by log level. One of debug, info, warn, or error.
sourcequerystringNoFilter by origin. One of backend or edge.
afterIdqueryintegerNoLimits the initial batch of a fresh connection to entries whose row id (id) is greater than this: the same id that GET /_logs returns and takes as afterId. Live frames are unaffected.
Last-Event-IDheaderstringNoThe numeric id of the last event received. On reconnect the server skips buffered entries whose id is at most this value.
Terminal window
curl -N "https://{projectId}-{containerId}-logs-1.{server}.containers.hoody.com/_logs/stream?level=error&serviceName=tunnel" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Last-Event-ID: 1923"

403 Logs token missing or invalid, or the gateway policy denied the request

Section titled “403 Logs token missing or invalid, or the gateway policy denied the request”

Returned when the request is denied.

Returned when rate limits are exceeded; reconnect with backoff.