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

# Sending input

> Deliver messages to a running agent

Every message or control action you send to an agent starts as an **agent input** (`ain_…`). Omnara stores the input first, then adds it to the timeline as a durable `agent_input` event when the agent processes it. This lets a busy agent accept new messages without interrupting its current work.

There are four input kinds. You only create the first directly; the others are produced by their own endpoints:

| `input_kind` | Created by |
| - | - |
| `content` | `POST /inputs` — the messages this page is about |
| `interaction_response` | [Resolving an interaction](/events/interactions) |
| `control` | [Stop current work](/agents/overview#stop-current-work) |
| `config_change` | [Changing the config mid-conversation](#change-the-config-mid-conversation) |

```bash theme={null}
export BASE="$OMNARA_API/orgs/$ORG/projects/$PROJ"
export AGENT="agt_v4qtrwz3jehcyd5n6a2bfgik7m"
```

## Send a message

An agent accepts up to 50 pending messages across queued and steering inputs; further submissions return `409` with code `agent_backlog_full` until messages are processed or canceled, while child lifecycle reports are always delivered.

**API**

The examples assume the client, `orgID`, and `projectID` setup from the [quickstart](/quickstart), with the `$AGENT` ID above available as `agentID` for the SDK. The CLI has no command for sending input — use the REST API or the SDK.

```bash REST theme={null}
curl "$BASE/agents/$AGENT/inputs" \
  -H "Authorization: Bearer $OMNARA_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-8f2c1a" \
  -d '{
    "content_blocks": [
      { "type": "text", "text": "Also check whether the deadline moved." }
    ]
  }'
```

```typescript SDK theme={null}
const input = await sdk.createAgentInput({
  client,
  path: { orgID, projectID, agentID },
  headers: { 'Idempotency-Key': 'reply-8f2c1a' },
  body: {
    content_blocks: [
      { type: 'text', text: 'Also check whether the deadline moved.' },
    ],
  },
})
console.log(input.data.agent_input)
```

```json theme={null}
{
  "agent_input": {
    "id": "ain_2bfgik7mv4qtrwz3jehcyd5n6a",
    "state": "received",
    "delivery_mode": "queued",
    "...": "..."
  }
}
```

`state: received` means Omnara accepted the input. When the agent processes it, the state becomes `resolved` and a matching event appears on the timeline. An input can instead become `canceled` if it is withdrawn before the agent processes it.

If your system retries requests, send an `Idempotency-Key` to avoid delivering the same message twice.

> [!NOTE]
>
> Full schema and playground: [Create agent input](/api-reference/endpoints/agents/create-agent-input).

**Dashboard**

Open the agent, type a message in the composer, and click **Send**.

## Attach files and images

An input can include files alongside text. Inline files are base64-encoded, with limits of 10 MiB per file, 24 MiB combined, and 20 files per input:

```bash REST theme={null}
jq -n --arg data "$(base64 < chart.png)" '{
  content_blocks: [
    { type: "text", text: "Does this chart match the numbers you found?" },
    { type: "media", media_type: "image/png", filename: "chart.png", data: $data }
  ]
}' |
curl "$BASE/agents/$AGENT/inputs" \
  -H "Authorization: Bearer $OMNARA_TOKEN" \
  -H "Content-Type: application/json" \
  -d @-
```

```typescript SDK theme={null}
import { readFile } from 'node:fs/promises'

const chart = await readFile('chart.png')

await sdk.createAgentInput({
  client,
  path: { orgID, projectID, agentID },
  body: {
    content_blocks: [
      { type: 'text', text: 'Does this chart match the numbers you found?' },
      {
        type: 'media',
        media_type: 'image/png',
        filename: 'chart.png',
        data: chart.toString('base64'),
      },
    ],
  },
})
```

Omnara accepts common images, PDFs, text files, spreadsheets, and Office documents. It stores each file as an [artifact](/events/artifacts) and replaces the inline data with a `media_ref` in the timeline.

## Queued vs steering

`delivery_mode` decides when a busy agent receives a message:

* **`queued`** (default) waits until the agent is ready to start new work. Use it for a new task or follow-up.
* **`steering`** is delivered at the next model call, ahead of queued messages, and starts a new [turn](/events/streaming#group-events-into-turns). Use it for an urgent correction such as “stop using the staging database.”

The other input kinds — interaction responses, cancels, config changes — never wait in the queue: they're delivered with a third mode, `immediate`, that you can't choose. Only `content` inputs take `queued` or `steering`.

```bash REST theme={null}
curl "$BASE/agents/$AGENT/inputs" \
  -H "Authorization: Bearer $OMNARA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "delivery_mode": "steering",
    "cancel_open_interactions": true,
    "content_blocks": [ { "type": "text", "text": "Ignore the 2024 draft — it was superseded." } ]
  }'
```

```typescript SDK theme={null}
await sdk.createAgentInput({
  client,
  path: { orgID, projectID, agentID },
  body: {
    delivery_mode: 'steering',
    cancel_open_interactions: true,
    content_blocks: [
      { type: 'text', text: 'Ignore the 2024 draft — it was superseded.' },
    ],
  },
})
```

If the agent is idle, both modes deliver immediately; the difference only exists under load.

For a steering message, set `cancel_open_interactions: true` when the correction makes an open approval or question irrelevant. Omnara cancels those interactions before delivering the message. This field is only valid with `delivery_mode: "steering"`.

## Inspect and manage the queue

The dashboard shows waiting inputs above the composer. Through the API, you can also list waiting inputs, change their order or delivery mode, or cancel them.

### List waiting inputs

```bash REST theme={null}
curl "$BASE/agents/$AGENT/inputs/backlog" \
  -H "Authorization: Bearer $OMNARA_TOKEN"
```

```typescript SDK theme={null}
const backlog = await sdk.listQueuedBacklogInputs({
  client,
  path: { orgID, projectID, agentID },
})
console.log(backlog.data)
```

```json theme={null}
{
  "data": [
    { "id": "ain_2bfgik7mv4qtrwz3jehcyd5n6a", "state": "received", "delivery_mode": "queued", "...": "..." },
    { "id": "ain_4qtrwz3jehcyd5n6a2bfgik7mv", "state": "received", "delivery_mode": "queued", "...": "..." }
  ],
  "next_cursor": null
}
```

Similarly, you can cancel a queued input, change its position, or switch it between queued and steering delivery. Promoting is idempotent: it also returns `200` if the input is already steering or the agent has already taken it. Other invalid state transitions return `409`.

> [!NOTE]
>
> Full schemas and playground: [List waiting backlog inputs](/api-reference/endpoints/agents/list-waiting-backlog-inputs) · [Cancel](/api-reference/endpoints/agents/cancel-queued-backlog-input) · [Move](/api-reference/endpoints/agents/move-queued-backlog-input) · [Promote](/api-reference/endpoints/agents/promote-queued-input-to-steering) · [Demote](/api-reference/endpoints/agents/demote-steering-input-to-queued).

## Change the config mid-conversation

You can change which [config](/agents/configuration) an agent uses without restarting its conversation. Send the new config source to switch models, tools, permissions, or instructions. Omnara validates the config and records the change on the timeline. This is not available in the dashboard yet.

```bash CLI theme={null}
npx omnara agents update {agent-id} --file agent-v2.yaml
```

```bash REST theme={null}
jq -n --rawfile src agent-v2.yaml '{source: $src, source_format: "yaml"}' |
curl "$BASE/agents/$AGENT/config" \
  -H "Authorization: Bearer $OMNARA_TOKEN" \
  -H "Content-Type: application/json" \
  -d @-
```

```typescript SDK theme={null}
import { readFile } from 'node:fs/promises'

const source = await readFile('agent-v2.yaml', 'utf8')

await sdk.updateAgentConfig({
  client,
  path: { orgID, projectID, agentID },
  body: { source, source_format: 'yaml' },
})
```

The change takes effect at the next model call. The dashboard marks it in the conversation with an **Agent configuration changed** divider.

> [!NOTE]
>
> Full schema and playground: [Update agent config](/api-reference/endpoints/agents/update-agent-config).

## Who said that? Actors and attribution

Every input is attributed to whoever authenticated the request. When a person calls the API with their own token, that's exactly what you want. But often the people talking to your agent aren't Omnara users at all — they're your customers, typing into your chat frontend or ticketing system. Your backend relays their messages with its own token, so without help the timeline would credit every message to that one backend identity.

An **actor** solves this. It's not another kind of account — it's a lightweight identity record for an external user, and passing `actor` on an input names who actually spoke:

```bash REST theme={null}
curl "$BASE/agents/$AGENT/inputs" \
  -H "Authorization: Bearer $OMNARA_SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content_blocks": [ { "type": "text", "text": "Customer says the export is still failing." } ],
    "actor": {
      "provider_tenant_id": "acme-support",
      "provider_user_id": "user-3841",
      "display_name": "Dana R."
    }
  }'
```

```typescript SDK theme={null}
await sdk.createAgentInput({
  client,
  path: { orgID, projectID, agentID },
  body: {
    content_blocks: [
      { type: 'text', text: 'Customer says the export is still failing.' },
    ],
    actor: {
      provider_tenant_id: 'acme-support',
      provider_user_id: 'user-3841',
      display_name: 'Dana R.',
    },
  },
})
```

Omnara reuses an actor when its `provider_tenant_id` and `provider_user_id` match an existing record. You can also pass an actor when [resolving an interaction](/events/interactions) or [stopping current work](/agents/overview#stop-current-work).

Hosted integrations record senders with `provider: "integration"`, a stable platform
namespace as `provider_tenant_id`, and the sender’s platform user ID as
`provider_user_id`. Slack uses `slack:<workspace ID>`, Discord uses `discord`, and
GitHub uses `github:github.com`. Within a project, the same person shares an actor
across configured bots and integration kinds on that platform. Projects, Slack
workspaces, and platforms keep separate identities. Actors are attribution records,
not permissions. Their saved `metadata.source_label` identifies the platform in the
timeline even after an integration is deleted.

Actors belong to a project and can be listed, fetched, or created through the API. Omit `actor` when the request is authenticated as an Omnara user; their account already provides the attribution.

> [!NOTE]
>
> Full schemas and playground: [List project actors](/api-reference/endpoints/actors/list-project-actors) · [Upsert an external actor](/api-reference/endpoints/actors/upsert-an-external-actor) · [Get a project actor](/api-reference/endpoints/actors/get-a-project-actor).

## Inputs from your own app

Your service chooses which agent receives each provider event and submits an
ordinary input. Pass an `actor` to attribute the message, and use a stable
`Idempotency-Key` for retries. Your service owns the conversation-to-agent mapping;
Omnara does not need an integration registration. See [Custom integrations](/integrations/custom-integrations)
for the complete input, custom-tool and interaction flow.

## Next

> **[Streaming events](/events/streaming)**
>
> Watch your input get admitted and answered, live

> **[Approvals & questions](/events/interactions)**
>
> Resolve the prompts that pause an agent


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.