Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/a2ui-project/a2ui/llms.txt

Use this file to discover all available pages before exploring further.

A2UI communicates through a small set of typed JSON messages sent as JSON Lines (JSONL), where each newline-delimited object is exactly one message. Agents stream these messages to clients over any supported transport. The v0.9.1 protocol adds a top-level "version" field to every message and flattens the component object format; v0.8 messages use a nested wrapper style and end with a beginRendering signal.
All v0.9.1 messages include "version": "v0.9.1" as their first field. The four core message types are:
MessagePurpose
createSurfaceInitialize a surface and declare its catalog
updateComponentsAdd or replace components in flat adjacency-list format
updateDataModelWrite a value to a JSON Pointer path in the data model
deleteSurfaceTear down a surface and free all associated state

createSurface / beginRendering

Initializes a new rendering surface and declares which component catalog the agent will use for the lifetime of that surface.

TypeScript Schema

{
  version: "v0.9.1";
  createSurface: {
    surfaceId: string;       // Required: unique surface identifier
    catalogId: string;       // Required: URL of the component catalog
    theme?: object;          // Optional: theme configuration
    sendDataModel?: boolean; // Optional: request client to echo data model changes
  }
}

Properties

version
string
required
Must be "v0.9.1". Identifies the protocol version for this message.
createSurface.surfaceId
string
required
A unique identifier for this surface. Must not conflict with any currently active surface in the renderer.
createSurface.catalogId
string
required
URI of the catalog JSON Schema that defines the components the agent will use. The client must have this catalog available at render time — no runtime download occurs.
createSurface.theme
object
Optional theme configuration (e.g., primaryColor, fontFamily) as defined by the catalog’s theme schema.
createSurface.sendDataModel
boolean
If true, the client sends data model change events back to the agent whenever user input updates the model.

JSON Example

{
  "version": "v0.9.1",
  "createSurface": {
    "surfaceId": "booking",
    "catalogId": "https://a2ui.org/specification/v0_9_1/catalogs/basic/catalog.json"
  }
}
In v0.9.1 the root component is identified by convention: one component in the subsequent updateComponents message must carry "id": "root". There is no separate root field in createSurface.

updateComponents / surfaceUpdate

Adds or replaces components within a surface. Re-sending a component with an existing id is an update, not a duplicate.

TypeScript Schema

{
  version: "v0.9.1";
  updateComponents: {
    surfaceId: string;
    components: Array<{
      id: string;         // Required: unique component ID within the surface
      component: string;  // Required: component type name, e.g. "Text"
      [key: string]: any; // Component-specific properties are flat on this object
    }>;
  }
}

Properties

updateComponents.surfaceId
string
required
The surfaceId of the surface to update. Must have been created with createSurface.
updateComponents.components
array
required
Array of component definition objects. Each entry carries id, component, and all component-specific properties at the top level (flat format).
updateComponents.components[].id
string
required
Unique identifier for this component within the surface. Sending a duplicate ID overwrites the existing component.
updateComponents.components[].component
string
required
The component type name as defined in the negotiated catalog (e.g., "Text", "Button", "Column").

JSON Example

{
  "version": "v0.9.1",
  "updateComponents": {
    "surfaceId": "booking",
    "components": [
      {
        "id": "root",
        "component": "Column",
        "children": ["header", "guests-field", "submit-btn"]
      },
      {
        "id": "header",
        "component": "Text",
        "text": "Confirm Reservation",
        "variant": "h1"
      },
      {
        "id": "guests-field",
        "component": "TextField",
        "label": "Number of Guests",
        "value": { "path": "/reservation/guests" },
        "textFieldType": "number"
      },
      {
        "id": "submit-btn",
        "component": "Button",
        "child": "submit-label",
        "variant": "primary",
        "action": { "event": { "name": "confirm" } }
      },
      {
        "id": "submit-label",
        "component": "Text",
        "text": "Book Table"
      }
    ]
  }
}

Common Errors

ErrorCauseSolution
Surface already existssurfaceId is already activeUse a globally unique ID; an orchestrator should manage surface IDs across subagents
Surface not foundsurfaceId does not match a created surfaceEnsure createSurface was sent first (v0.9.1) or that the ID matches an implicit v0.8 surface
Invalid component typeUnknown component name for the negotiated catalogVerify the type name against the catalog schema
Invalid propertyProperty not declared for this component typeCheck the catalog definition
Circular referenceA component references itself as a childRestructure the component hierarchy

updateDataModel / dataModelUpdate

Updates the per-surface data model that components bind to via JSON Pointer paths. Any component whose bound path is affected automatically re-renders.

TypeScript Schema

{
  version: "v0.9.1";
  updateDataModel: {
    surfaceId: string;  // Required: target surface
    path?: string;      // Optional: JSON Pointer path; defaults to "/"
    value?: any;        // Optional: value to set; omit to delete the key
  }
}

Properties

updateDataModel.surfaceId
string
required
ID of the surface whose data model should be updated.
updateDataModel.path
string
A RFC 6901 JSON Pointer path such as /user/email or /cart/items/0. Defaults to "/" (root) if omitted.
updateDataModel.value
any
The value to write at path. Can be any JSON type — string, number, boolean, object, array, or null. Omit the field entirely to delete the key at path.

JSON Examples

Initialize the entire model at root:
{
  "version": "v0.9.1",
  "updateDataModel": {
    "surfaceId": "booking",
    "path": "/",
    "value": {
      "reservation": {
        "datetime": "2025-12-16T19:00:00Z",
        "guests": "2"
      }
    }
  }
}
Update a single nested field:
{
  "version": "v0.9.1",
  "updateDataModel": {
    "surfaceId": "booking",
    "path": "/reservation/guests",
    "value": "3"
  }
}

deleteSurface

Removes a surface and frees all associated component definitions and data model state. Safe to call on a surface that no longer exists (no-op).

TypeScript Schema

{
  version: "v0.9.1";
  deleteSurface: {
    surfaceId: string;
  }
}

JSON Example

{
  "version": "v0.9.1",
  "deleteSurface": {
    "surfaceId": "booking"
  }
}

Properties

deleteSurface.surfaceId
string
required
The ID of the surface to remove. The client should tear down the rendered UI and release all state for this surface.
Send deleteSurface when closing modals, navigating away from a view, or completing a task. The identical inner structure works in both v0.8 and v0.9.1 — the only difference is the top-level version field.

Message Ordering

Messages for different surfaces are fully independent and can be interleaved freely. Within a single surface, ordering rules apply.

JSONL Streaming Example

The following sequence builds a complete booking surface from scratch, then tears it down after confirmation. Each line is sent as soon as it is generated — the client renders progressively.
{ "version": "v0.9.1", "createSurface": { "surfaceId": "booking", "catalogId": "https://a2ui.org/specification/v0_9_1/catalogs/basic/catalog.json" } }
{ "version": "v0.9.1", "updateComponents": { "surfaceId": "booking", "components": [{ "id": "root", "component": "Column", "children": ["header", "date-field", "guests-field", "submit-btn"] }] } }
{ "version": "v0.9.1", "updateComponents": { "surfaceId": "booking", "components": [{ "id": "header", "component": "Text", "text": "Book a Table", "variant": "h1" }, { "id": "date-field", "component": "DateTimeInput", "value": { "path": "/reservation/datetime" }, "enableDate": true, "enableTime": true }] } }
{ "version": "v0.9.1", "updateComponents": { "surfaceId": "booking", "components": [{ "id": "guests-field", "component": "TextField", "label": "Guests", "value": { "path": "/reservation/guests" }, "textFieldType": "number" }, { "id": "submit-btn", "component": "Button", "child": "submit-label", "variant": "primary", "action": { "event": { "name": "confirm" } } }, { "id": "submit-label", "component": "Text", "text": "Confirm" }] } }
{ "version": "v0.9.1", "updateDataModel": { "surfaceId": "booking", "path": "/reservation", "value": { "datetime": "2025-12-16T19:00:00Z", "guests": "2" } } }
{ "version": "v0.9.1", "deleteSurface": { "surfaceId": "booking" } }
Components can be batched across multiple updateComponents messages. The flat adjacency-list format means the client can render each batch immediately without waiting for the full tree.

Component Reference

All available component types, properties, and version differences.

Data Binding

How JSON Pointer paths connect components to application state.

Catalogs

Catalog negotiation, schema structure, and versioning guidelines.

Transports

Delivery mechanisms: A2A, AG-UI, WebSockets, and SSE.

Build docs developers (and LLMs) love