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

# Messaging

> Append user input to a session

**Steer** appends a user message to a session. Message envelopes are channel-agnostic, so API input and channel input use the same shape. Outbound delivery has its own page: [Webhooks](/agent-sessions/webhooks).

## Steer (inbound)

Send a message to a running or idle session; it wakes and continues with its prior context. Minimal form is just text:

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  import { connectSession } from "@opencomputer/sdk";

  const live = await connectSession({ sessionId, clientToken });
  await live.steer("also check the CI config", { idempotencyKey: "msg_01" });
  ```

  ```http REST API theme={null}
  POST https://api.opencomputer.dev/v3/sessions/ses_.../messages
  Authorization: Bearer $CLIENT_TOKEN
  Content-Type: application/json

  {
    "text": "also check the CI config",
    "idempotency_key": "msg_01"
  }
  ```
</CodeGroup>

Pass `idempotency_key` so retries (flaky network, double-click) are de-duplicated — applied at most once **per session**, so the same key is safe to reuse across different sessions.

## Message input

Steer with `{ text, idempotency_key? }`, or a fuller **envelope** when you need a source or a threading anchor:

```json theme={null}
{
  "envelope": {
    "id": "client:abc123",
    "source": "client",
    "conversation": { "source": "client", "ref": "thread-42" },
    "text": "also check the CI config",
    "refs": {},
    "raw": {}
  }
}
```

These fields don't all land in one place — the appended [event](/agent-sessions/events) splits them:

| You send                              | Lands on the event as                                                       |
| ------------------------------------- | --------------------------------------------------------------------------- |
| `text`                                | `body.text` — the content the agent reads                                   |
| `raw`                                 | `body.raw` — untouched original payload (optional; never interpreted)       |
| `id` (or top-level `idempotency_key`) | the idempotency key, deduped **per session**                                |
| `source`, `conversation`, `refs`      | merged into the event's top-level `refs` — opaque routing/threading anchors |
| (the sender)                          | the event's `actor` — `human` \| `agent` \| `system`, namespaced            |

So a `*.message` event's `body` is just `{ text, raw? }` — `source`/`conversation`/`refs` live under the event's top-level `refs`, and the sender under `actor` (not inside `body`). Outbound `agent.message` bodies are likewise `{ text }`.

To deliver a session's output, register a webhook destination. Deliveries carry the full [Event](/agent-sessions/events); see [Webhooks](/agent-sessions/webhooks). The same envelope also underpins [Channels](/agent-sessions/channels) in Labs.
