> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.astropods.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.astropods.com/_mcp/server.

# Sending a response

Every reply your agent sends is an `AgentResponse` carrying exactly one payload variant.

#### Node

**`AgentResponse`**

| Field            | Type           | Required | Notes                                                                                             |
| ---------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `conversationId` | string         | yes      | Must match the inbound `Message.conversationId`.                                                  |
| `responseId`     | string         | no       | Stable ID for this response. Used by feedback events.                                             |
| `traceContext`   | `TraceContext` | no       | W3C trace context for this assistant response. See [Trace context](/messaging-sdk/trace-context). |
| *one variant*    |                | yes      | One of the fields below; @grpc/proto-loader flattens the oneof.                                   |

**`AgentResponse` payload variants**

| Variant           | Type                   | Notes                                            |
| ----------------- | ---------------------- | ------------------------------------------------ |
| `incomingMessage` | `Message`              | Server → agent only. The inbound message itself. |
| `status`          | `StatusUpdate`         | Pre-content typing indicator.                    |
| `content`         | `ContentChunk`         | Actual message text, streamed.                   |
| `prompts`         | `SuggestedPrompts`     | Quick-reply suggestions.                         |
| `threadMetadata`  | `ThreadMetadata`       | Open a thread or update its title.               |
| `transcript`      | `Transcript`           | STT result back to the platform (audio flow).    |
| `error`           | `ErrorResponse`        | Surface an error to the user.                    |
| `contextRequest`  | `ThreadHistoryRequest` | Ask the sidecar to hydrate thread history.       |
| `audioConfig`     | `AudioStreamConfig`    | Server → agent only. Audio session start.        |
| `audioChunk`      | `AudioChunk`           | Server → agent only. Audio bytes.                |
| `feedback`        | `PlatformFeedback`     | Server → agent only. User feedback event.        |

#### Python

**`AgentResponse`**

| Field             | Type           | Required | Notes                                                                                             |
| ----------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `conversation_id` | string         | yes      | Must match the inbound `Message.conversation_id`.                                                 |
| `response_id`     | string         | no       | Stable ID for this response. Used by feedback events.                                             |
| `trace_context`   | `TraceContext` | no       | W3C trace context for this assistant response. See [Trace context](/messaging-sdk/trace-context). |
| *one variant*     |                | yes      | Exactly one payload variant below.                                                                |

**`AgentResponse` payload variants** (`oneof payload`)

| Variant            | Type                   | Notes                                            |
| ------------------ | ---------------------- | ------------------------------------------------ |
| `incoming_message` | `Message`              | Server → agent only. The inbound message itself. |
| `status`           | `StatusUpdate`         | Pre-content typing indicator.                    |
| `content`          | `ContentChunk`         | Actual message text, streamed.                   |
| `prompts`          | `SuggestedPrompts`     | Quick-reply suggestions.                         |
| `thread_metadata`  | `ThreadMetadata`       | Open a thread or update its title.               |
| `transcript`       | `Transcript`           | STT result back to the platform (audio flow).    |
| `error`            | `ErrorResponse`        | Surface an error to the user.                    |
| `context_request`  | `ThreadHistoryRequest` | Ask the sidecar to hydrate thread history.       |
| `audio_config`     | `AudioStreamConfig`    | Server → agent only.                             |
| `audio_chunk`      | `AudioChunk`           | Server → agent only.                             |
| `feedback`         | `PlatformFeedback`     | Server → agent only.                             |

## Streaming text content

#### Node

**`ContentChunk`**

| Field               | Type                   | Required | Notes                                                                     |
| ------------------- | ---------------------- | -------- | ------------------------------------------------------------------------- |
| `type`              | string                 | yes      | `START` \| `DELTA` \| `END` \| `REPLACE` (see lifecycle).                 |
| `content`           | string                 | no       | Semantics depend on `type`.                                               |
| `attachments`       | `ResponseAttachment[]` | no       | Ship with `END` chunks (or standalone).                                   |
| `platformMessageId` | string                 | no       | Returned by the adapter after `START`; pass on later chunks to update it. |
| `options`           | `MessageOptions`       | no       | Creation flags.                                                           |

**`ContentChunk.type` lifecycle**

| Value     | Use                                                                                        |
| --------- | ------------------------------------------------------------------------------------------ |
| `START`   | Create the platform message. May be empty (immediate presence) or include initial content. |
| `DELTA`   | Append the next token(s). Stream as many as you want.                                      |
| `END`     | Finalize. Last content (optional) and any `attachments` ship here.                         |
| `REPLACE` | Overwrite the full message content, for post-stream edits.                                 |

**`MessageOptions`**

| Field              | Type    | Required | Notes                                        |
| ------------------ | ------- | -------- | -------------------------------------------- |
| `ephemeral`        | boolean | no       | Only visible to the recipient user.          |
| `createThread`     | boolean | no       | Start a new thread under the user's message. |
| `replyToMessageId` | string  | no       | Reply to a specific message.                 |
| `silent`           | boolean | no       | Suppress notification.                       |

**`ResponseAttachment`** (set exactly one variant)

| Variant | Type              | Fields                                                                                                                                                        |
| ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image` | `ImageAttachment` | `url`, `altText?`, `title?`, `width?`, `height?`                                                                                                              |
| `file`  | `FileAttachment`  | `url?`, `filename`, `mimeType?`, `sizeBytes?`. For a filesystem output, write `filename` inside `AGENT_FILES_DIR` before sending `END` and leave `url` empty. |
| `card`  | `CardAttachment`  | `platformCardJson` — Slack Block Kit, Discord Embeds, Teams cards.                                                                                            |
| `link`  | `LinkPreview`     | `url`, `title?`, `description?`, `imageUrl?`                                                                                                                  |

```typescript
conversation.sendContentChunk(cid, { type: 'START', content: '' });
for await (const token of llm.stream(prompt)) {
  conversation.sendContentChunk(cid, { type: 'DELTA', content: token });
}
conversation.sendContentChunk(cid, { type: 'END', content: '' });
```

#### Python

**`ContentChunk`**

| Field                 | Type                          | Required | Notes                                                                     |
| --------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------- |
| `type`                | enum                          | yes      | See `ContentChunk.ChunkType` below.                                       |
| `content`             | string                        | no       | Semantics depend on `type`.                                               |
| `attachments`         | `repeated ResponseAttachment` | no       | Ship with `END` chunks (or standalone).                                   |
| `platform_message_id` | string                        | no       | Returned by the adapter after `START`; pass on later chunks to update it. |
| `options`             | `MessageOptions`              | no       | Creation flags.                                                           |

**`ContentChunk.ChunkType`** (access via `ContentChunk.START` etc.)

| Value         | Use                                                                                        |
| ------------- | ------------------------------------------------------------------------------------------ |
| `START` (1)   | Create the platform message. May be empty (immediate presence) or include initial content. |
| `DELTA` (2)   | Append the next token(s). Stream as many as you want.                                      |
| `END` (3)     | Finalize. Last content (optional) and any `attachments` ship here.                         |
| `REPLACE` (4) | Overwrite the full content, for post-stream edits.                                         |

**`MessageOptions`**

| Field                 | Type   | Required | Notes                                        |
| --------------------- | ------ | -------- | -------------------------------------------- |
| `ephemeral`           | bool   | no       | Only visible to the recipient user.          |
| `create_thread`       | bool   | no       | Start a new thread under the user's message. |
| `reply_to_message_id` | string | no       | Reply to a specific message.                 |
| `silent`              | bool   | no       | Suppress notification.                       |

**`ResponseAttachment`** (`oneof attachment_type`)

| Variant | Type              | Fields                                                                                                                                                       |
| ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `image` | `ImageAttachment` | `url`, `alt_text`, `title`, `width`, `height`                                                                                                                |
| `file`  | `FileAttachment`  | `url`, `filename`, `mime_type`, `size_bytes`. For a filesystem output, write `filename` inside `AGENT_FILES_DIR` before sending `END` and leave `url` empty. |
| `card`  | `CardAttachment`  | `platform_card_json` — Slack Block Kit, Discord Embeds, Teams cards                                                                                          |
| `link`  | `LinkPreview`     | `url`, `title`, `description`, `image_url`                                                                                                                   |

```python
def send(payload):
    outbound.put(ConversationRequest(agent_response=AgentResponse(
        conversation_id=cid,
        content=payload,
    )))

send(ContentChunk(type=ContentChunk.START, content=""))
for token in stream_llm(prompt):
    send(ContentChunk(type=ContentChunk.DELTA, content=token))
send(ContentChunk(type=ContentChunk.END, content=""))
```

## Status updates

#### Node

**`StatusUpdate`**

| Field           | Type   | Required | Notes                                                             |
| --------------- | ------ | -------- | ----------------------------------------------------------------- |
| `status`        | string | yes      | One of the enum values below.                                     |
| `customMessage` | string | no       | Required with `CUSTOM`; otherwise overrides the default phrasing. |
| `emoji`         | string | no       | Platform emoji, e.g. `:mag:`.                                     |

**`StatusUpdate.status` values**: `THINKING`, `SEARCHING`, `GENERATING`, `PROCESSING`, `ANALYZING`, `CUSTOM`.

```typescript
conversation.sendStatusUpdate(cid, { status: 'SEARCHING' });
conversation.sendStatusUpdate(cid, {
  status: 'CUSTOM',
  customMessage: 'Querying the knowledge base…',
  emoji: ':mag:',
});
```

#### Python

**`StatusUpdate`**

| Field            | Type   | Required | Notes                                                         |
| ---------------- | ------ | -------- | ------------------------------------------------------------- |
| `status`         | enum   | yes      | See `StatusUpdate.Status` below.                              |
| `custom_message` | string | no       | Required with `CUSTOM`; otherwise overrides default phrasing. |
| `emoji`          | string | no       | Platform emoji, e.g. `:mag:`.                                 |

**`StatusUpdate.Status`** (access via `StatusUpdate.THINKING` etc.)

| Value                    | Meaning                     |
| ------------------------ | --------------------------- |
| `STATUS_UNSPECIFIED` (0) | Do not use.                 |
| `THINKING` (1)           | Generic "thinking".         |
| `SEARCHING` (2)          | RAG/knowledge base search.  |
| `GENERATING` (3)         | LLM generation in progress. |
| `PROCESSING` (4)         | Tool execution.             |
| `ANALYZING` (5)          | Data analysis.              |
| `CUSTOM` (10)            | Use with `custom_message`.  |

```python
from astropods_messaging import StatusUpdate

send_response(AgentResponse(
    conversation_id=cid,
    status=StatusUpdate(status=StatusUpdate.SEARCHING),
))
send_response(AgentResponse(
    conversation_id=cid,
    status=StatusUpdate(
        status=StatusUpdate.CUSTOM,
        custom_message="Querying the knowledge base…",
        emoji=":mag:",
    ),
))
```

## Suggested prompts

#### Node

**`SuggestedPrompts`**

| Field     | Type       | Required | Notes                          |
| --------- | ---------- | -------- | ------------------------------ |
| `prompts` | `Prompt[]` | yes      | Max 4–6 depending on platform. |

**`Prompt`**

| Field         | Type   | Required | Notes                                                         |
| ------------- | ------ | -------- | ------------------------------------------------------------- |
| `id`          | string | yes      | Unique ID. Echoed back in `PlatformFeedback.promptSelection`. |
| `title`       | string | yes      | Button/chip label.                                            |
| `message`     | string | yes      | Full message sent on click.                                   |
| `description` | string | no       | Tooltip/help text.                                            |

#### Python

**`SuggestedPrompts`**

| Field     | Type                               | Required | Notes                          |
| --------- | ---------------------------------- | -------- | ------------------------------ |
| `prompts` | `repeated SuggestedPrompts.Prompt` | yes      | Max 4–6 depending on platform. |

**`SuggestedPrompts.Prompt`**

| Field         | Type   | Required | Notes                                                          |
| ------------- | ------ | -------- | -------------------------------------------------------------- |
| `id`          | string | yes      | Unique ID. Echoed back in `PlatformFeedback.prompt_selection`. |
| `title`       | string | yes      | Button/chip label.                                             |
| `message`     | string | yes      | Full message sent on click.                                    |
| `description` | string | no       | Tooltip/help text.                                             |

## Errors

#### Node

**`ErrorResponse`**

| Field       | Type    | Required | Notes                                                 |
| ----------- | ------- | -------- | ----------------------------------------------------- |
| `code`      | string  | yes      | One of the enum values below.                         |
| `message`   | string  | yes      | User-facing error message.                            |
| `details`   | string  | no       | Technical details. Logged, not shown to the user.     |
| `retryable` | boolean | no       | Whether the platform should offer a retry affordance. |

**`ErrorResponse.code` values**: `RATE_LIMIT`, `CONTEXT_TOO_LONG`, `INVALID_REQUEST`, `AGENT_ERROR`, `TOOL_ERROR`, `PLATFORM_ERROR`.

#### Python

**`ErrorResponse`**

| Field       | Type   | Required | Notes                                                 |
| ----------- | ------ | -------- | ----------------------------------------------------- |
| `code`      | enum   | yes      | See `ErrorResponse.ErrorCode` below.                  |
| `message`   | string | yes      | User-facing error message.                            |
| `details`   | string | no       | Technical details. Logged, not shown to the user.     |
| `retryable` | bool   | no       | Whether the platform should offer a retry affordance. |

**`ErrorResponse.ErrorCode`**

| Value                        | Meaning                    |
| ---------------------------- | -------------------------- |
| `ERROR_CODE_UNSPECIFIED` (0) | Fallback. Avoid.           |
| `RATE_LIMIT` (1)             | Agent hit rate limit.      |
| `CONTEXT_TOO_LONG` (2)       | Context exceeds LLM limit. |
| `INVALID_REQUEST` (3)        | Malformed request.         |
| `AGENT_ERROR` (4)            | Internal agent error.      |
| `TOOL_ERROR` (5)             | Tool execution failed.     |
| `PLATFORM_ERROR` (6)         | Platform API error.        |

## Thread metadata

#### Node

**`ThreadMetadata`**

| Field       | Type    | Required | Notes                                                 |
| ----------- | ------- | -------- | ----------------------------------------------------- |
| `threadId`  | string  | no       | Platform thread ID. Set to update an existing thread. |
| `title`     | string  | no       | Thread title/subject.                                 |
| `createNew` | boolean | no       | Create a new thread.                                  |

#### Python

**`ThreadMetadata`**

| Field        | Type   | Required | Notes                                                 |
| ------------ | ------ | -------- | ----------------------------------------------------- |
| `thread_id`  | string | no       | Platform thread ID. Set to update an existing thread. |
| `title`      | string | no       | Thread title/subject.                                 |
| `create_new` | bool   | no       | Create a new thread.                                  |

## Transcript

Sent after STT to replace the "\[audio]" placeholder on the platform.

#### Node

**`Transcript`**

| Field       | Type   | Required | Notes                                    |
| ----------- | ------ | -------- | ---------------------------------------- |
| `text`      | string | yes      | Transcribed text.                        |
| `messageId` | string | no       | Placeholder message ID to update.        |
| `language`  | string | no       | BCP-47 detected language (e.g. `en-US`). |

#### Python

**`Transcript`**

| Field        | Type   | Required | Notes                                    |
| ------------ | ------ | -------- | ---------------------------------------- |
| `text`       | string | yes      | Transcribed text.                        |
| `message_id` | string | no       | Placeholder message ID to update.        |
| `language`   | string | no       | BCP-47 detected language (e.g. `en-US`). |

## Next steps

* [Trace context](/messaging-sdk/trace-context): correlate a response back to its trace
* [Audio](/messaging-sdk/audio): the STT flow that produces a `Transcript`
* [Worked examples](/messaging-sdk/examples): these pieces assembled into an agent