# Stored message formats
URL: /docs/cloud/formats

The message formats Assistant Cloud stores, converts, and reads for thread titles.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

Assistant Cloud stores a format name with every message. The format preserves the writer's message shape, while a message read can request a compatible AI SDK shape.

## The stored formats

| Format      | Writer                                                                    | Stored shape                                                                      |
| ----------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `aui/v0`    | The assistant-ui cloud history adapter and external-store runtime copies. | An assistant-ui thread message with `content`, attachments, status, and metadata. |
| `ai-sdk/v6` | The `aiSDKV6FormatAdapter`.                                               | An AI SDK UI message without its message `id`.                                    |
| `ai-sdk/v5` | Older AI SDK message writers.                                             | The older AI SDK message row, retained as a readable format.                      |

## The `aui/v0` shape

`aui/v0` is the complete assistant-ui message shape. It is stored with `format: "aui/v0"`.

### Message fields

| Field         | Required | Shape                                                             |
| ------------- | -------- | ----------------------------------------------------------------- |
| `id`          | No       | Local message ID. The encoder does not store it at the top level. |
| `createdAt`   | No       | ISO date string. The encoder does not store it at the top level.  |
| `role`        | Yes      | `assistant`, `user`, or `system`.                                 |
| `status`      | No       | The assistant-ui message status.                                  |
| `content`     | Yes      | Array of message parts.                                           |
| `attachments` | No       | Array of attachment wrappers.                                     |
| `metadata`    | Yes      | State, annotations, data, steps, and custom values.               |

### Message parts

| Type                                   | Fields                                                                                                                                                                                                                                                          |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text`                                 | `text`, optional `id`, `providerMetadata`, and `parentId`.                                                                                                                                                                                                      |
| `reasoning`                            | `text`, optional `id`, `unstable_summary`, `timing`, `providerMetadata`, and `parentId`.                                                                                                                                                                        |
| `source` with `sourceType: "url"`      | `id`, `url`, optional `title`, `providerMetadata`, and `parentId`.                                                                                                                                                                                              |
| `source` with `sourceType: "document"` | `id`, `title`, `mediaType`, optional `filename`, `providerMetadata`, and `parentId`.                                                                                                                                                                            |
| `tool-call`                            | `toolCallId`, `toolName`, one of `args` or `argsText`, and optional `result`, `artifact`, `modelContent`, `providerMetadata`, `isPreliminary`, `isError`, `interrupt`, `timing`, `mcp`, `approval`, `parentId`, nested `messages`, and `unstable_interactions`. |
| `image`                                | `image`, optional `id`, `filename`, and `providerMetadata`.                                                                                                                                                                                                     |
| `file`                                 | `data`, `mimeType`, optional `id`, `filename`, `sourceType`, `providerMetadata`, and `parentId`.                                                                                                                                                                |
| `data`                                 | `name`, `data`, optional `id`.                                                                                                                                                                                                                                  |
| `audio`                                | `audio: { data, format }`, where format is `mp3` or `wav`.                                                                                                                                                                                                      |
| `generative-ui`                        | `spec`, optional `id` and `parentId`.                                                                                                                                                                                                                           |

For text, reasoning, image, file, and data parts, the host-supplied `id` keys the part when it is non-empty and no other part of the same type in the message shares it, preserving component state when parts are reordered or updated with the same id. Every part with a missing or empty id, or an id shared by another part of the same type, is keyed by its type and position. The format preserves optional ids on attachment content parts too.

### Attachments and metadata

| Item                            | Fields                                                                                           |
| ------------------------------- | ------------------------------------------------------------------------------------------------ |
| Attachment wrapper              | `id`, `type`, `name`, optional `contentType`, `status`, and `content`.                           |
| Attachment `text`               | `text`, optional `id`, `providerMetadata`, and `parentId`.                                       |
| Attachment `image`              | `image`, optional `id`, `filename`, and `providerMetadata`.                                      |
| Attachment `file`               | `data`, `mimeType`, optional `id`, `filename`, `sourceType`, `providerMetadata`, and `parentId`. |
| Attachment `audio`              | `audio: { data, format }`, where format is `mp3` or `wav`.                                       |
| Attachment `data`               | `name`, `data`, optional `id`.                                                                   |
| `metadata.unstable_state`       | Optional JSON value.                                                                             |
| `metadata.unstable_annotations` | Array of JSON values.                                                                            |
| `metadata.unstable_data`        | Array of JSON values.                                                                            |
| `metadata.steps`                | Array whose entries may have `usage.inputTokens` and `usage.outputTokens`.                       |
| `metadata.custom`               | JSON object for application values.                                                              |

### Encode a message

The encoder drops the top level `id` and `createdAt`, because the server supplies those values. It rewrites a `running` status as `{ type: "incomplete", reason: "cancelled" }`.

Attachments are stored only on a `user` message and only when that message has attachments. A tool call stores `args` when `JSON.stringify(args)` equals `argsText`; otherwise it stores `argsText`. A tool result that cannot serialize as JSON is omitted, and one that loses data is stored as its JSON form. A non JSON `artifact` is dropped. A non JSON data value is written after a warning. Nested tool messages keep their own ID and ISO `createdAt`.

Runtimes that keep their transcript in their own backend, including LangGraph, LangChain, ADK, and custom external stores, copy eligible messages as `aui/v0`. Each copy has `parent_id: null`, uses the runtime message ID as `external_id`, and uses the preceding copied message ID as `parent_external_id`. Empty IDs and IDs longer than 255 characters are skipped.

AG-UI stores the final agent state in `metadata.unstable_state` and activity in data parts named `agui-activity/<type>`. AG-UI and A2A store A2UI surfaces as `present` tool calls whose `artifact.a2ui` carries the operations. A2A stores task artifacts in `metadata.custom.a2a.artifacts` without inline file bytes.

An unsupported message part throws `Message part type not supported by aui/v0: <type>`. An unsupported attachment part throws `Attachment part type not supported by aui/v0: <type>`.

### Decode a message

The decoder injects the server message ID and `created_at`, and exposes the stored `parent_id` as `parentId`. The history adapter decodes leniently. It drops unreadable parts and nested messages, then skips unreadable or orphaned rows. A nested tool message without an ID receives one synthesized from the containing message ID, tool call ID, and its indexes. A message with no status decodes as `{ type: "complete", reason: "unknown" }`.

## The `ai-sdk/v6` shape

`ai-sdk/v6` stores a UI message without its `id`. On read, the adapter adds the server message ID and the server parent ID as `parentId`.

Its telemetry reader examines assistant messages only. It opens a step for each `step-start` part, concatenates text parts into output text, and reads a tool's arguments from `input` or `args` and its result from `output` or `result`. A `tool-<name>` part is a frontend tool, while a `dynamic-tool` part with `toolName` is an MCP tool. Tool calls mark their step as `tool-calls`, and sampling calls are attached by tool call ID from `metadata.samplingCalls`. Tool artifacts, interactions, and approval responses use `metadata.__aui_toolArtifacts`, `metadata.__aui_toolInteractions`, and `metadata.__aui_toolApprovalResponses`.

Usage comes from `metadata.usage`, or from the sum of `metadata.steps[].usage` when that is absent. The reader sums usage across assistant messages and reads the model ID from message metadata. It reports `completed` when any text or tool call exists, and `incomplete` otherwise.

## The `ai-sdk/v5` shape

`ai-sdk/v5` is the older AI SDK row with `role` and `parts`. It remains readable and can be converted to `ai-sdk/v6`. Its tool parts carry `toolCallId`, `toolName`, `state`, `input`, optional `output`, and optional `errorText`.

When converted from `aui/v0`, a tool call has `output-error` when `isError` is true, `output-available` when `result` is present, and `input-available` otherwise. `isError` wins when both are present. Any `result` other than `undefined` counts as present, including `false`, `0`, an empty string and `null`. During a v5 to v6 conversion, `output-error` becomes an error result and tries to parse `errorText` as JSON.

During a server conversion from `aui/v0` to `ai-sdk/v5` or `ai-sdk/v6`, tool `artifact` and `unstable_interactions` move to the corresponding `__aui_toolArtifacts` and `__aui_toolInteractions` metadata keys. A v6 row spreads `metadata.custom` into its metadata. Data parts become `data-<name>` parts, and document sources become `source-document` parts. Generative UI becomes a `data-generative-ui` part, images and audio become `file` parts, and unknown parts become `data-<type>` parts.

## Read a compatible format

Pass `format` to `GET /v1/threads/{thread_id}/messages` or `GET /v1/projects/threads/{thread_id}/messages` to request a compatible row.

```
const { messages } = await cloud.threads.messages.list(
  "thread_0qzof3jPoDwr7K3agyJN3D4U",
  { format: "ai-sdk/v6" },
);
console.log(messages);
```

```
curl "https://backend.assistant-api.com/v1/threads/thread_0qzof3jPoDwr7K3agyJN3D4U/messages?format=ai-sdk%2Fv6" \
  -H "Authorization: Bearer $ASSISTANT_API_KEY" \
  -H "Aui-User-Id: user_123" \
  -H "Aui-Workspace-Id: workspace_123"
```

| Requested format | Stored format     | Result                         |
| ---------------- | ----------------- | ------------------------------ |
| `ai-sdk/v6`      | `aui/v0`          | Converted to `ai-sdk/v6`.      |
| `ai-sdk/v6`      | `ai-sdk/v5`       | Converted to `ai-sdk/v6`.      |
| `ai-sdk/v5`      | `aui/v0`          | Converted to `ai-sdk/v5`.      |
| Any other pair   | Any stored format | Returned in its stored format. |

If a stored row cannot be parsed or converted, the list skips that row and logs a warning. It does not fail the whole response.

## Extract text for a thread title

Thread title generation reads only messages whose `role` is `user` or `assistant`. A user message with no extracted text is omitted; an assistant message with no extracted text remains as an empty message. The title model then uses the first user and first assistant message, and only their text.

| Format      | Text source                                            |
| ----------- | ------------------------------------------------------ |
| `aui/v0`    | `content` parts whose type is `text`, joined in order. |
| `ai-sdk/v5` | `parts` whose type is `text`, joined in order.         |
| `ai-sdk/v6` | `parts` whose type is `text`, joined in order.         |