Skip to main content
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:
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

TypePayloadDescription
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:
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

TypePayloadDescription
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).
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 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.
// 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.
  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 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:
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:
ParameterValueDescription
IDENTITY_TOKENJWT stringSets the initial identity token for BYOI
HIDE_LOGIN"true"Hides the Thesys login UI
appRenderContextTRAY_EMBED_WIDGET | FULLSCREEN_EMBED_WIDGET | CHATBAR_EMBED_WIDGETTells the agent which embed layout context it’s running in
<iframe
  id="thesys-agent"
  src="https://console.thesys.dev/app/your-slug?IDENTITY_TOKEN=eyJ...&HIDE_LOGIN=true"
  style="width: 100%; height: 600px; border: none;"
></iframe>