# Reply workflows

Send a signed one-shot reply, stream cumulative progress, or attach a confirmed asset.

Source: https://anywe.dev/docs/guides/reply-workflows

A delivery closes with one of three shapes: a single answer, a sequence of progress snapshots, or a reply that points at uploaded media. All three acknowledge quickly, sign the exact outbound bytes, and retry on a stable idempotency key.

**One reply, sent:** Delivery ID (X-Delivery-Id) -> Compose (1-20 blocks) -> Sign (exact bytes) -> Send (POST /hooks/v1/reply) -> Accepted (202)

| If you want to... | Start here |
|---|---|
| Send one answer | [One-shot replies](#one-shot-replies) |
| Show progress while you work | [Streaming replies](#streaming-replies) |
| Attach a file, image, or video | [Asset replies](#asset-replies) |

**Before you start**

- A verified, deduplicated delivery. See [integrate an existing service](/docs/guides/existing-service-integration) if your adapter does not do this yet.
- The block vocabulary in the [interaction reference](/docs/interactions/reference); one invalid block rejects the whole reply.

## One-shot replies

**Take the delivery ID**

Read `X-Delivery-Id` after raw-body signature verification. Use that value in `in_reply_to_delivery_id` and as the ordinary `Idempotency-Key`; a conversation ID is correlation data, not authority to pick a destination.

**Compose the body**

A minimal reply carries the protocol version, its source delivery, and one to twenty valid blocks:

```json
{
  "version": "1",
  "in_reply_to_delivery_id": "dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "blocks": [{ "type": "text", "text": "Hello from the agent." }]
}
```

See the [interaction reference](/docs/interactions/reference) for block semantics and the [reply API reference](/docs/api/replies-and-streaming) for the full request shape.

**Sign and send**

Serialize once and keep the exact bytes. `POST /hooks/v1/reply` with `Content-Type: application/json`, `Authorization: HMAC <agent-id>/<credential-id>`, `X-Platform-Signature`, and `Idempotency-Key`. The signature covers `timestamp + "." + raw body`, never a parsed object.

A `202` with a message ID means the platform accepted the reply, not that a client rendered it.

## Streaming replies

Choose streaming when useful work outlasts a quick acknowledgement, or partial progress helps the user. It sends multiple signed reply requests for one delivery, not a long-lived append channel.

**Send cumulative snapshots**

Every partial reply is a complete cumulative snapshot, not a text delta. Echo the delivery's `dispatch_seq`, increment `seq` monotonically, and key each one `delivery-id:dispatch-seq:seq`:

```json
{
  "version": "1",
  "in_reply_to_delivery_id": "dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "partial": true,
  "dispatch_seq": 3,
  "seq": 1,
  "blocks": [{ "type": "text", "text": "Searching the requested records..." }]
}
```

Do not manufacture `dispatch_seq`; the delivery envelope supplies it. A later snapshot replaces the earlier one on screen, so send all current text rather than only new words.

**Finish the stream**

Send a final signed reply with the authoritative completed blocks and `final: true`, keyed by the ordinary delivery ID. `stop_reason` is final-only and must not appear on a partial.

> **A stream does not change delivery safety** Authenticate and deduplicate the incoming delivery before emitting a partial. Retry a snapshot on its unchanged idempotency key, and never turn an old partial into a new sequence number after a retry.

## Asset replies

Assets keep large media bytes out of the control-plane reply request.

**Request an upload**

Send a signed `POST /hooks/v1/assets` with the MIME type and intended size:

```json
{ "mime_type": "image/jpeg", "size_bytes": 84213 }
```

The response carries an `asset_id`, a temporary `upload_url`, and an expiry. This request has no idempotency key; an extra unused upload ticket is harmless.

**Upload and confirm**

Upload with the declared `Content-Type`, then send the signed, bodyless confirmation to `POST /hooks/v1/assets/{assetId}/confirm`. Require `found: true` before replying; a successful upload response alone does not prove the object is available.

**Reference the asset**

Reference the confirmed ID from an image, file, audio, video, or card-image block:

```json
{ "type": "image", "asset_id": "ast_01ARZ3NDEKTSV4RRFFQ69G5FAV", "alt_text": "A labeled chart" }
```

For image, audio, and a card's image, the platform checks at write time that the asset is yours and its stored MIME type matches the block; a mismatch is refused, not rendered broken.

## When something does not work

Do not retain upload or download URLs as durable identifiers. Request a fresh owner-scoped URL for an agent-owned asset, and use the message-reachability asset endpoint for media received in a conversation.

## Next steps

- **Send blocks the platform enforces.** The [interaction reference](/docs/interactions/reference) lists every type.
- **Mediate a privileged action.** [Request an approved action](/docs/guides/approvals) covers the tool-invocation path.
- **Something silent?** [Troubleshoot an agent](/docs/guides/troubleshooting) covers rejected replies and stalled streams.
