Log management
Section titled “Log management”The Log Management API exposes five operations for working with the log files written by the exec service on a container: list the available files, read or stream their contents, search across them, and clear old entries. Every endpoint is scoped to a single container instance, addressed through the request URL, and requires an authenticated session.
GET /api/v1/exec/logs/list
Section titled “GET /api/v1/exec/logs/list”Returns the log files available in the container’s logs directory. The result may be filtered by type and capped by limit.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
type | query | string | No | "all" | Which logs to list. One of all, request, access, cron, other. Any other value is refused with 400. |
limit | query | string | No | Maximum number of files to return. |
Code samples
Section titled “Code samples”curl -X GET "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/logs/list?type=access&limit=50" \ -H "Authorization: Bearer $HOODY_TOKEN"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.logs.list({ type: 'access', limit: '50' });Response
Section titled “Response”{ "logs": [ { "name": "access/access.log", "size": 4096 }, { "name": "executions/exec-123/execution.log", "size": 8192 } ], "count": 2}The response may also include a directory field listing the top-level directory that was scanned.
{ "error": "Malformed query string", "code": "ERROR_400", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_400 | Bad request | The service answers 400 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | Percent-encoding failed to decode. Use valid %XX escapes. |
{ "error": "Too many distinct clients", "code": "ERROR_429", "timestamp": "2026-01-01T00:00:00.000Z", "retryAfter": 60}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_429 | Too many requests | The service answers 429 with this code; the message names the cause. Messages: “Too many distinct clients”, “Too many requests”. | (none) |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_500 | Internal server error | The service answers 500 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | (none) |
GET /api/v1/exec/logs/stream
Section titled “GET /api/v1/exec/logs/stream”Opens a server-sent event stream over a log file. With follow=true (the default), the connection stays open and new lines are delivered as they are written.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
file | query | string | Yes | Name of the log file to stream, as returned by logs/list. | |
follow | query | boolean | No | true | Keep the stream open and send new lines as they are written. Accepts true/false/1/0. |
Code samples
Section titled “Code samples”curl -N -X GET "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/logs/stream?file=executions/exec-123/execution.log&follow=true" \ -H "Authorization: Bearer $HOODY_TOKEN"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.logs.stream({ file: 'executions/exec-123/execution.log', follow: true });Response
Section titled “Response”The response is a text/event-stream body. Each event is a data: line carrying one log record, terminated by a blank line:
data: {"line": "2026-01-01 12:00:00 INFO Starting service"}
data: {"line": "2026-01-01 12:00:01 INFO Ready"}
event: gapdata: {"reason": "rotated_file_unavailable", "file": "execution.log.1"}
data: {"line": "2026-01-01 12:00:05 INFO Running task"}{ "error": "file parameter is required", "code": "ERROR_400", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_400 | Bad request | The service answers 400 with this code; the message names the cause. Messages: “file parameter is required”, “follow must be true/1 or false/0”, “Invalid log file name”, “Malformed query string” (Percent-encoding failed to decode. Use valid %XX escapes.). | (none) |
{ "error": "Log file not found", "code": "ERROR_404", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_404 | Not found | The service answers 404 with this code; the message names the cause. | (none) |
{ "error": "Too many distinct clients", "code": "ERROR_429", "timestamp": "2026-01-01T00:00:00.000Z", "retryAfter": 60}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_429 | Too many requests | The service answers 429 with this code; the message names the cause. Messages: “Too many distinct clients”, “Too many requests”. | (none) |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_500 | Internal server error | The service answers 500 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | (none) |
POST /api/v1/exec/logs/read
Section titled “POST /api/v1/exec/logs/read”Reads a log file or a specific file belonging to an execution and returns the lines, optionally filtered by a search term. Either file or executionId must be supplied.
Request body
Section titled “Request body”| Name | Type | Required | Default | Description |
|---|---|---|---|---|
file | string | Conditional | Log file to read, as logs/list names it: a file at the top of the logs directory, access/access.log or cron/fires.log (or a rotated .<...> copy), or executions/<id>/<file>. With executionId, one of that execution’s files instead: execution.log, request.log, response.log, summary.json, metadata.json (default execution.log). | |
executionId | string | Conditional | Execution whose log to read: 1 to 64 letters, digits, hyphens, or underscores. An empty string is the same as omitting it. | |
lines | integer | string | No | 100 | Maximum number of lines to return, 1 to 10000. A string of digits in that range is also accepted. |
tail | boolean | No | true | If true, return the most recent lines; otherwise return from the start. |
search | string | No | If set, only return lines that contain this substring. |
Code samples
Section titled “Code samples”curl -X POST "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/logs/read" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "file": "executions/exec-123/execution.log", "lines": 100, "tail": true, "search": "ERROR" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.logs.get({ file: 'executions/exec-123/execution.log', lines: 100, tail: true, search: 'ERROR'});Response
Section titled “Response”{ "file": "executions/exec-123/execution.log", "executionId": "exec-123", "files": [ "executions/exec-123/execution.log", "executions/exec-123/request.log" ], "totalLines": 250, "filteredLines": 12, "returnedLines": 12, "lines": [ "2026-01-01 12:00:00 INFO Starting task", "2026-01-01 12:00:01 ERROR Connection refused" ], "size": 4096, "modified": "2026-01-01T12:00:01.000Z"}{ "error": "file or executionId is required", "code": "ERROR_400", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_400 | Bad request | The service answers 400 with this code; the message names the cause. Messages: “file or executionId is required”, “Invalid executionId”, “Invalid log file name”, “Malformed query string” (Percent-encoding failed to decode. Use valid %XX escapes.), “Invalid JSON body” (Ensure the request body is valid JSON. Example: a backslash followed by a backtick is not a valid JSON escape.), “Request aborted before the body was fully received”. Other messages on this status are composed at runtime and are not listed here. | (none) |
{ "error": "Log file not found", "code": "ERROR_404", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_404 | Not found | The service answers 404 with this code; the message names the cause. | (none) |
{ "error": "Request body read timeout", "code": "ERROR_408", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_408 | Request timeout | The service answers 408 with this code; the message names the cause. | (none) |
{ "error": "Payload too large", "code": "ERROR_413", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_413 | Payload too large | The service answers 413 with this code; the message names the cause. | (none) |
{ "error": "Too many distinct clients", "code": "ERROR_429", "timestamp": "2026-01-01T00:00:00.000Z", "retryAfter": 60}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_429 | Too many requests | The service answers 429 with this code; the message names the cause. Messages: “Too many distinct clients”, “Too many requests”. | (none) |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_500 | Internal server error | The service answers 500 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | (none) |
POST /api/v1/exec/logs/search
Section titled “POST /api/v1/exec/logs/search”Searches log files for a literal string or a regular expression. At least one of query or regex must be supplied.
Request body
Section titled “Request body”| Name | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Conditional | Literal string to search for. | |
regex | string | Conditional | Regular expression to search for. | |
files | array of string | No | List of file names to search within. When omitted, all available log files are searched. | |
limit | integer | No | 1000 | Maximum number of matches to return. |
caseSensitive | boolean | No | false | If true, the match is case-sensitive. |
Code samples
Section titled “Code samples”curl -X POST "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/logs/search" \ -H "Authorization: Bearer $HOODY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "ERROR", "limit": 100, "caseSensitive": false }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.logs.search({ query: 'ERROR', limit: 100, caseSensitive: false});Response
Section titled “Response”{ "query": "ERROR", "searchType": "literal", "filesSearched": 5, "matchesFound": 3, "results": [ { "file": "executions/exec-123/execution.log", "line": 42, "content": "2026-01-01 12:00:01 ERROR Connection refused" } ]}{ "error": "query or regex is required", "code": "ERROR_400", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_400 | Bad request | The service answers 400 with this code; the message names the cause. Messages: “query or regex is required”, “Invalid log file name in files array”, “Malformed query string” (Percent-encoding failed to decode. Use valid %XX escapes.), “Invalid JSON body” (Ensure the request body is valid JSON. Example: a backslash followed by a backtick is not a valid JSON escape.), “Request aborted before the body was fully received”. Other messages on this status are composed at runtime and are not listed here. | (none) |
{ "error": "Request body read timeout", "code": "ERROR_408", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_408 | Request timeout | The service answers 408 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | (none) |
{ "error": "Payload too large", "code": "ERROR_413", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_413 | Payload too large | The service answers 413 with this code; the message names the cause. | (none) |
{ "error": "Too many concurrent regex searches, retry later", "code": "ERROR_429", "timestamp": "2026-01-01T00:00:00.000Z", "retryAfter": 60}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_429 | Too many requests | The service answers 429 with this code; the message names the cause. Messages: “Too many concurrent regex searches, retry later”, “Too many distinct clients”, “Too many requests”. | (none) |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_500 | Internal server error | The service answers 500 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | (none) |
DELETE /api/v1/exec/logs/clear
Section titled “DELETE /api/v1/exec/logs/clear”Removes log files matching the supplied filters. The request must include the safety parameter confirm=true; otherwise the call is rejected with 400.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
file | query | string | No | Delete only this specific file. |
type | query | string | No | Delete only files of this type. |
olderThanDays | query | string | No | Delete only files older than this many days (non-negative number). |
confirm | query | string | Yes | Safety confirmation; must be the literal true or the request is rejected with 400. |
Code samples
Section titled “Code samples”curl -X DELETE "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/logs/clear?olderThanDays=7&confirm=true" \ -H "Authorization: Bearer $HOODY_TOKEN"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.logs.clear({ confirm: 'true', olderThanDays: '7' });Response
Section titled “Response”{ "deleted": 15, "totalSize": 1048576, "skippedActive": 2, "message": "Cleared 15 log files"}{ "error": "confirm=true parameter required for safety", "code": "ERROR_400", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_400 | Bad request | The service answers 400 with this code; the message names the cause. Messages: “confirm=true parameter required for safety”, “olderThanDays must be a non-negative number”, “Invalid log file name”, “Malformed query string” (Percent-encoding failed to decode. Use valid %XX escapes.). Other messages on this status are composed at runtime and are not listed here. | (none) |
{ "error": "Too many distinct clients", "code": "ERROR_429", "timestamp": "2026-01-01T00:00:00.000Z", "retryAfter": 60}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_429 | Too many requests | The service answers 429 with this code; the message names the cause. Messages: “Too many distinct clients”, “Too many requests”. | (none) |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2026-01-01T00:00:00.000Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ERROR_500 | Internal server error | The service answers 500 with this code; the message names the cause. Other messages on this status are composed at runtime and are not listed here. | (none) |