Skip to documentation
Anywe

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
  1. Delivery ID

    X-Delivery-Id

  2. Compose

    1-20 blocks

  3. Sign

    exact bytes

  4. Send

    POST /hooks/v1/reply

  5. Accepted

    202

If you want to...Start here
Send one answerOne-shot replies
Show progress while you workStreaming replies
Attach a file, image, or videoAsset replies

Before you start

One-shot replies

  1. 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.

  2. 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 for block semantics and the reply API reference for the full request shape.

  3. 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.

  1. 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.

  2. 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.

Asset replies

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

  1. 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.

    The MIME type must be one of the types below. It is compared by the part before any ; parameter, ignoring case, so audio/webm;codecs=opus is accepted. Any other type is refused with 422 mime_type_not_allowed; image/svg+xml is not accepted because it can carry script, and application/octet-stream is not accepted because it names no type.

    KindAccepted media types
    Imagesimage/png, image/jpeg, image/gif, image/webp, image/heic, image/heif
    Audioaudio/webm, audio/ogg, audio/mp4, audio/mpeg, audio/aac, audio/wav, audio/x-m4a
    Videovideo/mp4, video/webm, video/quicktime
    Documents and data (served for download)application/pdf, text/plain, text/html, text/csv, text/markdown, application/json, application/zip, application/gzip, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint
  2. 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.

    The confirmation checks the type the upload actually stored, too. An upload sent with no Content-Type, or with a type outside the table, is refused there with the same 422 mime_type_not_allowed, and its bytes are deleted.

  3. 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