# createUIMessageCodec `createUIMessageCodec()` returns the pre-built codec for the Vercel AI SDK. The returned codec implements `Codec` so a session can encode `UIMessageChunk` events out and decode them back into `UIMessage` objects without a custom implementation. You rarely call `createUIMessageCodec()` yourself. The Vercel-pre-bound [`createClientSession`](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md#create-client-session) and [`createAgentSession`](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md#create-agent-session) already supply the codec. Call it explicitly when you build a codec wrapper, run the encoder or decoder outside of a session, or compose the codec into a different codec. #### Javascript ``` import { createUIMessageCodec } from '@ably/ai-transport/vercel'; const codec = createUIMessageCodec(); const decoder = codec.createDecoder(); const projection = codec.init(); ``` ## Properties `createUIMessageCodec()` is a factory rather than a type. Each call returns a fresh, stateless codec value that satisfies the full [`Codec`](https://ably.com/docs/ai-transport/api/javascript/core/codec.md) interface for the Vercel `TInput`, `TOutput`, `TProjection`, and `TMessage` parameters listed below. Because the codec is stateless, hold a single instance at module scope (or, in React, `useMemo(() => createUIMessageCodec(), [])`) and reuse it rather than calling the factory inline on every render. | Property | Description | Type | | --- | --- | --- | | init | Build an empty `VercelProjection`. | `() => VercelProjection` | | fold | Fold a `VercelInput` or `VercelOutput` into the projection. | `(state, event, meta) => VercelProjection` | | createEncoder | Create a Vercel encoder bound to the supplied channel writer. | `(channel, options?) => Encoder` | | createDecoder | Create a Vercel decoder for the channel. | `() => Decoder` | | getMessages | Extract `{ codecMessageId, message }` pairs from a `VercelProjection`. The domain `UIMessage.id` is preserved verbatim from the source stream; the SDK keys correlation off the paired `codecMessageId`. | `(projection) => CodecMessage[]` | | createUserMessage | Wrap a `UIMessage` as the `UserMessage` input variant. | `(message: UIMessage) => VercelInput` | | createRegenerate | Build a `Regenerate` input targeting an assistant `UIMessage`. | `(target, parent) => VercelInput` | | createToolResult | Build a `ToolResult` input addressed at the assistant codec-message that contains the tool call. | `(codecMessageId, { toolCallId, output }) => VercelInput` | | createToolResultError | Build a `ToolResultError` input. | `(codecMessageId, { toolCallId, message }) => VercelInput` | | createToolApprovalResponse | Build a `ToolApprovalResponse` input. | `(codecMessageId, { toolCallId, approved, reason? }) => VercelInput` |
## Type parameters `createUIMessageCodec` is generic over the AI SDK's three `UIMessage` type parameters, as `createUIMessageCodec()`. Supplying concrete types specialises the codec so `getMessages` (and a session's `view.getMessages()`) return messages whose `metadata`, data parts, and tool parts carry your types instead of the SDK defaults; omitting them preserves the default inference. The same parameters thread through [`createClientSession`](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md#create-client-session), [`createAgentSession`](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md#create-agent-session), and [`createChatTransport`](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md#create-chat-transport), and [typed messages](https://ably.com/docs/ai-transport/frameworks/vercel-ai-sdk-ui.md#typed-messages) shows the end-to-end pattern. `VercelInput`, `VercelOutput`, and `VercelProjection` are likewise generic over the same three parameters, each defaulting to the SDK default. The tables below show the default instantiation. | Property | Description | Type | | --- | --- | --- | | TInput | Discriminated union of every record-shape a client publishes on the `ai-input` wire. Composed from the SDK's well-known input variants, with the tool variants parameterised by the Vercel domain payload shapes (`VercelToolResultPayload`, `VercelToolResultErrorPayload`, `VercelToolApprovalResponsePayload`). | `UserMessage \| Regenerate \| ToolResult \| ToolResultError \| ToolApprovalResponse` |
| Property | Description | Type | | --- | --- | --- | | TOutput | Every record-shape the agent publishes on the `ai-output` wire. The codec passes Vercel's `UIMessageChunk` through unchanged. | `AI.UIMessageChunk` |
| Property | Description | Type | | --- | --- | --- | | TProjection | Per-run projection. Carries the `{ codecMessageId, message }` pair list plus internal stream-tracker state and a high-water-mark serial for idempotency. The SDK does not inspect this shape. Use `getMessages` instead. | `{ messages: CodecMessage[], ... }` |
The well-known input variants ([`UserMessage`](https://ably.com/docs/ai-transport/api/javascript/core/codec.md#user-message), [`Regenerate`](https://ably.com/docs/ai-transport/api/javascript/core/codec.md#regenerate), [`ToolResult`](https://ably.com/docs/ai-transport/api/javascript/core/codec.md#tool-result), [`ToolResultError`](https://ably.com/docs/ai-transport/api/javascript/core/codec.md#tool-result-error), [`ToolApprovalResponse`](https://ably.com/docs/ai-transport/api/javascript/core/codec.md#tool-approval-response)) are documented on the [`Codec` reference page](https://ably.com/docs/ai-transport/api/javascript/core/codec.md#input-variants). The Vercel codec layers no extra input variants. ## Example Decode a single Ably message and fold the resulting events into a fresh projection. `ReducerMeta.serial` is required (the reducer uses it as the high-water-mark for idempotency); the `messageId` field is optional and only needed when the codec routes events to an existing message. ### Javascript ``` import { createUIMessageCodec } from '@ably/ai-transport/vercel'; const codec = createUIMessageCodec(); const decoder = codec.createDecoder(); let projection = codec.init(); channel.subscribe((message) => { if (!message.serial) return; // live channel-subscribe messages always carry one const { inputs, outputs } = decoder.decode(message); for (const input of inputs) { projection = codec.fold(projection, input, { serial: message.serial }); } for (const output of outputs) { projection = codec.fold(projection, output, { serial: message.serial }); } render(codec.getMessages(projection).map((entry) => entry.message)); }); ``` ## Related Topics - [Chat transport](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md): API reference for the AI Transport Vercel ChatTransport adapter and the Vercel-bound createClientSession and createAgentSession factories. - [Run outcome](https://ably.com/docs/ai-transport/api/javascript/vercel/run-outcome.md): API reference for vercelRunOutcome, the helper that maps a Vercel streamText finishReason and run.pipe result to a VercelRunOutcome object for run.suspend or run.end. ## Documentation Index To discover additional Ably documentation: 1. Fetch [llms.txt](https://ably.com/llms.txt) for the canonical list of available pages. 2. Identify relevant URLs from that index. 3. Fetch target pages as needed. Avoid using assumed or outdated documentation paths.