The API Contract

JSON contract for threads, messages, and streaming.

OpenUI Chat can work with any backend stack as long as the API contract is respected.

This page is the reference source for request and response shapes. Use Connecting to LLM for decision guidance and Connect Thread History for the setup flow.

Chat endpoint contract

When you pass apiUrl, OpenUI sends a POST request with this shape:

{
  "threadId": "thread_123",
  "messages": [{ "id": "msg_1", "role": "user", "content": "Hello" }]
}
  • threadId is the selected thread ID when persistence is enabled, or "ephemeral" when no thread storage is configured.
  • messages is converted through messageFormat.toApi(messages) before the request is sent.

If your backend already accepts the default AG-UI message shape, each message can stay in this form:

{ "id": "msg_1", "role": "user", "content": "Hello" }

Stream response

Your response stream must match one of these cases:

Backend response shapeFrontend config
OpenUI ProtocolNo streamProtocol needed
Raw OpenAI Chat Completions SSEstreamProtocol={openAIAdapter()}
OpenAI SDK toReadableStream() / NDJSONstreamProtocol={openAIReadableStreamAdapter()}
OpenAI Responses APIstreamProtocol={openAIResponsesAdapter()}

Default thread API contract

When using threadApiUrl="/api/threads", OpenUI expects the base URL plus these default path segments:

ActionMethodURLRequest bodyResponse
List threadsGET/api/threads/get{ threads: Thread[], nextCursor?: any }
Create threadPOST/api/threads/create{ messages }Thread
Update threadPATCH/api/threads/update/:idThreadThread
Delete threadDELETE/api/threads/delete/:idempty response is fine
Load messagesGET/api/threads/get/:idmessage array in your backend format

messages in the create request is the first user message, already converted through messageFormat.toApi([firstMessage]).

Thread shape

type Thread = {
  id: string;
  title: string;
  createdAt: string | number;
};

Message format contract

messageFormat controls both directions:

  • toApi() shapes the messages array sent to apiUrl and threadApiUrl/create
  • fromApi() shapes the array returned from threadApiUrl/get/:id

OpenUI ships with these built-in message converters:

ConverterUse when your backend expects or returns...
DefaultAG-UI message objects
openAIMessageFormatOpenAI chat completion messages
openAIConversationMessageFormatOpenAI Responses conversation items

Every persisted message should include a unique id. Without stable message IDs, history hydration and message updates become unreliable.

Example custom converter

const myCustomFormat = {
  toApi(messages) {
    return messages.map((message) => ({
      speaker: message.role,
      text: message.content,
    }));
  },
  fromApi(items) {
    return items.map((item) => ({
      id: item.id,
      role: item.speaker,
      content: item.text,
    }));
  },
};

On this page