> ## Documentation Index
> Fetch the complete documentation index at: https://docs.herm.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Events Reference

> Complete reference for user, agent, and session event payloads.

Events are the session protocol between your application and a managed Herm
agent.

Use [`POST /v1/sessions/{sessionId}/events`](/api-reference/send-message) to send
`user.*` events. Use [`GET /v1/sessions/{sessionId}/events`](/api-reference/stream-events#list-events)
and [`GET /v1/sessions/{sessionId}/events/stream`](/api-reference/stream-events#stream-events)
to receive `agent.*` and `session.*` events.

## Event envelope

Every listed or streamed event is wrapped in an envelope:

```json theme={null}
{
  "id": "evt_123",
  "type": "agent.message",
  "sessionId": "session_123",
  "sequence": 6,
  "status": "complete",
  "payload": {
    "type": "agent.message",
    "delta": false,
    "content": [{ "type": "text", "text": "Hello!" }]
  },
  "processedAt": "2026-06-20T23:28:02.772Z",
  "createdAt": "2026-06-20T23:28:02.773Z"
}
```

| Field         | Type    | Description                                              |
| ------------- | ------- | -------------------------------------------------------- |
| `id`          | string  | Gateway event ID.                                        |
| `type`        | string  | Event type, duplicated from `payload.type`.              |
| `sessionId`   | string  | Session ID.                                              |
| `sequence`    | integer | Monotonic sequence number within the session.            |
| `status`      | string  | Event processing status.                                 |
| `payload`     | object  | Event-specific payload.                                  |
| `processedAt` | string  | ISO timestamp when processing completed, when available. |
| `createdAt`   | string  | ISO timestamp when the event was created.                |

## Shared types

### Text content block

```json theme={null}
{ "type": "text", "text": "Hello" }
```

Text content arrays contain 1 to 100 text blocks. Each `text` value can be up to
20,000 characters.

### Stop reason

`session.status_idle.stop_reason` is one of:

```json theme={null}
{ "type": "end_turn" }
```

```json theme={null}
{ "type": "error", "message": "The runtime failed." }
```

```json theme={null}
{ "type": "requires_action", "event_ids": ["custom_toolu_123"] }
```

When `type` is `requires_action`, send the matching `user.*` response event.

## User events

User events are accepted by `POST /v1/sessions/{sessionId}/events`.

### `user.message`

Starts a new agent turn.

```json theme={null}
{
  "type": "user.message",
  "content": [{ "type": "text", "text": "Say hello in one sentence." }]
}
```

| Field     | Type          | Required | Description             |
| --------- | ------------- | -------- | ----------------------- |
| `type`    | string        | Yes      | Must be `user.message`. |
| `content` | text block\[] | Yes      | User message content.   |

Only one `user.message` is allowed per request. A new `user.message` is rejected
while another turn is already running.

### `user.steer`

Adds steering text to the current session without starting a normal prompt.

```json theme={null}
{
  "type": "user.steer",
  "message": "Keep the answer short and cite files you change."
}
```

| Field     | Type   | Required | Description           |
| --------- | ------ | -------- | --------------------- |
| `type`    | string | Yes      | Must be `user.steer`. |
| `message` | string | Yes      | Steering instruction. |

### `user.interrupt`

Interrupts the active turn.

```json theme={null}
{
  "type": "user.interrupt",
  "message": "Stop this run."
}
```

| Field     | Type   | Required | Description                 |
| --------- | ------ | -------- | --------------------------- |
| `type`    | string | Yes      | Must be `user.interrupt`.   |
| `message` | string | No       | Optional reason or UI note. |

### `user.tool_confirmation`

Allows or denies a built-in or MCP tool call that requires approval.

```json theme={null}
{
  "type": "user.tool_confirmation",
  "tool_use_id": "toolu_123",
  "result": "allow",
  "scope": "once"
}
```

| Field         | Type   | Required | Description                                                      |
| ------------- | ------ | -------- | ---------------------------------------------------------------- |
| `type`        | string | Yes      | Must be `user.tool_confirmation`.                                |
| `tool_use_id` | string | Yes      | `id` from `agent.tool_use` or `agent.mcp_tool_use`.              |
| `result`      | string | Yes      | `allow` or `deny`.                                               |
| `scope`       | string | No       | `once`, `session`, or `always`. Defaults to `once` when omitted. |

### `user.custom_tool_result`

Returns the result of an application-owned custom tool.

```json theme={null}
{
  "type": "user.custom_tool_result",
  "tool_use_id": "custom_toolu_123",
  "content": [{ "type": "text", "text": "Order #123 ships tomorrow." }],
  "is_error": false
}
```

| Field         | Type          | Required | Description                                               |
| ------------- | ------------- | -------- | --------------------------------------------------------- |
| `type`        | string        | Yes      | Must be `user.custom_tool_result`.                        |
| `tool_use_id` | string        | Yes      | `id` from `agent.custom_tool_use`.                        |
| `content`     | text block\[] | Yes      | Tool result content shown to the model.                   |
| `is_error`    | boolean       | No       | Defaults to `false`. Set to `true` when your tool failed. |

### `user.clarify_result`

Answers an agent clarification request.

```json theme={null}
{
  "type": "user.clarify_result",
  "request_id": "clarify_123",
  "answer": "Use the production environment."
}
```

| Field        | Type   | Required | Description                                |
| ------------ | ------ | -------- | ------------------------------------------ |
| `type`       | string | Yes      | Must be `user.clarify_result`.             |
| `request_id` | string | Yes      | `request_id` from `agent.clarify_request`. |
| `answer`     | string | Yes      | User answer. Empty string means skip.      |

### `user.sudo_result`

Supplies a sudo password for a blocked runtime request.

```json theme={null}
{
  "type": "user.sudo_result",
  "request_id": "sudo_123",
  "password": "correct-horse-battery-staple"
}
```

| Field        | Type   | Required | Description                         |
| ------------ | ------ | -------- | ----------------------------------- |
| `type`       | string | Yes      | Must be `user.sudo_result`.         |
| `request_id` | string | Yes      | Runtime request ID.                 |
| `password`   | string | Yes      | Password value forwarded to Hermes. |

### `user.secret_result`

Supplies a requested secret value.

```json theme={null}
{
  "type": "user.secret_result",
  "request_id": "secret_123",
  "value": "secret-value"
}
```

| Field        | Type   | Required | Description                       |
| ------------ | ------ | -------- | --------------------------------- |
| `type`       | string | Yes      | Must be `user.secret_result`.     |
| `request_id` | string | Yes      | Runtime request ID.               |
| `value`      | string | Yes      | Secret value forwarded to Hermes. |

## Session events

Session events describe turn status and session metadata changes.

### `session.status_running`

The agent turn is running.

```json theme={null}
{ "type": "session.status_running" }
```

### `session.status_idle`

The agent turn ended, errored, or paused for required action.

```json theme={null}
{
  "type": "session.status_idle",
  "stop_reason": { "type": "end_turn" }
}
```

```json theme={null}
{
  "type": "session.status_idle",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["custom_toolu_123"]
  }
}
```

| Field         | Type   | Description            |
| ------------- | ------ | ---------------------- |
| `type`        | string | `session.status_idle`. |
| `stop_reason` | object | Why the turn is idle.  |

### `session.title_updated`

Live-only event emitted when the gateway generates a session title.

```json theme={null}
{
  "type": "session.title_updated",
  "title": "Holiday campaign planning"
}
```

This event is not stored in the persisted event history.

## Agent events

Agent events are emitted by the Hermes runtime through the gateway.

### `agent.message`

Assistant response content.

```json theme={null}
{
  "type": "agent.message",
  "content": [{ "type": "text", "text": "Hello!" }],
  "delta": false
}
```

| Field     | Type          | Description                                                         |
| --------- | ------------- | ------------------------------------------------------------------- |
| `content` | text block\[] | Assistant text.                                                     |
| `delta`   | boolean       | `true` for streaming chunks, `false` for complete durable messages. |

### `agent.thinking`

Reasoning content emitted by the model or runtime.

```json theme={null}
{
  "type": "agent.thinking",
  "content": [{ "type": "text", "text": "I need to inspect the repo." }],
  "delta": true
}
```

| Field     | Type          | Description                                                           |
| --------- | ------------- | --------------------------------------------------------------------- |
| `content` | text block\[] | Reasoning text.                                                       |
| `delta`   | boolean       | `true` for streaming chunks, `false` for finalized durable reasoning. |

### `agent.tool_use`

Built-in Hermes tool call.

```json theme={null}
{
  "type": "agent.tool_use",
  "id": "toolu_123",
  "tool": "terminal",
  "input": { "command": "pwd" },
  "status": "running",
  "preview": "pwd",
  "requires_action": true
}
```

| Field             | Type    | Description                                              |
| ----------------- | ------- | -------------------------------------------------------- |
| `id`              | string  | Tool use ID.                                             |
| `tool`            | string  | Tool name.                                               |
| `input`           | object  | Tool input.                                              |
| `status`          | string  | Currently `running`.                                     |
| `preview`         | string  | Optional human-readable preview.                         |
| `requires_action` | boolean | Present when the tool requires `user.tool_confirmation`. |

### `agent.tool_result`

Built-in Hermes tool result.

```json theme={null}
{
  "type": "agent.tool_result",
  "tool_use_id": "toolu_123",
  "tool": "terminal",
  "status": "completed",
  "content": [{ "type": "text", "text": "/workspace" }],
  "is_error": false
}
```

| Field         | Type          | Description                          |
| ------------- | ------------- | ------------------------------------ |
| `tool_use_id` | string        | ID of the matching `agent.tool_use`. |
| `tool`        | string        | Tool name.                           |
| `status`      | string        | `completed` or `failed`.             |
| `content`     | text block\[] | Tool result text.                    |
| `is_error`    | boolean       | Whether the tool result is an error. |

### `agent.mcp_tool_use`

MCP tool call. Shape matches `agent.tool_use`.

```json theme={null}
{
  "type": "agent.mcp_tool_use",
  "id": "mcptoolu_123",
  "tool": "mcp_github_create_issue",
  "input": { "title": "Bug" },
  "status": "running",
  "requires_action": true
}
```

### `agent.mcp_tool_result`

MCP tool result. Shape matches `agent.tool_result`.

```json theme={null}
{
  "type": "agent.mcp_tool_result",
  "tool_use_id": "mcptoolu_123",
  "tool": "mcp_github_create_issue",
  "status": "completed",
  "content": [{ "type": "text", "text": "Created issue #42." }],
  "is_error": false
}
```

### `agent.custom_tool_use`

Application-owned custom tool request. Your application must execute the tool and
return `user.custom_tool_result`.

```json theme={null}
{
  "type": "agent.custom_tool_use",
  "id": "custom_toolu_123",
  "tool": "check_order_status",
  "input": { "order_id": "123" }
}
```

| Field   | Type   | Description                                                     |
| ------- | ------ | --------------------------------------------------------------- |
| `id`    | string | Tool use ID to return as `user.custom_tool_result.tool_use_id`. |
| `tool`  | string | Custom tool name from the agent configuration.                  |
| `input` | object | Model-supplied tool input.                                      |

### `agent.clarify_request`

Blocking clarification question from the agent.

```json theme={null}
{
  "type": "agent.clarify_request",
  "request_id": "clarify_123",
  "question": "Which environment should I deploy to?",
  "choices": ["Staging", "Production"]
}
```

| Field        | Type              | Description                                                |
| ------------ | ----------------- | ---------------------------------------------------------- |
| `request_id` | string            | ID to return as `user.clarify_result.request_id`.          |
| `question`   | string            | Question to display to the user.                           |
| `choices`    | string\[] or null | Multiple-choice options, or `null` for open-ended answers. |

### `agent.thread_message_sent`

Parent agent delegated work to a subagent or thread.

```json theme={null}
{
  "type": "agent.thread_message_sent",
  "thread_id": "thread_123",
  "content": [{ "type": "text", "text": "Research deployment options." }]
}
```

### `agent.thread_message_received`

Parent agent received a subagent or thread result.

```json theme={null}
{
  "type": "agent.thread_message_received",
  "thread_id": "thread_123",
  "content": [{ "type": "text", "text": "Use the existing deployment." }]
}
```

## Custom tool handling

Custom tools are host-executed. Herm only brokers the request and response.

1. Declare a `custom_tool` in the agent's `tools` array.
2. The model calls the tool during a turn.
3. The stream emits `agent.custom_tool_use` with the tool `id`, `tool` name, and `input`.
4. The stream emits `session.status_idle` with `stop_reason.type: "requires_action"` and the custom tool ID.
5. Your application executes the tool.
6. Your application posts `user.custom_tool_result` with `tool_use_id` equal to the `agent.custom_tool_use.id`.
7. The gateway forwards the result to Hermes and the paused turn continues streaming.

Custom tools are not governed by Herm permission policies because they do not run
inside the Hermes sandbox. Your application is responsible for authorization,
validation, idempotency, retries, and side-effect safety.
