# Agent: Workflows

**Page:** api/agent/workflows

[Download Raw Markdown](./api/agent/workflows.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



Agent Workflows let you manage multi-step workflow definitions and run them onto live sessions. Use these endpoints to list, read, create, replace, hide, and delete workflow definitions, dispatch a workflow run onto a session, and snapshot, inspect, cancel, or resume workflow runs. Definitions are realm-scoped to the gateway's own realm; a client-supplied realm is ignored on definition and hide routes.

The base URL for every endpoint on this page is:
`https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu`

## Workflow definitions

### `GET /api/v1/agent/workflows`

List the workflow definitions visible to the requesting cwd/config_dir (name, summary, step briefs, system flag). The result is realm-scoped to the gateway's own realm; a client-supplied `X-Hoody-Realm`/`?realm=` is ignored.



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows?page=1&limit=20" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody"
```


```typescript
await client.agent.workflows.listWorkflowsIterator({ page: 1, limit: 20 })
```


```json
{
  "items": [
    {
      "name": "build",
      "summary": "Compile and run the project's test suite.",
      "steps": [
        { "id": "lint", "brief": "Run linter" },
        { "id": "test", "brief": "Run tests" }
      ],
      "system": false
    },
    {
      "name": "release",
      "summary": "Cut a tagged release.",
      "steps": [
        { "id": "bump", "brief": "Bump version" },
        { "id": "tag", "brief": "Tag and push" }
      ],
      "system": true
    }
  ],
  "meta": {
    "total": 2,
    "page": 1,
    "limit": 20
  }
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `page` | query | integer | No | 1-based page number for pagination. |
| `limit` | query | integer | No | Maximum items per page (0 = no pagination). |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"global"` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector — 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. |

### `GET /api/v1/agent/workflows/{name}`

Read one workflow definition. Returns the validated definition in the `{output, is_error}` tool envelope. With `?include_revision=true` the output's first line is `revision: r1:<64hex>` — the optimistic-concurrency baseline to send back as `expected_revision` on `putWorkflow`.



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/build?include_revision=true" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody"
```


```typescript
await client.agent.workflows.getWorkflow("build", { include_revision: true })
```


```json
{
  "status": "ok",
  "output": "revision: r1:4f2c8a1b9d3e7f0a5c6b8d2e9f1a4c7b8d0e2f4a6c8b0d2e4f6a8c0b2d4e6f80\n{\n  \"name\": \"build\",\n  \"summary\": \"Compile and run the project's test suite.\",\n  \"entry_point\": \"lint\",\n  \"steps\": [\n    { \"id\": \"lint\", \"brief\": \"Run linter\" },\n    { \"id\": \"test\", \"brief\": \"Run tests\" }\n  ]\n}",
  "is_error": false
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `tool_mutation_refused` | Tool mutation refused | A sessionless tool run resolved to a mutating tool with no confirmation posture. Sessionless runs are read-only by default. | Open a session and run the tool there, or supply `allow_mutations:true` / `confirm:true` on the sessionless run. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "code": "tool_not_found",
  "message": "no tool with that name"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `tool_not_found` | Tool not found | No tool with the given name exists in the catalogue or in the session's effective tool list. | List the catalogue (GET /tools) or the session's tools (GET /sessions/&#123;id&#125;/tools) and use a valid name. |


```json
{
  "code": "tool_needs_confirmation",
  "message": "tool requires confirmation before execution"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `tool_needs_confirmation` | Tool needs confirmation | The tool was NOT executed: the permission check requires confirmation first. The echoed tool+params let the caller re-issue the run with `confirm:true`. | Re-issue the run with `confirm:true` (or `?confirm=true`), preserving the echoed params. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `name` | path | string | Yes | Path identifier. |
| `include_revision` | query | boolean | No | If `"true"`, the tool output's first line is `revision: <opaque>` — pass that value as `putWorkflow`'s `expected_revision` to guard against concurrent edits; the JSON below it is unchanged. Strictly parsed: exactly one value, `"true"` or `"false"`; anything else (empty, `"TRUE"`, `"1"`, repeated) is a 400 `bad_request`. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

### `PUT /api/v1/agent/workflows/{name}`

Create or replace one workflow definition. The body carries the `definition` object; the `{name}` path value is authoritative. The daemon validates the definition strictly before writing atomically (unknown fields rejected, the loader's structural gate enforced). The `system` flag is authoritative from the embedded defaults — a caller cannot forge it.


Optimistic concurrency: read first with `GET /workflows/{name}?include_revision=true` and pass the returned `revision:` value as `expected_revision` to guard against concurrent edits. Use `expected_absent: true` for a create-only guard that refuses to overwrite an existing entry. These two guards are mutually exclusive.




```bash
curl -X PUT "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/build" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody" \
  -d '{
    "definition": {
      "name": "build",
      "summary": "Compile and run the project'\''s test suite.",
      "entry_point": "lint",
      "steps": [
        { "id": "lint", "brief": "Run linter" },
        { "id": "test", "brief": "Run tests" }
      ]
    },
    "expected_revision": "r1:4f2c8a1b9d3e7f0a5c6b8d2e9f1a4c7b8d0e2f4a6c8b0d2e4f6a8c0b2d4e6f80"
  }'
```


```typescript
await client.agent.workflows.putWorkflow("build", {
  definition: {
    name: "build",
    summary: "Compile and run the project's test suite.",
    entry_point: "lint",
    steps: [
      { id: "lint", brief: "Run linter" },
      { id: "test", brief: "Run tests" }
    ],
  },
  expected_revision: "r1:4f2c8a1b9d3e7f0a5c6b8d2e9f1a4c7b8d0e2f4a6c8b0d2e4f6a8c0b2d4e6f80",
})
```


```json
{
  "status": "ok",
  "output": "workflow build updated",
  "is_error": false
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `tool_mutation_refused` | Tool mutation refused | A sessionless tool run resolved to a mutating tool with no confirmation posture. | Open a session and run the tool there, or supply `allow_mutations:true` / `confirm:true` on the sessionless run. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |
| `reserved_name` | Reserved workflow name | The workflow name collides with a literal `/workflows/` route segment (e.g. `"runs"`), so its definition GET would be permanently shadowed. | Choose a workflow name that is not a reserved `/workflows/` route segment. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "tool_not_found",
  "message": "no tool with that name"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `tool_not_found` | Tool not found | No tool with the given name exists in the catalogue or in the session's effective tool list. | List the catalogue or the session's tools and use a valid name. |


```json
{
  "code": "realm_unpinned_conditional_write",
  "message": "conditional workflow write requires a realm-pinned gateway"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `realm_unpinned_conditional_write` | Conditional write needs a realm-pinned gateway | An `expected_revision` workflow upsert was issued through an unpinned agent gateway, which resolves its realm per request and cannot guarantee the baseline read and this write target the same realm. | Use a realm-pinned agent gateway for optimistic-concurrency writes, or save unconditionally by omitting `expected_revision`. `expected_absent` (create-only) is always allowed. |


```json
{
  "code": "payload_too_large",
  "message": "request body exceeds the configured size limit"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `name` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

#### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `definition` | object | Yes | The full workflow definition object (steps, entry_point, summary). Validated strictly server-side before an atomic write. |
| `expected_revision` | string | No | The `revision:` value from `getWorkflow` (`?include_revision=true`). If the stored workflow changed since that read, the upsert is refused with `[revision_conflict]` and nothing is written. Omit to save unconditionally. |
| `expected_absent` | boolean | No | Create-only guard: refuse with `[already_exists]` (writing nothing) if any workflow with this name already exists. Mutually exclusive with `expected_revision`. |

### `DELETE /api/v1/agent/workflows/{name}`

Delete one user workflow definition. System workflows are refused (they re-seed on boot; hide them instead via `hideWorkflow`).



```bash
curl -X DELETE "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/build" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody"
```


```typescript
await client.agent.workflows.deleteWorkflow("build")
```


```json
{
  "status": "ok",
  "output": "workflow build deleted",
  "is_error": false
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `tool_mutation_refused` | Tool mutation refused | A sessionless tool run resolved to a mutating tool with no confirmation posture. | Open a session and run the tool there, or supply `allow_mutations:true` / `confirm:true` on the sessionless run. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "tool_not_found",
  "message": "no tool with that name"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `tool_not_found` | Tool not found | No tool with the given name exists in the catalogue or in the session's effective tool list. | List the catalogue or the session's tools and use a valid name. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `name` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

### `POST /api/v1/agent/workflows/{name}/hide`

Hide or un-hide a workflow. Hiding is the only way to remove a system workflow from view — system workflows are re-seeded on every boot and can never be deleted. Pass `hidden: false` in the body to un-hide. Realm-scoped to the gateway's own realm so a hide stays realm-private.



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/release/hide" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody" \
  -d '{ "hidden": true }'
```


```typescript
await client.agent.workflows.hideWorkflow("release", { hidden: true })
```


```json
{
  "hidden": true,
  "hidden_workflows": ["release"]
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "not_found",
  "message": "resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |


```json
{
  "code": "payload_too_large",
  "message": "request body exceeds the configured size limit"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |


```json
{
  "code": "service_unavailable",
  "message": "service unavailable"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `name` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

#### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `hidden` | boolean | No | `true` (default) to hide the workflow; `false` to un-hide it. |

## Workflow runs

### `GET /api/v1/agent/workflows/runs`

Snapshot of in-flight and recently-finished workflow runs. The registry exposes `Snapshot()`, not `Subscribe()` — poll this endpoint to track a run's progress; live events also flow on the owning session's stream.



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/runs?page=1&limit=20" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody"
```


```typescript
await client.agent.workflows.listWorkflowRunsIterator({ page: 1, limit: 20 })
```


```json
{
  "items": [
    {
      "run_id": "01HZX8R9C2QF7G3W4K5N6P7Q8R",
      "workflow": "build",
      "session_id": "01HZX8R5T1V3B7X9Y2Z4N6P8Q0",
      "status": "running",
      "started_at": "2026-01-15T09:00:00.000Z",
      "step": "lint"
    },
    {
      "run_id": "01HZX8R4A1B2C3D4E5F6G7H8J9",
      "workflow": "release",
      "session_id": "01HZX8R5T1V3B7X9Y2Z4N6P8Q0",
      "status": "completed",
      "started_at": "2026-01-15T08:30:00.000Z",
      "finished_at": "2026-01-15T08:35:12.000Z"
    }
  ],
  "meta": {
    "total": 2,
    "page": 1,
    "limit": 20
  }
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `page` | query | integer | No | 1-based page number for pagination. |
| `limit` | query | integer | No | Maximum items per page (0 = no pagination). |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

### `GET /api/v1/agent/workflows/runs/{run_id}`

Get one workflow run by id, including per-step outcomes (step id, status, duration, tokens, and error) that the leaner list snapshot omits. Returns 404 when the run id is no longer retained (evicted or never existed), or belongs to another realm or account.



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/runs/01HZX8R9C2QF7G3W4K5N6P7Q8R" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody"
```


```typescript
await client.agent.workflows.getWorkflowRun("01HZX8R9C2QF7G3W4K5N6P7Q8R")
```


```json
{
  "run_id": "01HZX8R9C2QF7G3W4K5N6P7Q8R",
  "workflow": "build",
  "session_id": "01HZX8R5T1V3B7X9Y2Z4N6P8Q0",
  "status": "completed",
  "started_at": "2026-01-15T09:00:00.000Z",
  "finished_at": "2026-01-15T09:04:32.000Z",
  "steps": [
    {
      "id": "lint",
      "status": "completed",
      "duration_ms": 18432,
      "input_tokens": 1280,
      "output_tokens": 412
    },
    {
      "id": "test",
      "status": "completed",
      "duration_ms": 95211,
      "input_tokens": 3104,
      "output_tokens": 1102
    }
  ]
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "not_found",
  "message": "resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |


```json
{
  "code": "service_unavailable",
  "message": "service unavailable"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `run_id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

### `POST /api/v1/agent/sessions/{id}/workflows/{name}/runs`

Dispatch a workflow run onto a live session. Returns a `job_id`; `run_id` is `null` during the brief dispatch window and is populated once the workflow loop registers the run (observe `workflow_start` on the session's stream). Run events flow on the owning session's WS/SSE attach; there is no per-run event bus.



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/sessions/01HZX8R5T1V3B7X9Y2Z4N6P8Q0/workflows/build/runs" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody" \
  -d '{
    "prompt": "Run the nightly build.",
    "inputs": {
      "branch": "main",
      "verbose": "true"
    }
  }'
```


```typescript
await client.agent.workflows.runSessionWorkflow(
  "01HZX8R5T1V3B7X9Y2Z4N6P8Q0",
  "build",
  {
    prompt: "Run the nightly build.",
    inputs: {
      branch: "main",
      verbose: "true",
    },
  }
)
```


```json
{
  "job_id": "01HZX8RAB1C2D3E4F5G6H7J8K9",
  "session_id": "01HZX8R5T1V3B7X9Y2Z4N6P8Q0",
  "run_id": null
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "workflow_not_found",
  "message": "no workflow named build"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |
| `workflow_not_found` | Workflow not found | No workflow definition with the requested name is visible to this cwd/config_dir. | List the available workflows (GET /workflows) and use an existing name. |


```json
{
  "code": "turn_in_flight",
  "message": "a turn is already running on this session"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `turn_in_flight` | Turn in flight | A turn is already running on this session; the single serial turn slot is occupied, so a new turn/workflow run is refused. | Wait for the running turn to finish (observe `agent_done` on the session stream), then retry. |
| `gate_parked` | Gate parked | A confirm/question gate is parked on this session, so a new turn/workflow run is refused until it is answered. The parked gate is surfaced under `details.pending_gate`. | Answer the parked gate (`/confirm` or `/answer`) and retry; read it via `GET /sessions/{id}` or `details.pending_gate`. |


```json
{
  "code": "payload_too_large",
  "message": "request body exceeds the configured size limit"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |


```json
{
  "code": "service_unavailable",
  "message": "service unavailable"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `name` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

#### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `prompt` | string | No | Optional input text fed to the workflow run (`$(workflow.prompt)`). |
| `inputs` | object | No | Optional run-time values for the workflow's declared input parameters (declared name → string value; resolves to `$(input.&lt;name&gt;)` in every step). A workflow with a REQUIRED declared parameter cannot run without these. Values must be strings; a non-string value is a 400. |

### `POST /api/v1/agent/workflows/runs/{run_id}/cancel`

Cancel an in-flight workflow run.



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/runs/01HZX8R9C2QF7G3W4K5N6P7Q8R/cancel" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody"
```


```typescript
await client.agent.workflows.cancelWorkflowRun("01HZX8R9C2QF7G3W4K5N6P7Q8R")
```


```json
{
  "run_id": "01HZX8R9C2QF7G3W4K5N6P7Q8R",
  "status": "cancelled",
  "cancelled_at": "2026-01-15T09:02:11.000Z"
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "not_found",
  "message": "resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |


```json
{
  "code": "payload_too_large",
  "message": "request body exceeds the configured size limit"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |


```json
{
  "code": "service_unavailable",
  "message": "service unavailable"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `run_id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

### `POST /api/v1/agent/workflows/runs/{run_id}/resume`

Resume a terminal (failed or cancelled) workflow run on a live session. Committed steps replay from the run's recorded journal and execution continues live from the first incomplete step. The resuming session must match the run's realm, owner, working directory, and container binding.



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/workflows/runs/01HZX8R9C2QF7G3W4K5N6P7Q8R/resume" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/projects/hoody" \
  -d '{ "session_id": "01HZX8R5T1V3B7X9Y2Z4N6P8Q0" }'
```


```typescript
await client.agent.workflows.resumeWorkflowRun(
  "01HZX8R9C2QF7G3W4K5N6P7Q8R",
  { session_id: "01HZX8R5T1V3B7X9Y2Z4N6P8Q0" }
)
```


```json
{
  "run_id": "01HZX8R9C2QF7G3W4K5N6P7Q8R",
  "session_id": "01HZX8R5T1V3B7X9Y2Z4N6P8Q0",
  "status": "running",
  "resumed_at": "2026-01-15T09:05:00.000Z",
  "replayed_steps": ["lint"]
}
```


```json
{
  "code": "bad_request",
  "message": "invalid request"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
| `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC. | Omit the realm header on this route, or open a session to scope by realm. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through hoody-proxy, not by connecting to the container directly. |


```json
{
  "code": "not_found",
  "message": "resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |


```json
{
  "code": "payload_too_large",
  "message": "request body exceeds the configured size limit"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |


```json
{
  "code": "rate_limited",
  "message": "request rate limit exceeded"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `rate_limited` | Too many requests | The per-client request rate limit was exceeded. | Honor the Retry-After header and retry; reduce the request rate. |


```json
{
  "code": "internal_error",
  "message": "internal server error"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |


```json
{
  "code": "service_unavailable",
  "message": "service unavailable"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |



#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `run_id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector. |
| `realm` | query | string | No | Per-request realm selector — query alias of the `X-Hoody-Realm` header. |

#### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | Yes | A live session matching the run's realm, owner, working directory, and container binding. |