> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thesys.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# PostMessage Protocol

> Low-level postMessage API for communicating with a Thesys agent embedded via iframe.

<Note>
  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.
</Note>

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
);
```

<Warning>
  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.
</Warning>

### 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;
  }
});
```

<Warning>
  Always validate `event.origin` against the expected iframe origin to prevent unauthorized windows from triggering actions.
</Warning>

### 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                                                                                                                                                        |

<Tip>
  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.
</Tip>

***

## 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}
<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>
```
