# PostMessage Protocol Source: https://docs.thesys.dev/agent-builder/advanced/postmessage-protocol Low-level postMessage API for communicating with a Thesys agent embedded via iframe. If you're using the **embed widget** npm package, you don't need this page — the widget handles all postMessage communication internally. This protocol is for developers who embed the agent using a **direct iframe** and want programmatic control. The Thesys agent iframe communicates with the parent window using the browser's `postMessage` API. Messages are plain objects with a `type` field that identifies the action. *** ## Parent → Iframe Send messages to the agent by calling `postMessage` on the iframe's `contentWindow`: ```javascript theme={null} const iframe = document.getElementById('thesys-agent'); iframe.contentWindow.postMessage( { type: 'THESYS_SEND_MESSAGE', message: 'Hello!', newThread: false }, new URL(iframe.src).origin ); // Reset the current thread (start a new chat) iframe.contentWindow.postMessage( { type: 'THESYS_RESET_THREAD' }, new URL(iframe.src).origin ); // Toggle the conversation history sidebar iframe.contentWindow.postMessage( { type: 'THESYS_TOGGLE_SIDEBAR' }, new URL(iframe.src).origin ); ``` Always use a specific `targetOrigin` (e.g. `new URL(iframe.src).origin`) instead of `"*"` to prevent leaking data if the iframe navigates to an unexpected URL. ### Message Types | Type | Payload | Description | | --------------------------------- | ----------------------------------------- | ---------------------------------------------------------- | | `THESYS_SEND_MESSAGE` | `{ message: string, newThread: boolean }` | Send a user message to the agent | | `THESYS_SET_INPUT` | `{ message: string, newThread: boolean }` | Prefill the chat input without sending | | `THESYS_IDENTITY_TOKEN_REFRESHED` | `{ identityToken: string }` | Provide a fresh identity token after a refresh request | | `THESYS_RESET_THREAD` | *(none)* | Start a new conversation thread, clearing the current chat | | `THESYS_TOGGLE_SIDEBAR` | *(none)* | Open or close the conversation history sidebar | *** ## Iframe → Parent The agent sends messages to the parent window. Listen for them with `addEventListener`: ```javascript theme={null} const expectedOrigin = new URL(iframe.src).origin; window.addEventListener('message', (event) => { if (event.origin !== expectedOrigin) return; if (!event.data || typeof event.data.type !== 'string') return; switch (event.data.type) { case 'THESYS_APP_READY': // Agent has finished loading and is ready break; case 'THESYS_THREAD_CHANGED': // Active thread changed — event.data.threadId break; case 'THESYS_NEW_THREAD': // User started a new chat break; case 'THESYS_USER_MESSAGE_SENT': // User sent a message — event.data.message, event.data.threadId break; case 'THESYS_GENERATION_STARTED': // Agent started generating — event.data.threadId, event.data.messageId break; case 'THESYS_GENERATION_ENDED': // Agent finished generating — event.data.threadId, event.data.messageId // event.data.message holds the full persisted assistant message, // only when this origin is on the allowedParentOrigins list. break; case 'THESYS_TOOL_EXECUTION_STARTED': // Tool started — event.data.toolName, event.data.threadId break; case 'THESYS_TOOL_EXECUTION_ENDED': // Tool finished — event.data.toolName, event.data.threadId, event.data.error break; case 'THESYS_AGENT_ERROR': // Error occurred — event.data.code, event.data.message break; case 'THESYS_WIDGET_CLOSE': // Agent requested to close break; case 'THESYS_WIDGET_OPEN': // Agent requested to open break; case 'THESYS_WIDGET_TOGGLE': // Agent requested to toggle open/close break; case 'THESYS_IDENTITY_TOKEN_REFRESH_NEEDED': // Identity token expired — fetch a new one and send it back break; case 'THESYS_IDENTITY_TOKEN_ERROR': // Identity token error — event.data.code, event.data.message break; } }); ``` Always validate `event.origin` against the expected iframe origin to prevent unauthorized windows from triggering actions. ### Message Types | Type | Payload | Description | | -------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `THESYS_APP_READY` | *(none)* | The agent has finished loading and is ready for interaction | | `THESYS_WIDGET_CLOSE` | *(none)* | The agent is requesting to be closed | | `THESYS_WIDGET_OPEN` | *(none)* | The agent is requesting to be opened | | `THESYS_WIDGET_TOGGLE` | *(none)* | The agent is requesting to toggle its visibility | | `THESYS_THREAD_CHANGED` | `{ threadId: string }` | The active conversation thread changed | | `THESYS_NEW_THREAD` | *(none)* | The user started a new chat (before the thread is created) | | `THESYS_USER_MESSAGE_SENT` | `{ message: string, threadId: string }` | The user sent a message | | `THESYS_GENERATION_STARTED` | `{ threadId: string, messageId: string }` | The agent started generating a response | | `THESYS_GENERATION_ENDED` | `{ threadId: string, messageId: string, message?: AssistantMessage }` | The agent finished generating a response. `message` is the full persisted assistant item, present only for allowlisted origins (see [Allowed Parent Origins](#allowed-parent-origins)). | | `THESYS_TOOL_EXECUTION_STARTED` | `{ toolName: string, threadId: string }` | A tool started executing | | `THESYS_TOOL_EXECUTION_ENDED` | `{ toolName: string, threadId: string, error?: string }` | A tool finished executing | | `THESYS_AGENT_ERROR` | `{ code: string, message: string }` | An error occurred during processing | | `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED` | *(none)* | The identity token has expired; parent should provide a new one | | `THESYS_IDENTITY_TOKEN_ERROR` | `{ code: string, message: string }` | An identity token error occurred | If you're using the **embed widget** npm package, you can use the higher-level [Events API](/agent-builder/embed-widget/events) instead of listening for raw `postMessage` events. *** ## Allowed Parent Origins For security, the agent splits its outgoing messages into two classes: * **Protocol events** — the handshake (`THESYS_APP_READY`), widget controls (`THESYS_WIDGET_OPEN/CLOSE/TOGGLE`), and `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED`. These are required for the embed to function and are always sent to `'*'` with their full payload. * **Data events** — anything that carries information about the user's conversation: `THESYS_THREAD_CHANGED`, `THESYS_NEW_THREAD`, `THESYS_USER_MESSAGE_SENT`, `THESYS_GENERATION_STARTED`, `THESYS_GENERATION_ENDED`, `THESYS_AGENT_ERROR`, `THESYS_TOOL_EXECUTION_STARTED/ENDED`, and `THESYS_IDENTITY_TOKEN_ERROR`. Data events are only delivered with their full payload to parent origins on the playground's **Allowed Parent Origins** allowlist. When the allowlist is empty, the event still fires, but the payload is stripped to just `{ type }` so non-opted-in embedders cannot read private conversation data. ```javascript theme={null} // Allowlist empty (default): // Parent receives: { type: 'THESYS_GENERATION_ENDED' } // // Allowlist contains the parent's origin: // Parent receives: { // type: 'THESYS_GENERATION_ENDED', // threadId: '…', // messageId: '…', // message: { id, role: 'assistant', content: [...], ... } // } ``` ### Configuring the allowlist 1. Open your playground in the [Thesys Console](https://console.thesys.dev/). 2. Click **Deploy**, expand the **Embed on your website** section. 3. Under **Allowed parent origins**, add each origin (e.g. `https://app.example.com`, `http://localhost:3000`) that should receive full event payloads. 4. Republish the playground — the new allowlist applies to all subsequent loads of the iframe. Origins must be exact `scheme://host[:port]` matches. No paths, no trailing slashes, no wildcards. *** ## Identity Token Refresh When a [BYOI](/agent-builder/byoi) identity token expires during an active session, the agent sends `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED` to the parent. The parent should fetch a new token from its backend and reply with `THESYS_IDENTITY_TOKEN_REFRESHED`: ```javascript theme={null} const iframe = document.getElementById('thesys-agent'); const agentOrigin = new URL(iframe.src).origin; window.addEventListener('message', async (event) => { if (event.origin !== agentOrigin) return; if (event.data?.type === 'THESYS_IDENTITY_TOKEN_REFRESH_NEEDED') { try { const res = await fetch('/api/thesys-token'); const { token } = await res.json(); iframe.contentWindow.postMessage( { type: 'THESYS_IDENTITY_TOKEN_REFRESHED', identityToken: token }, agentOrigin ); } catch (error) { console.error('Failed to refresh identity token:', error); } } }); ``` If the parent doesn't respond within **10 seconds**, the agent shows an error modal to the user. *** ## URL Parameters When embedding via a direct iframe, you can configure the agent through URL query parameters: | Parameter | Value | Description | | ------------------ | -------------------------------------------------------------------------- | --------------------------------------------------------------- | | `IDENTITY_TOKEN` | JWT string | Sets the initial identity token for [BYOI](/agent-builder/byoi) | | `HIDE_LOGIN` | `"true"` | Hides the Thesys login UI | | `appRenderContext` | `TRAY_EMBED_WIDGET` \| `FULLSCREEN_EMBED_WIDGET` \| `CHATBAR_EMBED_WIDGET` | Tells the agent which embed layout context it's running in | ```html theme={null} ``` # Bring Your Own Identity (BYOI) Source: https://docs.thesys.dev/agent-builder/byoi Pass your own user identities into a published Thesys agent — each user gets isolated conversation history without needing a Thesys login. ## Overview BYOI lets you identify **your** users inside a Thesys-published agent. Instead of requiring end-users to create a Thesys account, you sign a lightweight JWT on your backend and pass it to the embedded agent. Thesys verifies the signature and scopes every conversation to the user ID you provide. ```mermaid theme={null} sequenceDiagram participant User participant YourBackend as Your Backend participant Agent as Thesys Agent User->>YourBackend: Logs in to your app YourBackend->>YourBackend: Signs JWT with Identity Secret YourBackend->>Agent: Passes JWT (via widget or iframe) Agent->>Agent: Verifies JWT signature Agent->>Agent: Scopes conversations to externalUserId Agent-->>User: Personalized, isolated chat history ``` **What you get:** * Per-user conversation history — no Thesys login required * Full control over user identification * Works with both the embed widget and direct iframe embedding * Automatic token refresh when JWTs expire mid-session *** ## Quick Setup Open your agent in [Agent Builder](https://console.thesys.dev), click **Share** to open the publish side panel, and find the **Bring Your Own Identity** section. Click **Generate Secret** and copy the value. Store it securely on your backend — treat it like a password. Never expose the Identity Secret in frontend code. It must only live on your server. Create an **HS256-signed JWT** containing your user's ID. The token must include: | Claim | Type | Required | Description | | ---------------- | -------- | ----------- | ----------------------------------- | | `externalUserId` | `string` | **Yes** | Your app's unique user identifier | | `exp` | `number` | Recommended | Expiration timestamp (Unix seconds) | ```javascript Node.js theme={null} const jwt = require('jsonwebtoken'); function generateIdentityToken(userId) { return jwt.sign( { externalUserId: userId }, process.env.THESYS_IDENTITY_SECRET, { algorithm: 'HS256', expiresIn: '1h' } ); } // Express endpoint app.get('/api/thesys-token', (req, res) => { const token = generateIdentityToken(req.user.id); res.json({ token }); }); ``` ```python Python theme={null} import jwt, time, os def generate_identity_token(user_id: str) -> str: return jwt.encode( { "externalUserId": user_id, "exp": int(time.time()) + 3600, # 1 hour }, os.environ["THESYS_IDENTITY_SECRET"], algorithm="HS256", ) # Flask / FastAPI endpoint @app.get("/api/thesys-token") def get_token(): token = generate_identity_token(current_user.id) return {"token": token} ``` Set a reasonable expiration (e.g. 1 hour). If you configure automatic token refresh (see below), expired tokens are silently renewed without interrupting the user. Choose the integration method that fits your setup: Install the embed widget: ```bash theme={null} npm install agent-embed-widget ``` Pass `identityToken` for the initial token and `getIdentityToken` for automatic refresh on expiry: ```javascript theme={null} import { embedWidget } from 'agent-embed-widget'; const { token } = await fetch('/api/thesys-token') .then(r => r.json()); embedWidget({ url: 'https://console.thesys.dev/app/YOUR_SLUG', identityToken: token, getIdentityToken: async () => { // Called automatically when the token expires const res = await fetch('/api/thesys-token'); const { token } = await res.json(); return token; }, hideLogin: true, }); ``` The widget handles the entire refresh cycle for you — when the JWT expires mid-conversation, it calls your `getIdentityToken` callback, gets a fresh token, and retries the failed request seamlessly. The widget supports three display modes: `tray` (default), `full-screen`, and `chatbar`. Append `IDENTITY_TOKEN` and `HIDE_LOGIN` as URL query parameters: ```html theme={null} ``` To set the token dynamically: ```javascript theme={null} const token = await fetchTokenFromYourBackend(); const iframe = document.getElementById('thesys-agent'); iframe.src = `https://console.thesys.dev/app/YOUR_SLUG?IDENTITY_TOKEN=${token}&HIDE_LOGIN=true`; ``` **Automatic token refresh for iframes** requires a `postMessage` listener. See the [Token Refresh](#automatic-token-refresh) section below for the full setup. *** ## Automatic Token Refresh When a JWT expires during an active conversation, the Thesys agent automatically requests a fresh token from the parent window using `postMessage`. If a new token is provided, the failed request is retried seamlessly — the user never sees an error. ### How it works ```mermaid theme={null} sequenceDiagram participant Agent as Thesys Agent (iframe) participant Parent as Parent Window participant Backend as Your Backend Agent->>Agent: API call fails — token expired Agent->>Parent: postMessage: REFRESH_NEEDED Parent->>Backend: Fetch new token Backend-->>Parent: Fresh JWT Parent->>Agent: postMessage: REFRESHED {token} Agent->>Agent: Updates token & retries request Note over Agent: Conversation continues seamlessly ``` If the parent doesn't respond within 10 seconds (e.g. no listener configured), the agent falls back to showing an error modal asking the user to refresh the page. ### Embed Widget If you're using the embed widget, token refresh is handled automatically — just provide the `getIdentityToken` callback as shown in [Step 3](#quick-setup) above. No additional setup needed. ### Direct iframe For direct iframe embeds, add a `message` event listener on your parent page: ```javascript theme={null} const iframe = document.getElementById('thesys-agent'); window.addEventListener('message', async (event) => { if (event.data?.type === 'THESYS_IDENTITY_TOKEN_REFRESH_NEEDED') { try { const res = await fetch('/api/thesys-token'); const { token } = await res.json(); iframe.contentWindow.postMessage({ type: 'THESYS_IDENTITY_TOKEN_REFRESHED', identityToken: token, }, '*'); } catch (error) { console.error('Failed to refresh identity token:', error); } } }); ``` ### PostMessage Protocol | Direction | Message Type | Payload | | --------------- | -------------------------------------- | --------------------------- | | iframe → parent | `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED` | *(none)* | | parent → iframe | `THESYS_IDENTITY_TOKEN_REFRESHED` | `{ identityToken: string }` | *** ## How It Works Under the Hood * Thesys verifies the JWT signature using your Identity Secret (HS256) * The `externalUserId` claim is extracted and used to scope conversations * Each unique `externalUserId` gets its own isolated conversation history * If no identity token is provided, users are anonymous (session-based) * If a user is logged into Thesys **and** has an identity token, the identity token takes priority *** ## Secret Rotation If you need to rotate your Identity Secret: 1. Click **Rotate Secret** in the BYOI section of the publish panel 2. Copy the new secret to your backend environment 3. Redeploy your backend Rotating the secret invalidates **all** previously signed tokens immediately. Users with old tokens will see an "Authentication Failed" error until they get a new token (via refresh or page reload). *** ## Token Errors | Error | Cause | Resolution | | --------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------- | | **Session Expired** | JWT `exp` has passed | Handled automatically if token refresh is configured; otherwise the user must refresh the page | | **Authentication Failed** | Signature doesn't match (secret was rotated?) | Ensure your backend uses the current Identity Secret | | **Invalid Identity Token** | Missing `externalUserId` claim | Add the required claim to your JWT payload | | **Identity Not Configured** | No secret has been generated | Generate a secret in the publish panel | *** ## FAQ No. The secret is synced to all published versions automatically. No. Each agent has its own independent Identity Secret. Only **HS256** (HMAC-SHA256). Other algorithms will be rejected. No hard limit, but keep payloads small. Only `externalUserId` and `exp` are read by Thesys. Yes, but Thesys only reads `externalUserId` and `exp`. Extra claims are ignored. When the token expires mid-session, the agent shows an error modal. The user must refresh the page to continue. Configuring automatic refresh avoids this interruption entirely. Yes. BYOI works with all three embed widget display modes: `tray`, `full-screen`, and `chatbar`. # Customization Source: https://docs.thesys.dev/agent-builder/embed-widget/customization Theme the chatbar, set placeholders, and style the widget to match your brand. ## Theme Set the overall color scheme with the `theme` option: ```javascript theme={null} embedWidget({ url: 'https://console.thesys.dev/app/your-slug', theme: 'light', // 'dark' (default) or 'light' }); ``` This affects the floating button, loading screen, and the overall widget chrome. The agent content inside the iframe is styled independently through the Agent Builder style settings. *** ## Chatbar Theming The chatbar layout supports fine-grained visual customization through the `options.theme` property. Each value maps to a CSS variable on the chatbar container. ```javascript theme={null} embedWidget({ url: 'https://console.thesys.dev/app/your-slug', type: 'chatbar', options: { theme: { containerFills: '#1a1a2e', strokeDefault: '#333366', roundedL: '24px', primaryText: '#e0e0ff', interactiveAccent: '#7c3aed', accentPrimaryText: '#ffffff', roundedS: '12px', }, }, }); ``` ### Theme Properties | Property | CSS Variable | Description | | ------------------- | ------------------------------ | ------------------------------------------ | | `containerFills` | `--crayon-container-fills` | Background color of the chatbar input area | | `strokeDefault` | `--crayon-stroke-default` | Border color of the input area | | `roundedL` | `--crayon-rounded-l` | Border radius of the entire chatbar | | `primaryText` | `--crayon-primary-text` | Color of the input text | | `interactiveAccent` | `--crayon-interactive-accent` | Background color of the send button | | `accentPrimaryText` | `--crayon-accent-primary-text` | Icon/text color on the send button | | `roundedS` | `--crayon-rounded-s` | Border radius of the send button | All properties are optional. Unset properties use the defaults from the widget's `dark` or `light` theme. To match your site's design exactly, inspect the chatbar element in DevTools and override additional CSS variables on the `.thesys--chatbar-widget` selector. *** ## Chatbar Options Beyond theming, the chatbar accepts functional options: ```javascript theme={null} embedWidget({ url: '...', type: 'chatbar', options: { placeholder: 'Search our knowledge base...', keyboardShortcutEnabled: false, conversationStarters: [ 'What features do you offer?', 'How does pricing work?', ], }, }); ``` | Option | Type | Default | Description | | ------------------------- | ---------- | ------------------- | ------------------------------------------------------------ | | `placeholder` | `string` | `"Ask me anything"` | Input placeholder text | | `keyboardShortcutEnabled` | `boolean` | `true` | Allow **Space** to focus the input from anywhere on the page | | `conversationStarters` | `string[]` | `[]` | Suggested prompts shown when the input is focused | See [Widget Types — Chatbar](/agent-builder/embed-widget/widget-types#chatbar) for more details on chatbar behavior. *** ## Hiding the Login UI If you're using [BYOI](/agent-builder/byoi) to identify users, you'll typically want to hide the built-in Thesys login interface: ```javascript theme={null} embedWidget({ url: '...', hideLogin: true, identityToken: yourJwt, }); ``` *** ## Preloading By default, the agent iframe loads when the user first opens the widget. To eliminate that initial load delay, enable preloading: ```javascript theme={null} // Preload immediately const widget = embedWidget({ url: '...', preload: true, }); // Or preload later based on user behavior widget.preload(); ``` The iframe loads invisibly in the background so the agent is ready instantly when the user opens it. # Events Source: https://docs.thesys.dev/agent-builder/embed-widget/events Listen to lifecycle events from the embedded agent — thread changes, message flow, tool execution, and errors. The embed widget emits events at key points in the agent's lifecycle, letting you react to user actions, track conversation flow, and handle errors in your application. There are two ways to listen to events: **inline callbacks** passed to `embedWidget()`, and the **`on()` method** on the widget instance. **Data events carry payload only when the parent origin is allowlisted.** By default, events that contain conversation data (`generationEnded`, `userMessageSent`, `threadChanged`, etc.) still fire but their payload is stripped to just the event type. To receive the full payload — including the assistant's message body for live rendering — you must add your parent page's origin to the playground's **Allowed Parent Origins** list. See [Receiving Message Data](#receiving-message-data) below. *** ## Quick Start ```javascript theme={null} import { embedWidget } from 'agent-embed-widget'; import 'agent-embed-widget/dist/agent-embed-widget.css'; const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', // Inline callbacks onGenerationStarted: (threadId, messageId) => { console.log('Agent is thinking…', { threadId, messageId }); }, onGenerationEnded: (threadId, messageId) => { console.log('Agent replied', { threadId, messageId }); }, }); // Or subscribe dynamically with on() const unsub = widget.on('userMessageSent', (message, threadId) => { analytics.track('user_message', { message, threadId }); }); // Later: stop listening unsub(); ``` *** ## Event Reference ### Thread Events #### `threadChanged` Fires when the active conversation thread changes — for example, when the user selects a different thread from the thread list, or a new thread is created after the first message is sent. | Parameter | Type | Description | | ---------- | -------- | --------------------------------- | | `threadId` | `string` | The ID of the newly active thread | ```javascript theme={null} widget.on('threadChanged', (threadId) => { console.log('Switched to thread:', threadId); }); ``` #### `newThread` Fires when the user clicks "New Chat" to start a fresh conversation. At this point no thread has been created on the backend yet — the thread is created once the first message is sent (which then fires `threadChanged`). ```javascript theme={null} widget.on('newThread', () => { console.log('User started a new chat'); }); ``` *** ### Message Events #### `userMessageSent` Fires when the user sends a message to the agent. | Parameter | Type | Description | | ---------- | -------- | ---------------------------------- | | `message` | `string` | The message content | | `threadId` | `string` | The thread the message was sent in | ```javascript theme={null} widget.on('userMessageSent', (message, threadId) => { console.log('User said:', message, 'in thread:', threadId); }); ``` #### `generationStarted` Fires when the agent begins generating a response. | Parameter | Type | Description | | ----------- | -------- | ----------------------------------------------- | | `threadId` | `string` | The thread the response is being generated in | | `messageId` | `string` | The ID of the assistant message being generated | ```javascript theme={null} widget.on('generationStarted', (threadId, messageId) => { showTypingIndicator(threadId); }); ``` #### `generationEnded` Fires when the agent finishes generating a response. The `messageId` is the persisted backend ID that you can use with the Thesys API to fetch the message content. When your parent origin is allowlisted, `message` contains the **full persisted assistant message** so you can render the response live in another panel without an additional API call. | Parameter | Type | Description | | ----------- | ------------------------------- | ---------------------------------------------------------------------------------------------------- | | `threadId` | `string \| undefined` | The thread the response was generated in | | `messageId` | `string \| undefined` | The backend ID of the completed assistant message | | `message` | `AssistantMessage \| undefined` | The full persisted assistant message item (id, role, content, status, conversation\_id, metadata, …) | All three arguments are `undefined` unless the embedding page's origin is on the playground's **Allowed Parent Origins** list. See [Receiving Message Data](#receiving-message-data). ```javascript theme={null} widget.on('generationEnded', (threadId, messageId, message) => { hideTypingIndicator(threadId); if (message) { // Render the assistant's reply in a side panel renderAssistantMessage(message); } }); ``` **`AssistantMessage` shape** ```typescript theme={null} interface AssistantMessage { id: string; role: 'assistant'; type: 'message'; content: Array>; status?: Record; conversation_id?: string; created_at?: number; metadata?: Record; } ``` *** ### Tool Execution Events These events fire when the agent uses a tool (web search, image search, artifact builder, etc.) during response generation. Internal tools used by the system are excluded — only user-facing tools trigger these events. #### `toolExecutionStarted` Fires when the agent starts executing a tool. | Parameter | Type | Description | | ---------- | -------- | ----------------------------------- | | `toolName` | `string` | The name of the tool being executed | | `threadId` | `string` | The thread the tool is executing in | ```javascript theme={null} widget.on('toolExecutionStarted', (toolName, threadId) => { console.log(`Tool "${toolName}" started in thread ${threadId}`); }); ``` #### `toolExecutionEnded` Fires when a tool finishes executing. If the tool failed, the `error` parameter contains the error message. | Parameter | Type | Description | | ---------- | --------------------- | -------------------------------------------------------- | | `toolName` | `string` | The name of the tool that finished | | `threadId` | `string` | The thread the tool executed in | | `error` | `string \| undefined` | Error message if the tool failed, `undefined` on success | ```javascript theme={null} widget.on('toolExecutionEnded', (toolName, threadId, error) => { if (error) { console.error(`Tool "${toolName}" failed:`, error); } else { console.log(`Tool "${toolName}" completed successfully`); } }); ``` *** ### Error Events #### `agentError` Fires when an error occurs during message processing or response streaming. | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------------- | | `code` | `string` | Error code (`MESSAGE_PROCESSING_ERROR` or `STREAM_PROCESSING_ERROR`) | | `message` | `string` | Human-readable error description | ```javascript theme={null} widget.on('agentError', (code, message) => { errorReporter.capture({ code, message }); }); ``` #### `identityTokenError` Fires when a [BYOI](/agent-builder/byoi) identity token is invalid or cannot be refreshed. This is separate from the normal token refresh flow — it indicates a persistent authentication failure. | Parameter | Type | Description | | --------- | -------- | --------------------------------------- | | `code` | `string` | Error code identifying the failure type | | `message` | `string` | Human-readable error description | ```javascript theme={null} widget.on('identityTokenError', (code, message) => { console.error('Identity token error:', code, message); // Redirect user to re-authenticate window.location.href = '/login'; }); ``` *** ## Inline Callbacks All events can also be passed as callbacks directly to `embedWidget()`. Callback names use the `on` prefix with PascalCase: ```javascript theme={null} const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', onThreadChanged: (threadId) => { /* ... */ }, onNewThread: () => { /* ... */ }, onUserMessageSent: (message, threadId) => { /* ... */ }, onGenerationStarted: (threadId, messageId) => { /* ... */ }, onGenerationEnded: (threadId, messageId, message) => { /* ... */ }, onAgentError: (code, message) => { /* ... */ }, onToolExecutionStarted: (toolName, threadId) => { /* ... */ }, onToolExecutionEnded: (toolName, threadId, error) => { /* ... */ }, onIdentityTokenError: (code, message) => { /* ... */ }, }); ``` Both inline callbacks and `on()` listeners fire for the same event — you can use either or both. *** ## `widget.on(event, callback)` Subscribe to events dynamically after the widget is created. Returns an **unsubscribe function**. ```javascript theme={null} const unsubscribe = widget.on('generationEnded', (threadId, messageId) => { console.log('Done:', messageId); }); // Stop listening unsubscribe(); ``` Multiple listeners can be registered for the same event. Each call to `on()` returns its own independent unsubscribe function. *** ## Receiving Message Data For security, the published agent does **not** broadcast conversation data (assistant messages, thread IDs, user messages, tool names, error details) to arbitrary embedders. Without explicit opt-in, every data event still fires — but with its payload stripped to just the event type: ```javascript theme={null} // Default behavior (no allowlisted origins): all data events fire as // { type: 'THESYS_GENERATION_ENDED' } // { type: 'THESYS_USER_MESSAGE_SENT' } // ... // All payload fields are undefined in your callbacks. ``` To receive the full payload — including the assistant `message` for live rendering — add your parent page's origin to the playground's **Allowed Parent Origins** list: 1. Open your playground in the [Thesys Console](https://console.thesys.dev/). 2. Click **Deploy**, expand the **Embed on your website** section. 3. Under **Allowed parent origins**, add each origin that should receive full event payloads (for example, `https://app.example.com` or `http://localhost:3000`). 4. Republish the playground. Once allowlisted, your origin will receive the full payload for every data event. Origins not on the list will continue to receive stripped events. The handshake events (`APP_READY`, widget open/close/toggle, identity-token refresh) are unaffected and always work. ### What counts as a "data event"? | Event | Class | Notes | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------- | | `threadChanged`, `newThread`, `userMessageSent`, `generationStarted`, `generationEnded`, `agentError`, `toolExecutionStarted`, `toolExecutionEnded`, `identityTokenError` | **Data** | Payload only delivered to allowlisted origins | | `THESYS_APP_READY`, `THESYS_WIDGET_OPEN/CLOSE/TOGGLE`, `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED` | **Protocol** | Required for widget functionality, always delivered with full payload | Origins must be exact matches: `scheme://host[:port]` with no path or trailing slash. `https://app.example.com` and `https://app.example.com:443` are treated as different origins. *** ## Typical Event Flow Here's the sequence of events during a normal conversation turn: ``` newThread // User clicks "New Chat" → userMessageSent // User sends first message → threadChanged (threadId) // Backend creates the thread → generationStarted // Agent begins responding → toolExecutionStarted // (if the agent uses a tool) → toolExecutionEnded // → generationEnded // Agent finishes responding ``` For subsequent messages in the same thread: ``` userMessageSent → generationStarted → generationEnded ``` *** ## Direct Iframe Integration If you're embedding the agent via a **direct iframe** instead of the embed widget npm package, you can listen for the same events using the browser's `postMessage` API. Each event is sent as a message with a `type` field prefixed with `THESYS_`. For full details on `postMessage` usage, origin validation, and URL parameters, see the [PostMessage Protocol](/agent-builder/advanced/postmessage-protocol) page. ### Event Mapping | Embed Widget Event | postMessage `type` | Payload | | ---------------------- | -------------------------------------- | -------------------------------------------------------------------- | | `threadChanged` | `THESYS_THREAD_CHANGED` | `{ threadId: string }` | | `newThread` | `THESYS_NEW_THREAD` | *(none)* | | `userMessageSent` | `THESYS_USER_MESSAGE_SENT` | `{ message: string, threadId: string }` | | `generationStarted` | `THESYS_GENERATION_STARTED` | `{ threadId: string, messageId: string }` | | `generationEnded` | `THESYS_GENERATION_ENDED` | `{ threadId: string, messageId: string, message: AssistantMessage }` | | `toolExecutionStarted` | `THESYS_TOOL_EXECUTION_STARTED` | `{ toolName: string, threadId: string }` | | `toolExecutionEnded` | `THESYS_TOOL_EXECUTION_ENDED` | `{ toolName: string, threadId: string, error?: string }` | | `agentError` | `THESYS_AGENT_ERROR` | `{ code: string, message: string }` | | `identityTokenError` | `THESYS_IDENTITY_TOKEN_ERROR` | `{ code: string, message: string }` | | *(widget-only)* | `THESYS_APP_READY` | *(none)* — the agent has finished loading | | *(widget-only)* | `THESYS_WIDGET_CLOSE` | *(none)* — the agent requested to close | | *(widget-only)* | `THESYS_WIDGET_OPEN` | *(none)* — the agent requested to open | | *(widget-only)* | `THESYS_WIDGET_TOGGLE` | *(none)* — the agent requested to toggle visibility | | *(widget-only)* | `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED` | *(none)* — token expired, parent should refresh | | *(parent → iframe)* | `THESYS_RESET_THREAD` | *(none)* — start a new conversation thread | | *(parent → iframe)* | `THESYS_TOGGLE_SIDEBAR` | *(none)* — open or close the conversation history sidebar | ### Example ```javascript theme={null} const iframe = document.getElementById('thesys-agent'); const expectedOrigin = new URL(iframe.src).origin; window.addEventListener('message', (event) => { if (event.origin !== expectedOrigin) return; if (!event.data?.type) return; switch (event.data.type) { case 'THESYS_APP_READY': console.log('Agent loaded'); break; case 'THESYS_THREAD_CHANGED': console.log('Thread:', event.data.threadId); break; case 'THESYS_NEW_THREAD': console.log('New chat started'); break; case 'THESYS_USER_MESSAGE_SENT': console.log('User said:', event.data.message); break; case 'THESYS_GENERATION_STARTED': console.log('Generating…', event.data.messageId); break; case 'THESYS_GENERATION_ENDED': console.log('Done:', event.data.messageId); // event.data.message is the full assistant message — only present // when this origin is on the playground's allowed parent origins list. if (event.data.message) { renderAssistantMessage(event.data.message); } break; case 'THESYS_TOOL_EXECUTION_STARTED': console.log('Tool started:', event.data.toolName); break; case 'THESYS_TOOL_EXECUTION_ENDED': console.log('Tool ended:', event.data.toolName, event.data.error); break; case 'THESYS_AGENT_ERROR': console.error('Error:', event.data.code, event.data.message); break; case 'THESYS_IDENTITY_TOKEN_ERROR': console.error('Token error:', event.data.code, event.data.message); break; case 'THESYS_IDENTITY_TOKEN_REFRESH_NEEDED': // Fetch a new token and send THESYS_IDENTITY_TOKEN_REFRESHED back break; } }); ``` *** ## Example: Analytics Integration ```javascript theme={null} import { embedWidget } from 'agent-embed-widget'; import 'agent-embed-widget/dist/agent-embed-widget.css'; const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', }); widget.on('userMessageSent', (message, threadId) => { analytics.track('agent_message_sent', { threadId, messageLength: message.length, }); }); widget.on('generationEnded', (threadId, messageId, message) => { analytics.track('agent_response_received', { threadId, messageId, contentLength: message?.content?.length, }); }); widget.on('toolExecutionStarted', (toolName, threadId) => { analytics.track('agent_tool_used', { toolName, threadId }); }); widget.on('agentError', (code, message) => { analytics.track('agent_error', { code, message }); }); ``` # Getting Started Source: https://docs.thesys.dev/agent-builder/embed-widget/getting-started Install the embed widget and add a Thesys agent to your site in under five minutes. ## Installation ```bash npm theme={null} npm install agent-embed-widget ``` ```bash yarn theme={null} yarn add agent-embed-widget ``` ```bash pnpm theme={null} pnpm add agent-embed-widget ``` The widget requires **React 18+** or **React 19+** as a peer dependency. If your project doesn't use React, use the [UMD / CDN](#umd--cdn) approach below. *** ## Quick Start ### ES Module ```javascript theme={null} import { embedWidget } from 'agent-embed-widget'; import 'agent-embed-widget/dist/agent-embed-widget.css'; const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', theme: 'dark', }); ``` That's it — a floating chat button appears in the bottom-right corner. Click it to open the agent. ### CommonJS ```javascript theme={null} const { embedWidget } = require('agent-embed-widget'); require('agent-embed-widget/dist/agent-embed-widget.css'); const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', theme: 'dark', }); ``` ### UMD / CDN If you're not using a bundler, load the widget from a CDN: ```html theme={null} ``` *** ## Options | Option | Type | Default | Description | | ------------------ | -------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------- | | `url` | `string` | — | **Required.** The published agent URL | | `type` | `'tray'` \| `'full-screen'` \| `'chatbar'` | `'tray'` | Widget layout. See [Widget Types](/agent-builder/embed-widget/widget-types) | | `theme` | `'dark'` \| `'light'` | `'dark'` | Color theme | | `hideLogin` | `boolean` | `false` | Hide the Thesys login UI inside the agent | | `identityToken` | `string` | — | Signed HS256 JWT for [BYOI](/agent-builder/byoi) user identity | | `getIdentityToken` | `() => Promise` | — | Callback for automatic token refresh on expiry | | `preload` | `boolean` | `false` | Load the agent iframe in the background immediately | | `options` | `TrayOptions` \| `FullScreenOptions` \| `ChatbarOptions` | — | Type-specific options. See [Customization](/agent-builder/embed-widget/customization) | *** ## Widget Instance `embedWidget()` returns a `WidgetInstance` with methods for programmatic control: ```javascript theme={null} const widget = embedWidget({ url: '...', theme: 'dark' }); widget.open(); // Show the widget widget.close(); // Hide the widget widget.sendMessage('Hello!'); // Send a message widget.sendMessage('Start over', { newThread: true }); // New thread widget.setInput('Draft for the user to review'); // Prefill input widget.preload(); // Start loading early widget.destroy(); // Remove from DOM ``` See [Programmatic Control](/agent-builder/embed-widget/programmatic-control) for detailed usage and examples. *** ## Direct iframe (Alternative) If you prefer not to use the npm package, embed the agent directly with an iframe: ```html theme={null} ``` The embed widget is recommended over a raw iframe because it handles widget chrome, responsive layout, programmatic control, and automatic identity token refresh out of the box. *** ## Next Steps * [**Widget Types**](/agent-builder/embed-widget/widget-types) — Choose the right layout for your use case * [**Customization**](/agent-builder/embed-widget/customization) — Theme the chatbar and configure options * [**User Identity (BYOI)**](/agent-builder/byoi) — Identify users and isolate their conversations # Mobile Behavior Source: https://docs.thesys.dev/agent-builder/embed-widget/mobile-behavior How the embed widget adapts to mobile devices and small viewports. The embed widget automatically adapts its layout on mobile devices (viewport width under 768px). No configuration is needed — the responsive behavior is built in. *** ## Tray on Mobile On desktop, the tray opens a compact 583 × 800px panel. On mobile, it switches to a **full-screen overlay** for better usability on small screens. | | Desktop | Mobile | | -------------- | ------------------------------ | ------------------------------ | | **Trigger** | Floating button (bottom-right) | Floating button (bottom-right) | | **Open state** | Compact panel | Full-screen overlay | | **Close** | Click outside or close button | Close button | *** ## Full-screen on Mobile No change — full-screen is already the same layout on both desktop and mobile. *** ## Chatbar on Mobile The chatbar adapts its layout while keeping the inline input bar: | | Desktop | Mobile | | --------------- | ------------------------------ | ------------------------------------- | | **Input bar** | Fixed at bottom center | Fixed at bottom center | | **Chat window** | Floating window above input | Full-screen overlay with close handle | | **Close** | Pill close button above window | Drag handle at top of overlay | When the chat window is open on mobile, the widget container expands to fill the entire screen. When closed, it shrinks back to just the input bar. *** ## Detection Mobile detection uses `window.innerWidth < 768`. This check runs when the widget is first created. If the user resizes the browser window after creation, the chatbar re-evaluates on open/close transitions. The 768px breakpoint is not currently configurable. If you need different behavior at a specific breakpoint, you can conditionally set the `type` option based on your own viewport check before calling `embedWidget()`. # Programmatic Control Source: https://docs.thesys.dev/agent-builder/embed-widget/programmatic-control Open, close, send messages, and prefill input on the embedded agent from your application code. `embedWidget()` returns a `WidgetInstance` with methods to control the agent programmatically. This lets you trigger conversations from buttons, links, application events, or any other logic in your app. ```javascript theme={null} import { embedWidget } from 'agent-embed-widget'; import 'agent-embed-widget/dist/agent-embed-widget.css'; const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', theme: 'dark', }); ``` *** ## Methods ### `widget.open()` Opens the widget and makes it visible. ```javascript theme={null} widget.open(); ``` ### `widget.close()` Closes the widget. The agent stays loaded in the background — reopening is instant. ```javascript theme={null} widget.close(); ``` ### `widget.destroy()` Removes the widget from the DOM entirely and cleans up all resources. After calling `destroy()`, the instance cannot be reopened. ```javascript theme={null} widget.destroy(); ``` ### `widget.sendMessage(message, options?)` Sends a user message to the agent. If the widget is closed, it opens automatically. ```javascript theme={null} widget.sendMessage('Tell me about pricing'); widget.sendMessage('Start a new topic', { newThread: true }); ``` | Parameter | Type | Description | | ------------------- | --------- | ---------------------------------------------------------------------------- | | `message` | `string` | The message text to send | | `options.newThread` | `boolean` | If `true`, starts a new conversation thread before sending. Default: `false` | ### `widget.setInput(message, options?)` Prefills the chat input bar **without** sending. The widget opens (if closed) and the user can review, edit, and submit the message themselves. ```javascript theme={null} widget.setInput('Can you help me understand my recent invoice?'); widget.setInput('New question for a fresh thread', { newThread: true }); ``` | Parameter | Type | Description | | ------------------- | --------- | ------------------------------------------------------------------------------- | | `message` | `string` | The message text to prefill | | `options.newThread` | `boolean` | If `true`, starts a new conversation thread before prefilling. Default: `false` | ### `widget.preload()` Starts loading the agent iframe in the background so it's ready instantly when the user opens the widget. Useful when you didn't set `preload: true` in the initial options but want to trigger it based on user behavior. ```javascript theme={null} widget.preload(); ``` *** ## Examples ### Trigger from buttons ```html theme={null} ``` ### Preload on scroll Load the agent in the background as the user scrolls near the bottom of the page: ```javascript theme={null} const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', }); window.addEventListener('scroll', () => { if (window.scrollY > document.body.scrollHeight - 1000) { widget.preload(); } }, { once: true }); ``` ### Open after a delay ```javascript theme={null} const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', }); setTimeout(() => widget.open(), 5000); ``` ### Clean up on navigation (SPA) In a single-page application, destroy the widget when the user navigates away from the page: ```javascript theme={null} const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', }); // React useEffect cleanup, Vue onUnmounted, etc. return () => widget.destroy(); ``` *** ## Pending Messages If you call `sendMessage()` or `setInput()` before the agent iframe has finished loading, the action is queued and executed automatically once the iframe is ready. You don't need to wait for a "loaded" event. # Widget Types Source: https://docs.thesys.dev/agent-builder/embed-widget/widget-types Choose between tray, full-screen, and chatbar layouts for your embedded agent. The embed widget ships with three layout types. Each is optimized for a different use case. Set the type when calling `embedWidget()`: ```javascript theme={null} const widget = embedWidget({ url: 'https://console.thesys.dev/app/your-slug', type: 'tray', // 'tray' | 'full-screen' | 'chatbar' }); ``` *** ## Tray ```javascript theme={null} embedWidget({ url: '...', type: 'tray' }); ``` The default layout. A floating circular button appears in the **bottom-right corner**. Clicking it opens a compact chat panel (583 × 800px, capped at `100vh - 100px`). **Best for:** Help widgets, customer support, contextual assistants that shouldn't dominate the page. On mobile devices (viewport under 768px), the tray automatically switches to a full-screen layout for better usability. See [Mobile Behavior](/agent-builder/embed-widget/mobile-behavior). *** ## Full-screen ```javascript theme={null} embedWidget({ url: '...', type: 'full-screen' }); ``` Same floating button as the tray, but clicking it opens the agent as a **full-screen overlay**. The agent fills the entire viewport. **Best for:** Dedicated AI assistant pages, onboarding flows, or experiences where you want the agent to be the primary focus. *** ## Chatbar ```javascript theme={null} embedWidget({ url: '...', type: 'chatbar' }); ``` An **inline input bar** fixed to the bottom center of the page — no floating button. The user types directly into the bar, and a chat window opens above it. **Best for:** Search-like experiences, landing pages, AI-native products where the agent is the main interaction point. ### Chatbar-Specific Options The chatbar type accepts additional configuration through the `options` property: ```javascript theme={null} embedWidget({ url: 'https://console.thesys.dev/app/your-slug', type: 'chatbar', options: { placeholder: 'Ask me anything about our product...', keyboardShortcutEnabled: true, conversationStarters: [ 'What can you help me with?', 'Show me pricing plans', 'How do I get started?', ], }, }); ``` | Option | Type | Default | Description | | ------------------------- | -------------- | ------------------- | ------------------------------------------------------------------------------------------------ | | `placeholder` | `string` | `"Ask me anything"` | Placeholder text in the input field | | `keyboardShortcutEnabled` | `boolean` | `true` | Press **Space** anywhere on the page to focus the input (only when no other input is focused) | | `conversationStarters` | `string[]` | `[]` | Suggested prompts displayed above the input when focused | | `theme` | `ChatbarTheme` | — | Visual overrides for the chatbar. See [Customization](/agent-builder/embed-widget/customization) | ### Keyboard Shortcuts When `keyboardShortcutEnabled` is `true` (the default): * **Space** — Focuses the chatbar input (only when no other text field is active) * **Escape** — Blurs the chatbar input ### Conversation Starters When the input is focused and the chat window is closed, the starters appear as clickable chips labeled **A**, **B**, **C**, etc. Clicking one sends it as the first message and opens the chat window. *** ## Choosing a Type | Criteria | Tray | Full-screen | Chatbar | | ---------------- | --------------- | ---------------- | ------------------------------------ | | Presence on page | Floating button | Floating button | Inline input bar | | Space when open | Compact panel | Full viewport | Window above input | | Best for | Help / support | Focused AI tasks | Search / main UX | | Mobile behavior | Full-screen | Full-screen | Input stays; window goes full-screen | # Agent Builder Source: https://docs.thesys.dev/agent-builder/introduction Build, publish, and embed AI agents — then integrate them into any website or app. ## What is Agent Builder? Agent Builder is the Thesys platform for creating and publishing AI agents. You configure your agent — model, system prompt, knowledge sources, tools, and styling — through a visual editor at [console.thesys.dev](https://console.thesys.dev), then publish it as a shareable URL. Once published, you embed the agent into your own product using either the **Embed Widget** npm package or a direct **iframe**. Your users interact with the agent without ever leaving your site. ## Key Capabilities Drop a fully featured chat widget into any website with a few lines of code. Supports tray, full-screen, and chatbar layouts. Identify your own users with a signed JWT so each person gets isolated conversation history — no Thesys login required. Open, close, send messages, and prefill input programmatically. Trigger the agent from buttons, links, or application events. Theme the chatbar to match your brand with CSS variable overrides, custom placeholders, and conversation starters. ## How It Works Go to [Agent Builder](https://console.thesys.dev) and create a new project. Configure the model, system prompt, knowledge sources, and tools. Click **Share** in the editor toolbar. Your agent gets a unique URL like `https://console.thesys.dev/app/your-slug`. Install the embed widget and add it to your site: ```javascript theme={null} import { embedWidget } from 'agent-embed-widget'; import 'agent-embed-widget/dist/agent-embed-widget.css'; embedWidget({ url: 'https://console.thesys.dev/app/your-slug', }); ``` ## Next Steps * [**Getting Started**](/agent-builder/embed-widget/getting-started) — Install the widget and embed your first agent * [**Widget Types**](/agent-builder/embed-widget/widget-types) — Choose between tray, full-screen, and chatbar layouts * [**User Identity (BYOI)**](/agent-builder/byoi) — Give each user their own conversation history # Embed Source: https://docs.thesys.dev/api-reference/endpoints/embed POST /v1/embed/chat/completions A LLM native endpoint that generates UI. POST `https://api.thesys.dev/v1/embed/chat/completions` * Drop in replacement for existing LLM endpoint that can be used to generate UI rather than text completions. * Supports tool calling for fetching external data. * Ability to steer semantic design via system prompts. * Visual customization via [Crayon](https://crayonai.org) ## Request Supports both [streaming](/api-reference/objects/streaming#request-body) and [non-streaming](/api-reference/objects/message#request-body) payloads. ## Response Returns [streaming chunks](/api-reference/objects/streaming#sse-event-stream) in streaming mode or a [message](/api-reference/objects/message#response) object in non-streaming mode. # Visualize Source: https://docs.thesys.dev/api-reference/endpoints/visualize POST /v1/visualize/chat/completions Generates a UI for the given data. * Given a data object, generates the best UI representation for it. * DOES NOT support tool calling. * Ability to steer semantic design via system prompts. * Visual customization via [Crayon](https://crayonai.org) Learn how to integrate C1 Visualize with your existing LLM infrastructure in our [Two-Step Visualize Pattern guide](/guides/integration-pattern/visualize). ## Request Supports both [streaming](/api-reference/objects/streaming#request-body) and [non-streaming](/api-reference/objects/message#request-body) payloads. * Accepts data via the `messages` property. * Does not accept the `tools` property. ## Response Returns [streaming chunks](/api-reference/objects/streaming#sse-event-stream) in streaming mode or a [message](/api-reference/objects/message#response) object in non-streaming mode. # Errors Source: https://docs.thesys.dev/api-reference/errors ## HTTP Errors The following table shows the HTTP errors that can be returned by the C1 API: * 400 `Bad Request`: The request payload has errors. * 403 `Forbidden`: The request is missing a valid API key. * 404 `Not Found`: Likely a result of invalid path. * 413 `Request Entity Too Large`: Request exceeds the maximum size allowed. * 429 `Too Many Requests`: Rate limit exceeded. * 500 `Internal Server Error`: An unexpected error occurred on our side. ## Generated UI Errors In addition to HTTP errors, the C1 API may return errors within the generated response. This is handled by the UI components automatically and you do not need to handle them manually. # API Overview Source: https://docs.thesys.dev/api-reference/getting-started ## Accessing the API All API endpoints are available at `https://api.thesys.dev/v1`. ## Authentication Authentication is done via API keys. You can create API keys from the [keys page](https://console.thesys.dev/keys). API keys must be sent as a `Authorization` header with the value `Bearer `. If the API key is not valid, you will receive a 403 error. ## Rate Limits Please refer to the [Rate Limits](/api-reference/rate-limits) page for more information. ## Errors Please refer to the [Errors](/api-reference/errors) page for more information. ## Examples ### For Chat Completions API ```python python theme={null} from openai import OpenAI client = OpenAI( base_url="https://api.thesys.dev/v1/embed", api_key="" ) completion = client.chat.completions.create( model="c1/anthropic/claude-sonnet-4/v-20250930", messages=[ { "role": "user", "content": "How did the population of the world grow from 1950 to 2020?" } ]) ``` ```js node theme={null} import OpenAI from "openai"; const client = new OpenAI({ base_url="https://api.thesys.dev/v1/embed", api_key="" }); const completion = await client.chat.completions.create({ model: "c1/anthropic/claude-sonnet-4/v-20250930", messages: [ { role: "user", content: "How did the population of the world grow from 1950 to 2020?", }, ], }); ``` Stream this response via the language specific helper functions to the react client. ### For Responses API ```python python theme={null} from openai import OpenAI client = OpenAI( base_url="https://api.thesys.dev/v1/embed", api_key="" ) response = client.responses.create( model="c1/anthropic/claude-sonnet-4/v-20250930", input="How did the population of the world grow from 1950 to 2020?" ) print(response) ``` ```js node theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.thesys.dev/v1/embed", apiKey: "" }); const response = await client.responses.create({ model: "c1/anthropic/claude-sonnet-4/v-20250930", input: "How did the population of the world grow from 1950 to 2020?", }); console.log(response); ``` ## Chat Completions API For C1 Gemini models, the `reasoning_effort` parameter translates to the following max reasoning tokens: | `reasoning_effort` | 3 Flash - Max reasoning tks | 3 Pro - Max reasoning tks | | ------------------ | --------------------------- | ------------------------- | | `none` | 0 | 256 | | `minimal` | 512 | 512 | | `low` | 1024 | 1024 | | `medium` | 2048 | 2048 | | `high` | 4096 | 4096 | | `xhigh` | 8192 | 8192 | | Property | C1 OpenAI models | C1 Anthropic Models | | ----------------------- | ------------------------------------------- | ------------------------------------------- | | `messages` | (audio not supported) | (audio not supported) | | `model` | | | | `audio` | | | | `frequency_penalty` | | | | `function_call` | use `tool_choice` | use `tool_choice` | | `functions` | use `tools` | use `tools` | | `logit_bias` | | | | `logprobs` | | | | `max_completion_tokens` | | | | `max_tokens` | | | | `metadata` | `thesys` metadata only | `thesys` metadata only | | `modalities` | | | | `n` | must be `1` | must be `1` | | `parallel_tool_calls` | `true` by default | `true` by default | | `prediction` | | | | `presence_penalty` | | | | `prompt_cache_key` | | | | `reasoning_effort` | | | | `response_format` | | | | `service_tier` | | | | `stop` | | | | `stream` | | | | `temperature` | | | | `tool_choice` | | | | `tools` | | | | `top_p` | | | | `verbosity` | | | | `web_search_options` | | | ## Responses API All C1 models are supported via the Responses API, including OpenAI and non-OpenAI models. Our Responses API implementation is [Open Responses](https://www.openresponses.org/) compliant. There are three ways to maintain chat history in Responses API: 1. **Pass the full conversation history** — Include all previous messages in the `input` array with each request. 2. **Use `previous_response_id`** — Reference a prior response by its ID to automatically chain conversations (requires `store: true`). 3. **Use `conversation`** — Group related responses into a named conversation for persistent multi-turn context (requires `store: true`). To create a conversation using `previous_response_id`: ```python python theme={null} from openai import OpenAI client = OpenAI( base_url="https://api.thesys.dev/v1/embed", api_key="" ) response = client.responses.create( model="c1/anthropic/claude-sonnet-4/v-20250930", input="What is the capital of France?" ) follow_up = client.responses.create( model="c1/anthropic/claude-sonnet-4/v-20250930", input="And what is its population?", previous_response_id=response.id ) print(follow_up) ``` ```js node theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.thesys.dev/v1/embed", apiKey: "" }); const response = await client.responses.create({ model: "c1/anthropic/claude-sonnet-4/v-20250930", input: "What is the capital of France?", }); const followUp = await client.responses.create({ model: "c1/anthropic/claude-sonnet-4/v-20250930", input: "And what is its population?", previous_response_id: response.id, }); console.log(followUp); ``` To create a conversation and use it across multiple requests: ```python python theme={null} from openai import OpenAI # Client for conversation management conv_client = OpenAI( base_url="https://api.thesys.dev", api_key="" ) # Client for generation embed_client = OpenAI( base_url="https://api.thesys.dev/v1/embed", api_key="" ) conversation = conv_client.conversations.create() response = embed_client.responses.create( model="c1/anthropic/claude-sonnet-4/v-20250930", input="What is the capital of France?", store=True, conversation={"id": conversation.id} ) follow_up = embed_client.responses.create( model="c1/anthropic/claude-sonnet-4/v-20250930", input="And what is its population?", store=True, conversation={"id": conversation.id} ) print(follow_up) ``` ```js node theme={null} import OpenAI from "openai"; // Client for conversation management const convClient = new OpenAI({ baseURL: "https://api.thesys.dev", apiKey: "" }); // Client for generation const embedClient = new OpenAI({ baseURL: "https://api.thesys.dev/v1/embed", apiKey: "" }); const conversation = await convClient.conversations.create(); const response = await embedClient.responses.create({ model: "c1/anthropic/claude-sonnet-4/v-20250930", input: "What is the capital of France?", store: true, conversation: { id: conversation.id }, }); const followUp = await embedClient.responses.create({ model: "c1/anthropic/claude-sonnet-4/v-20250930", input: "And what is its population?", store: true, conversation: { id: conversation.id }, }); console.log(followUp); ``` Built-in tools (`web_search`, `file_search`, `code_interpreter`, `computer_use`, `mcp`) will be supported soon. | Property | Supported | | ------------------------ | ----------------------------------------------------------------- | | `background` | Always `false` | | `conversation` | with `store: true` | | `include` | | | `input` | | | `instructions` | | | `max_output_tokens` | | | `max_tool_calls` | | | `metadata` | `thesys` metadata only | | `model` | | | `parallel_tool_calls` | `true` by default | | `previous_response_id` | with `store: true` | | `prompt` | | | `prompt_cache_key` | | | `prompt_cache_retention` | | | `reasoning` | `effort` only | | `safety_identifier` | | | `service_tier` | | | `store` | `true` by default | | `stream` | | | `temperature` | | | `text` | `verbosity` only | | `tool_choice` | | | `tools` | `function` only; built-in tools coming soon | | `top_logprobs` | | | `top_p` | | ### `input` item types | Input Item Type | Supported | | -------------------------------------------------------------------------------- | --------------------------------- | | `message` (user/system/developer/assistant) | | | `function_call` | | | `function_call_output` | | | `item_reference` | | | `reasoning` | | | `compaction` | coming soon | | `web_search_call` | coming soon | | `file_search_call` | coming soon | | `computer_call` / `computer_call_output` | coming soon | | `code_interpreter_call` | coming soon | | `image_generation_call` | coming soon | | `local_shell_call` / `local_shell_call_output` | | | `shell_call` / `shell_call_output` | | | `apply_patch_call` / `apply_patch_call_output` | | | `mcp_list_tools` / `mcp_approval_request` / `mcp_approval_response` / `mcp_call` | coming soon | | `custom_tool_call` / `custom_tool_call_output` | | ### `text` sub-fields | Property | Supported | | ---------------- | ------------------------------------------ | | `text.verbosity` | | | `text.format` | Always Thesys format | # API Changelog Source: https://docs.thesys.dev/api-reference/model-changelog View changelogs for each API version of C1. Embed model versions: * `c1///v-20260331` For example, `c1/anthropic/claude-sonnet-4.6/v-20260331` Minimum SDK version: * @thesysai/genui-sdk: `0.9.0` * @crayonai/react-ui: `0.9.16` * @crayonai/react-core: `0.7.7` Upgrading to `v-20260331` is a **backwards incompatible** change due to a new OpenUI response format and component-level changes. **Existing threads are not affected.** Threads started on `v-20251230` or earlier will continue to function on those versions even if you change the model. The new API version is automatically routed to `v-20251230` for old threads. OpenUI responses are only supported for new threads created with `v-20260331` or later. ## OpenUI Support C1 now supports the [OpenUI Framework](https://www.openui.com/) - upto 3x faster generations compared to the JSON implementation, with upto 66% less token consumption. ## New Components This release brings in a whole set of new components. The entire library has also ben shifted to OpenUI CSS from Crayon CSS. A full list of all the new components and changes will be documented soon, with guides on CSS migration and generation prompts. ## Custom Component Migration The custom component declaration, schema declaration, and passing to the API stays the same. The only change is an additional step of passing the component schema to the GenUI SDK. Basing the instructions off the [existing guide](/guides/custom-components), the change is as follows: ### `v-20260331` version onwards Previous version [here](/guides/custom-components). Steps 1, 2, 3 stay the same. `zod` moves from version `3` to version `4`, i.e. `zodToJsonSchema` is now `z.toJSONSchema`. ```ts src/app/api/chat/route.tsx theme={null} const CUSTOM_COMPONENT_SCHEMAS = { FlightList: zodToJsonSchema(FlightListSchema), // [!code --] FlightList: z.toJSONSchema(FlightListSchema), // [!code ++] }; ``` ```ts src/app/page.tsx theme={null} import { C1Chat } from "@thesysai/genui-sdk"; import { FlightList } from "./components"; // ... export default function Home() { return ( ); } ``` Artifact model version: * `c1/artifact/v-20260130` Minimum SDK version: * @thesysai/genui-sdk: `0.8.3` * @crayonai/react-ui: `0.9.16` ## New Features * **Welcome Screen** - The `C1Chat` component now supports a configurable welcome screen displayed when a thread is empty. You can customize the title, description, and image to greet users when they start a new conversation. See the [`welcomeMessage` prop](/react-reference/c1-chat#props) for details. * **Conversation Starters** - The `C1Chat` component now supports conversation starters — clickable prompt buttons shown on the welcome screen to help users quickly begin a conversation with predefined options. Supports both `short` (pill) and `long` (list) variants. See the [`conversationStarters` prop](/react-reference/c1-chat#props) for details. * **Artifact Editing** - Users can now edit artifacts inline directly within the chat interface, enabling a more interactive workflow. For setup instructions, see [Editing Artifacts](/guides/artifacts/editing). * **Pinch Zoom in Reports** - Reports now support pinch-to-zoom functionality, making it easier to inspect details in generated reports. * **New Slide Templates** - Added new slide templates, giving more design options for generated slide presentations. See the full [Slide Templates (v-20260130)](/guides/artifacts/slides/slide-templates/v-20260130) reference for all available templates and parameters. * **PPTX Export for Slides** - Slide artifacts can now be exported as `.pptx` files, enabling seamless use in PowerPoint and other presentation tools. See the [Exporting Slides to PPTX](/guides/artifacts/pptx-export) guide for setup instructions. * **New Report Blocks** - Added new block types for reports, expanding the variety of content that can be generated within report artifacts.
VisualCards SummaryGrid
FeatureHighlight Table
NumberedKeyPoint KeyMetrics
HeadlineStatement HeroMetric
Stable embed model versions: * `c1/anthropic/claude-sonnet-4/v-20251230` * `c1/openai/gpt-5.2/v-20251230` * `c1/openai/gpt-5/v-20251230` Experimental embed model versions: * `c1-exp/anthropic/claude-sonnet-4.5/v-20251230` * `c1-exp/anthropic/claude-haiku-4.5/v-20251230` Visualise model version: * `c1/anthropic/claude-sonnet-4/v-20251230` Artifact model version: * `c1/artifact/v-20251230` Minimum SDK version: * @crayonai/react-ui: `0.9.9` * @thesysai/genui-sdk: `0.7.15` ## New Model Support C1 now supports `Claude Sonnet 4.5` and `Claude Haiku 4.5` models. These are enabled via the C1 API as **experimental** models. Production use of these models is not encouraged. ## New Features * **Tray Widget Form Factor** - The `C1Chat` component now supports a new `bottom-tray` form factor for tray widget layouts. This enables a variety of use cases, including widget-based chatbot interactions and minimizable chat interfaces.
Tag variants example 2
* **Mermaid Diagram Generation** - C1 now supports generating mermaid diagrams. This is **not** enabled by default. To use mermaid generation, users should enter a supporting system prompt: ``` You can generate mermaid diagram code in TextContent. ``` * **Enhanced Components** * **Tag variants** - C1 tags now support standard color variants: `neutral`, `info`, `success`, `warning`, `danger`. These variants can be used to suppliment semantic information with colors, and can optionally be guided via system prompts.
Tag variants example
Tag variants example 2
Tag variants example 2
Tag variants example 2
* **Image improvements** - We improved the way images are displayed, especially in wide layouts. This ensures all images are rendered with balanced aspect ratios.
Tag variants example 2
* **C1 Artifact icon generation** - We have significantly improved our icon generation process. Embed models already support this, and support has been extended to the C1 Artifact model as well.
Model versions: * `c1/anthropic/claude-sonnet-4/v-20251130` * `c1/openai/gpt-5/v-20251130` Minimum SDK version: * @crayonai/react-ui: `0.9.6` * @thesysai/genui-sdk: `0.7.5` ## New Features * **Citations and Source Links** - C1 can now include citations and links to the original sources for the facts and data it presents. This adds credibility to the data and allows the user to verify the information.
Citations and Source Links Example
Citations and Source Links Example 2
* **Enhanced Interactivity** * **Action Column for Tables** - C1 can now generate a dedicated action column in tables, with buttons and actions that are specific to each row. This makes it easier than ever for users to act on data directly where they see it, from viewing details to deleting an entry.
Action Column for Tables Example
* **Expanded Support for Actions** - C1 can now attach continue conversation, custom actions and open\_url actions to Icon Buttons and List Items. This provides more design flexibility to place actions in more intuitive, contextual locations within your UI.
List with Actions
* **Carousel Footer for Actions** - Carousel items can now have a footer for action buttons. The buttons are aligned across the bottom of the carousel item.
Carousel Footer for Actions Example
* **'Explore' Button** - C1 can now add an optional "explore" button next to choices in checkboxes, radio groups, and switches. This allows a user to click for more information about a specific option, perfect for complex forms or when choices require more explanation.
Explore Button Example
Not included by default, but can be added by using the `c1_included_components` metadata. ```ts theme={null} const runToolResponse = openAIClient.chat.completions.runTools({ // ... other options metadata: { thesys: JSON.stringify({ c1_included_components: ["ExploreButton"], }), }, }); ``` * **New Chart Type: Donut Chart** - We have added Donut Chart to the library of charts in C1. This is a variant of the Pie Chart that is displayed as a donut shape.
Donut Chart Example
* **New: Condensed Charts** - Perfect for dashboards and tight spaces, C1 can now generate condensed versions of Bar, Area, and Line charts. These minimalist charts are designed to convey trends and data at a glance without taking up too much screen real estate.
Condensed Charts Example
* **Smarter Icon Generation** : We've significantly improved our icon generation logic to be more reliable. This fix completely eliminates cases of missing icons in the UI.
Model versions: * `c1/anthropic/claude-sonnet-4/v-20250930` * `c1/openai/gpt-5/v-20250930` Minimum SDK version: * @crayonai/react-ui: `0.8.41` * @thesysai/genui-sdk: `0.6.40` ## New Features * **Custom Actions** - We now allow C1 users to define custom actions that can be triggered from the C1 UI. For more details and examples, refer to [Custom Actions](/guides/custom-actions). * **New Components** - We have added the following new components: * **Single stacked bar chart** - A horizontal bar chart that represents a whole divided into proportional segments. Each segment (or “slice”) corresponds to a specific category and visually depicts its percentage contribution to the total. Single stacked bar chart Example * **Scatter chart** - A chart that plots data points to show the relationship between two variables. It helps identify trends, correlations, and outliers in the data. Scatter chart Example * **Redesigned Component** - We have redesigned the Carousel, Table and Slider components to be more responsive and user-friendly. Model versions: * `c1/anthropic/claude-sonnet-4/v-20250915` * `c1/openai/gpt-5/v-20250915` Minimum SDK version: * @crayonai/react-ui: `0.8.31` * @thesysai/genui-sdk: `0.6.34` ## New Features * **Bring Your Own Components** - We now allow C1 users to provide their own custom React components for generation as a part of the C1 API and GenUI SDK. For more details and examples, refer to [Custom Components](/guides/custom-components). * **New Stable Models** - With `v-20250915` we are introducing GPT-5 as a stable offering. Older model strings for GPT-5 experimental (`c1-exp/openai/gpt-5/`) will no longer be maintained, and all new features and bug fixes will now be released for the stable versions. * **Foldable Sections** - We updated our sections to now allow foldable sections. The C1 API decides whether to use foldable sections or not at runtime based on the content and length of the section. Callout Example * **Inline Headers** - Components now have an optional inline header, allowing for more context to be given with the response. Callout Example ## Bug Fixes / Improvements * Form inputs are now disabled until the assistant response is fully generated. * Improved component usage of `c1/anthropic/claude-sonnet-4/v-20250915` and `c1/openai/gpt-5/v-20250915` * Improved internal error handling of the C1 API Model versions: * `c1/anthropic/claude-sonnet-4/v-20250831` * `c1-exp/openai/gpt-5/v-20250831` Minimum SDK version: * @crayonai/react-ui:"0.8.27" * @thesysai/genui-sdk: "0.6.32" ## New Features * **Editable Table** - Added support for an inline multi edit component that allows changing multiple records at once. Not included by default, but can be added by using the `c1_included_components` metadata. Editable Table Example ```ts {3-6} [expandable] theme={null} const runToolResponse = openAIClient.chat.completions.runTools({ // ... other options metadata: { thesys: JSON.stringify({ c1_included_components: ["EditableTable"], }), }, }); ``` * **Improved List** - We refined the list component by removing unclear interactive behavior, improving visual clarity, and adding support for multiple types: images, icons, and numbers. List Example * **Improved Callout** - We updated the callout visually to feel more contextual and natural specifically within conversation-based interfaces, improving clarity and highlighting key information more effectively. Callout Example * **Form validations** - Generated forms can optionally have validation rules applied to them. Form Example * **Improved Error Handling** - SDK now allows passing an `onError` prop to the `C1Component` to handle errors. ```tsx {3-5} [expandable] theme={null} import { C1Component } from "@thesysai/genui-sdk"; ... { console.error(`Generated UI error: ${code}, ${c1Response}`); }} /> ``` ## Bug Fixes / Improvements * Improved carousel responsiveness to adjust for different screen sizes. Deprecation Notice: From this version onwards Anthropic Sonnet 3.5 and Sonnet 3.7 will not be supported since Anthropic has announced EOL for these models and actively suggests using Sonnet 4 instead. *** Model versions: * `c1/anthropic/claude-sonnet-4/v-20250815` * `c1/anthropic/claude-3.7-sonnet/v-20250815` * `c1/anthropic/claude-3.5-sonnet/v-20250815` ## UI * Added support for advance layouting options Layout Example * Added new mini-card component Mini-card Example * Advanced layouting options can be disabled by using metadata. Pass `c1_excluded_components` in the metadata to exclude the component. This might be useful if you are building a copilot or width-constrained agent. ```ts focus={3-7} theme={null} const runToolResponse = openAIClient.chat.completions.runTools({ // ... other options metadata: { thesys: JSON.stringify({ c1_excluded_components: ["Layout"], }), }, }); ``` * Added optional description for switch, radio and checkbox components - this is helpful for explaining each option to the user before they make a choice. Options Description Example ## Bug Fixes / Improvements * Fixed a bug where carousels appeared visually cluttered. *** *** Model versions: * `c1/anthropic/claude-3.5-sonnet/v-20250709` * Added new 'Section' and 'Horizontal Stacked Bar Chart' components * Updated graph schema to significantly reduce token usage * Various fixes: * Improved KaTeX rendering in markdown * Fixed chart issues with negative and very large numbers * Corrected Y-axis label display in charts * Fixed datepicker value saving and submission *** # Models & Compatibility Source: https://docs.thesys.dev/api-reference/models-and-compatibility ## Models & Pricing C1 is family of API endpoints that use different underlying LLM models. The pricing is based on the model family and the amount of tokens used. Pricing is based on model name irrespective of the kind of endpoint you use. For example, using the [embed](/api-reference/endpoints/embed) endpoint with `c1/anthropic/claude-sonnet-4/v-*` is priced the same as using the [visualize](/api-reference/endpoints/visualize) endpoint with the same model. Generally speaking, a model name is more than just the model of the LLM it uses. It also includes the version of the model, the set of components available for use in the UI, and any other metadata. This guide is called "Models" and not "API Versions" because typically you'd pass the API version to the `model` parameter in the request to the LLM compatible endpoints of C1. ## Model Versions & SDK Requirements Thesys supports all major models — including every model from OpenAI, Anthropic, and Google, as well as the full catalog of models available via OpenRouter. All models use the same model string format: `c1/provider/model/v-*` * `c1` prefix: marks the request as a C1 Generative UI request * `provider/model`: standard provider/model path (follows the OpenRouter naming convention) * `v-*`: the Thesys API version — see [API Changelog](/api-reference/model-changelog) for details on each release Only `v-20251230` is currently supported for OpenRouter models. ### Model Coverage **OpenAI, Anthropic & Google** are accessed through first-party APIs, giving you full compliance coverage, zero data retention guarantees, and rigorous quality assurance. These are the primary recommended providers and the ones we actively test and optimise against. See the [Recommended Models](#recommended-models) section for a curated list. For example, to use Claude Opus 4.6: `c1/anthropic/claude-opus-4.6/v-20251230`. **All other models** are available via OpenRouter — any model in the OpenRouter catalog works with the same `c1/provider/model/v-*` format. You can configure provider constraints such as ZDR and data residency via the `provider` parameter in your API request — see [OpenRouter Provider Routing](https://openrouter.ai/docs/guides/routing/provider-selection). For example, to use Qwen 3.5: `c1/qwen/qwen3.5-397b-a17b/v-20251230`. Compliance and availability are not guaranteed for OpenRouter-routed models. OpenRouter may route requests across multiple underlying providers, each with their own data retention and availability policies, which are outside of Thesys's control. You can use the `provider` parameter to restrict routing to specific providers or enforce constraints such as ZDR — see [OpenRouter Provider Routing](https://openrouter.ai/docs/guides/routing/provider-selection) for configuration options. For compliance-sensitive workloads, use OpenAI, Anthropic, or Google models via the Thesys API. ### Recommended Models #### Embed Endpoint Models **Model:** `c1/anthropic/claude-sonnet-4.6/v-20260331` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.9.0 @crayonai/react-ui@~0.9.16 @crayonai/react-core@~0.7.7 ``` **Model:** `c1/openai/gpt-5.2/v-20260331` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.9.0 @crayonai/react-ui@~0.9.16 @crayonai/react-core@~0.7.7 ``` **Model:** `c1/anthropic/claude-sonnet-4/v-20251230` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.7.15 @crayonai/react-ui@~0.9.9 @crayonai/react-core@~0.7.6 ``` **Model:** `c1/openai/gpt-5.2/v-20251230` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.7.15 @crayonai/react-ui@~0.9.9 @crayonai/react-core@~0.7.6 ``` **Model:** `c1/openai/gpt-5/v-20251230` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.7.15 @crayonai/react-ui@~0.9.9 @crayonai/react-core@~0.7.6 ``` **Model:** `c1/google/gemini-3-pro/v-20251230` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.7.15 @crayonai/react-ui@~0.9.9 @crayonai/react-core@~0.7.6 ``` Reasoning for Gemini 3 Pro cannot be disabled, which increases the Time-to-First-Bite (TTFB). This might lead to high user-perceived latency. **Model:** `c1/google/gemini-3-flash/v-20251230` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.7.15 @crayonai/react-ui@~0.9.9 @crayonai/react-core@~0.7.6 ``` #### Visualize Endpoint Model **Model:** `c1/anthropic/claude-sonnet-4/v-20251230` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.7.15 @crayonai/react-ui@~0.9.9 @crayonai/react-core@~0.7.6 ``` #### Artifact Endpoint Model **Model:** `c1/artifact/v-20260130` **Install Dependencies:** ```bash theme={null} npm install @thesysai/genui-sdk@~0.8.3 @crayonai/react-ui@~0.9.16 @crayonai/react-core@~0.7.6 ``` #### Free Models Requests and responses for free models will be stored and might be used to improve services and offerings. **Model:** `c1/google/gemini-3.1-pro-free/v-20260331` **Model:** `c1/google/gemini-3.1-flash-lite-free/v-20260331` ### Other Model Versions | C1 Model (Latest Version) | genui-sdk | react-ui | react-core | | :------------------------------------------ | :-------- | :------- | :--------- | | `c1/anthropic/claude-3.5-sonnet/v-20250709` | \~0.6.27 | \~0.8.14 | \~0.7.6 | | `c1/anthropic/claude-3.7-sonnet/v-20250709` | \~0.6.27 | \~0.8.14 | \~0.7.6 | | `c1-latest` | -- | -- | -- | | `c1-202504XX*` | -- | -- | -- | ## Model Specifications These specifications apply to all versions of each model family. | C1 Model | LLM | Input Price / M tokens | Cached Input (Read) | Cached Input (Write) | Output Price / M tokens | Context Window | Max Output | | :----------------------------------- | :--------- | :--------------------- | :------------------ | :------------------- | :---------------------- | :------------- | :--------- | | `c1/anthropic/claude-sonnet-4` | Sonnet 4 | \$3.00 | \$0.30 | \$3.75 | \$15.00 | 200K | 64K | | `c1/openai/gpt-5.2` | GPT 5.2 | \$1.75 | \$0.175 | -- | \$14.00 | 400K | 128K | | `c1/openai/gpt-5` | GPT 5 | \$1.25 | \$0.125 | -- | \$10.50 | 400K | 128K | | `c1-exp/anthropic/claude-sonnet-4.5` | Sonnet 4.5 | \$3.00 | \$0.30 | \$3.75 | \$15.00 | 200K | 64K | | `c1-exp/anthropic/claude-haiku-4.5` | Haiku 4.5 | \$1.00 | \$0.10 | \$1.25 | \$5.00 | 200K | 64K | | `c1-exp/anthropic/claude-3.5-haiku` | 3.5 Haiku | \$0.80 | \$0.80 | \$1.00 | \$4.00 | 200K | 8192 | | `c1-exp/openai/gpt-4.1` | GPT 4.1 | \$2.00 | \$0.50 | -- | \$8.00 | 1M | 32K | # Messages Source: https://docs.thesys.dev/api-reference/objects/message UI completions are returned by the API when `stream=false` is set in the request. ## Request Headers The API key to use for the request. The content type of the request. Should be `application/json`. ## Request Body An array of messages of the conversation so far. The role of the message. Can be either `user`, `assistant`, or `tool`. The content of the message. The ID of the tool call. Only present if the role is `tool`. The model to use for the UI completion. Should be one of the models listed in the [Models](/api-reference/models-and-compatibility) page. The temperature to use for the UI completion. Should be between 0 and 1. The top-p value to use for the UI completion. Should be between 0 and 1. The maximum number of tokens to use for the UI completion. The number of completions to generate. Should be unset or set to 1. Whether to stream the response. Should be unset or `false`. The parameters above are commonly used with the Messages API. For a comprehensive list of all supported parameters and their compatibility across different model providers (OpenAI vs Anthropic), see the [Supported API Parameters](/api-reference/getting-started#supported-api-parameters) table. ```sh curl theme={null} curl -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model": "c1/anthropic/claude-sonnet-4/v-20251230", "messages": [{"role": "user", "content": "Hello, world!"}]}' \ https://api.thesys.dev/v1/embed/chat/completions ``` ```json theme={null} { "id": "chatcmpl-1743157633416-cffw7tgswx", "object": "chat.completion", "created": 1743157633, "model": "c1/anthropic/claude-sonnet-4/v-20251230", "choices": [ { "index": 0, "message": { "role": "assistant", "content": { ... } }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 8, "completion_tokens": 439, "total_tokens": 447 } } ``` ## Response A unique identifier for the UI completion An array of completions. Will always contain exactly one object. The reason the generation stopped. Can be either `stop` or `tool_calls`. Completion message generated by the model. The role of the message. Will always be `assistant` for generations. A JSON object containing the UI completion. The timestamp when the UI completion was created The model used to generate the UI completion The object type, which is always `chat.completion` An object containing the usage statistics for the UI completion Number of tokens in the prompt Number of tokens in the generated UI completion Total number of tokens: `prompt_tokens + completion_tokens` # Streaming Chunks Source: https://docs.thesys.dev/api-reference/objects/streaming Streaming message chunks are returned by the API when `stream=true` is set in the request. ## Request Headers The API key to use for the request. The content type of the request. Should be `application/json`. ## Request Body An array of messages of the conversation so far. The role of the message. Can be either `user`, `assistant`, or `tool`. The content of the message. The ID of the tool call. Only present if the role is `tool`. The model to use for the UI completion. Should be one of the models listed in the [Models](/api-reference/models-and-compatibility) page. The temperature to use for the UI completion. Should be between 0 and 1. The top-p value to use for the UI completion. Should be between 0 and 1. The maximum number of tokens to use for the UI completion. The number of completions to generate. Should be unset or set to 1. Whether to stream the response. Should be `true` for streaming. The parameters above are commonly used with the Messages API. For a comprehensive list of all supported parameters and their compatibility across different model providers (OpenAI vs Anthropic), see the [Supported API Parameters](/api-reference/getting-started#supported-api-parameters) table. ```sh curl theme={null} curl -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model": "c1/anthropic/claude-sonnet-4/v-20251230", "stream": true, "messages": [{"role": "user", "content": "Hello, world!"}]}' \ https://api.thesys.dev/v1/embed/chat/completions ``` ``` id: 1 data: {"id":"chatcmpl-1743157601318-pzx9ougakm","object":"chat.completion.chunk","created":1743157601,"model":"c1/anthropic/claude-sonnet-4/v-20251230","choices":[{"index":0,"delta":{"role":"assistant","content":{...}}]} id: 2 data: {"id":"chatcmpl-1743157601318-pzx9ougakm","object":"chat.completion.chunk","created":1743157601,"model":"c1/anthropic/claude-sonnet-4/v-20251230","choices":[{"index":0,"delta":{"content":{...}}]} ``` ## SSE Event Stream A identifier for this event. One of the following objects: * [Completion Chunk](#completion-chunk) ## Completion Chunk A unique identifier for the UI completion. Will be the same in all events for a given completion. The object type, which is always `chat.completion.chunk` Timestamp for when the chunk was created. The model used to generate the UI completion. An object containing the usage statistics for the UI completion Only present if the generation is complete. Number of tokens in the prompt Number of tokens in the generated UI completion Total number of tokens: `prompt_tokens + completion_tokens` An array of completions. Will always contain exactly one object. The reason the generation stopped. Can be either `stop` or `tool_calls`. Present only if the generation is complete. A JSON object containing the UI completion. # Rate Limits Source: https://docs.thesys.dev/api-reference/rate-limits Like most other LLM API providers, we have 2 sets of rate limits to prevent abuse and manage our capacity. * Spend limits: We limit the max amount of money you can spend in a month. * Usage limits: We limit the number of requests you can make within a time window. ## Spend Limits Spend limits are set per organization per month. We are working on adding support for setting spend limits automatically from the developer console but for now, you can contact us at `support@thesys.dev` to set a spend limit for your organization. Billing is done on a monthly basis or a cumulative monthly spend of \$100, whichever comes first. ## Usage Limits * Usage limits are also set per organization irrespective of the number of API key used. * Limits are enforced at both `ITPM` (Input Tokens Per Minute) and `OTPM` (Output Tokens Per Minute) * If you hit the rate limit, you will receive a 429 error. If you need higher limits, please contact us at `support@thesys.dev` # Build an AI Financial Data Copilot Source: https://docs.thesys.dev/examples/analytics-with-c1 Learn how to build an AI-powered data copilot with generative UI and real-time financial data Build an intelligent financial data copilot that generates custom visualizations and insights based on natural language queries. This guide teaches you how to combine financial APIs with C1 to create dynamic charts, tables, and insights.