> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/en/serve/web-chat-sdk.md).

# Web Chat Embedding and SDK

Embed MaiAgent Web Chat on any website: embed code, window modes, user identification, the full configuration reference, and the JavaScript SDK API

Add an embed snippet to your page to place a MaiAgent AI Agent on your website. The snippet loads `embed.min.js` (referred to below as the SDK). The SDK creates a chat button and an iframe chat window on the page, and exposes a global `MaiAgent` JavaScript API for programmatic control.

This page is the complete technical reference for embedding Web Chat. Related topics:

* [Integrating MaiGPT: Choose the Right Agent and Fill In Capabilities](/tech/en/work/maigpt-system-integration-guide/maigpt-integration.md) — the MaiGPT product vs. the MaiGPT window mode, and how to let an embedded Agent generate images and use tools
* [Embedding Web Chat in MaiGPT Mode](/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md) — a full-featured, ChatGPT-style conversation interface
* [Injecting Page Context into Web Chat](/tech/en/serve/web-chat-sdk/web-chat-context-data.md) — inject page information into the LLM System Prompt
* [Contact Identity Sync and Token Refresh](/tech/en/serve/contacts/contact-credentials-sync.md) — create contacts and credentials from your backend
* [Software Vendor Integration Guide](/tech/en/serve/vendor-integration-guide.md) — the complete journey of embedding plus permission integration (batch backfill, login sync, logout revocation)

## 1. Quick Start <a href="#quickstart" id="quickstart"></a>

Add the following before `</body>` on your page:

```html
<script>
  window.maiagentChatbotConfig = {
    webChatId: 'your-web-chat-id',
    baseUrl: 'https://chat.maiagent.ai/web-chats',
    primaryColor: '#1890ff',
  }
</script>
<script src="https://chat.maiagent.ai/js/embed.min.js" defer></script>
```

* Get `webChatId` from the Web Chat settings page in the admin console (see [Connect a Channel: Website](https://docs.maiagent.ai/build/choose-channel/web-chat/website); the embed code generated by the console fills it in automatically)
* `baseUrl` and the SDK URL use the SaaS environment (`chat.maiagent.ai`) as an example; for private cloud or on-premises deployments, replace them with your environment's domain
* `window.maiagentChatbotConfig` **must be defined before the SDK script loads**

Default behavior: a chat button appears at the bottom right of the page, and clicking it opens a floating chat window. Users can switch between the floating and sidebar window modes.

### Initialization Timing <a href="#init-timing" id="init-timing"></a>

After loading, the SDK registers a `document.body.onload` handler to run initialization, so no manual intervention is normally needed. When you need to control initialization timing (for example, when an SPA container mounts, or for deferred rendering):

1. **Defer script loading**: insert the `<script>` tag for `embed.min.js` dynamically at the right moment
2. **Trigger manually**: set `window.maiagentChatbotConfig` first, then call `document.body.onload()`

The SDK initializes only once per page; repeated loads are ignored.

## 2. Window Modes <a href="#window-modes" id="window-modes"></a>

The `enabledWindowModes` array determines which window modes are available to users, and **the first element is the default mode**. If not set, it is equivalent to `['floating', 'sidebar']`.

| Mode       | Form                                                                                                                                                           | Suitable for                                                                                                                          |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `floating` | A floating window overlaid at the bottom right of the page; the button is draggable and the window is resizable (320×400 to 900×900 px)                        | General customer service and consultation                                                                                             |
| `sidebar`  | A sidebar docked to the right edge of the window (320–800 px wide, resizable by dragging); page content shrinks automatically to make room                     | Work scenarios where users chat while viewing the page                                                                                |
| `inline`   | The chat window is placed directly in a container you specify (`container`), fills the container's width and height, is always visible, and has no chat button | A fixed conversation area on a page, such as a support center page or a product page sidebar                                          |
| `maigpt`   | A full ChatGPT-style interface (conversation history sidebar, search, settings) that fills the specified container or the full screen                          | The main feature area of a page or a site-wide AI entry point; see [MaiGPT mode](/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md) |

```javascript
// Allow sidebar mode only
window.maiagentChatbotConfig = {
  webChatId: 'your-web-chat-id',
  baseUrl: 'https://chat.maiagent.ai/web-chats',
  enabledWindowModes: ['sidebar'],
}
```

* When the array contains two or more modes, a mode switch button appears in the chat window; set `allowWindowModeSwitch: false` to hide it
* When `maigpt` is the first element, the window enters MaiGPT mode and the remaining elements have no effect (that mode does not offer switching)
* `inline` can only be used alone (`enabledWindowModes: ['inline']`); if it is placed in the same array as `floating` or `sidebar`, nothing is embedded and the console shows an error
* The legacy field `defaultWindowMode` is deprecated: it still works (treated as `enabledWindowModes: [that value]`), but a deprecation warning appears in the console

### The Container for inline Mode <a href="#inline-container" id="inline-container"></a>

`inline` mode requires `container` to specify where to embed. Give the container a height yourself (the chat window has a minimum height of 400 px):

```html
<div id="chat-container" style="height: 640px;"></div>

<script>
  window.maiagentChatbotConfig = {
    webChatId: 'your-web-chat-id',
    baseUrl: 'https://chat.maiagent.ai/web-chats',
    enabledWindowModes: ['inline'],
    container: '#chat-container',
  }
</script>
<script src="https://chat.maiagent.ai/js/embed.min.js" defer></script>
```

| `container` value                                   | Behavior                                                                                                |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| A CSS selector string (such as `'#chat-container'`) | Waits for the element to appear, up to 5 seconds                                                        |
| An HTMLElement, or `{ element }`                    | Used directly; the element must already be attached to the page                                         |
| `{ selector, waitForElement, waitTimeout }`         | `waitForElement: false` disables waiting; `waitTimeout` adjusts the wait in milliseconds (default 5000) |

* If `container` is not set, the selector is wrong, or the element never appears, the chat window does not appear and the console explains why
* If the container is `position: static`, the SDK changes it to `position: relative`
* inline mode is always visible and has no open/close toggle: `MaiAgent.control.open()` / `close()` have no effect

### What floating, sidebar, and maigpt Look Like <a href="#window-modes-gallery" id="window-modes-gallery"></a>

{% tabs %}
{% tab title="floating window" %}

<figure><img src="https://3415477754-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNBTi475lqozGpB7xObpE%2Fuploads%2Fgit-blob-56a6936e787340e6429586677d8be3bacc08b308%2Fwebchat-mode-floating.png?alt=media" alt="floating mode: the chat window overlaid at the bottom right of the page"><figcaption><p>floating: the chat window is overlaid at the bottom right of the page without affecting the page layout, and the button can be dragged to reposition it</p></figcaption></figure>
{% endtab %}

{% tab title="sidebar" %}

<figure><img src="https://3415477754-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNBTi475lqozGpB7xObpE%2Fuploads%2Fgit-blob-4d989af74c4a47e70c3d61a6f6f4b496a31f6a09%2Fwebchat-mode-sidebar.png?alt=media" alt="sidebar mode: the chat window is docked to the right and page content shrinks automatically"><figcaption><p>sidebar: the chat window is docked to the right edge of the window and page content shrinks automatically to make room, which suits chatting while viewing the page</p></figcaption></figure>
{% endtab %}

{% tab title="maigpt full interface" %}

<figure><img src="https://3415477754-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNBTi475lqozGpB7xObpE%2Fuploads%2Fgit-blob-aab515bf01f9122d1b558aeaefcd1e59079466a0%2Fwebchat-maigpt-mode-a-container.png?alt=media" alt="maigpt mode: a ChatGPT-style full conversation interface"><figcaption><p>maigpt: a ChatGPT-style full interface (including the conversation history sidebar); see <a href="/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md">Embedding in MaiGPT Mode</a> for details</p></figcaption></figure>
{% endtab %}
{% endtabs %}

## 3. Identifying Users <a href="#identify-users" id="identify-users"></a>

Depending on whether Web Chat is bound to a contact (Contact), there are three identity levels:

| Level               | How to set it                                                                                                                                        | Conversation history                          | AI personalization / calling tools with the user's permissions |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------- |
| Anonymous           | Set nothing                                                                                                                                          | Kept per browser                              | ✖                                                              |
| Frontend auth       | Pass `auth: { sourceId, ... }` in config; the SDK creates or matches a contact automatically                                                         | Follows the contact, available across devices | ✔                                                              |
| Backend integration | Your backend calls the [identity sync API](/tech/en/serve/contacts/contact-credentials-sync.md) to get a `contactId`, and config carries `contactId` | Follows the contact, available across devices | ✔                                                              |

**Frontend auth**: once the SDK finishes initializing, it automatically calls MaiAgent's identity sync API (`setup-contact-credentials`) with the contents of `auth`, creating or matching a contact by `sourceId`:

```javascript
window.maiagentChatbotConfig = {
  webChatId: 'your-web-chat-id',
  baseUrl: 'https://chat.maiagent.ai/web-chats',
  auth: {
    sourceId: 'user-12345',   // Required: the unique identifier of this user in your system
    name: 'Alex Wang',        // Optional: the contact's display name (only takes effect on first creation)
    contextData: { ... },     // Optional: injected into the LLM System Prompt; see the contextData page
    ts: 1785500000,           // Required when signature verification is enabled: Unix seconds (UTC), generated by your backend
    sig: '9f2c...',           // Required when signature verification is enabled: HMAC-SHA256 signature; see "Signature Verification" below
    getToken: async () => ({ accessToken, organizationId, sourceId }),  // Used only for "member identity embedding": your backend issues a short-lived member token and the SDK renews it automatically
  },
}
```

`getToken` (or the static `accessToken` plus `organizationId`) is used only when embedding the organization's built-in MaiGPT for organization members. In that case `sourceId` must be the member's user ID; see [Member Identity Embedding](/tech/en/work/maigpt-system-integration-guide/maigpt-integration.md#member-embed) for the complete flow. Identifying ordinary customers or visitors does not require these fields.

If the Web Chat's login settings have "Allow the embedding side to chat without login using a Source ID" enabled with signature mode selected, `ts` and `sig` are required, and users are redirected to the login page when they are missing or fail verification. Manually calling `MaiAgent.auth.setup()` accepts these two fields as well.

**Backend integration**: when the user logs in, your backend calls the same API (which can also bind MCP tool credentials), stores the returned `contactId` in the member record, and the frontend carries only `contactId`.

How to choose between the two:

* If you only need to "recognize the user and keep conversations across devices" → **frontend auth** is the simplest
* If you need to bind the user's Access Token into MCP tool credentials (so the AI calls your API with the user's permissions) and do not want the token to appear in the page's frontend source → **backend integration**
* Both use the same underlying contact mechanism: the same `sourceId` is treated as the same contact, so you can mix them

{% hint style="info" %}
You can also use `queryMetadata` to control the knowledge base retrieval scope for that user (RAG permission filtering); see [Knowledge Management Permissions (Query Metadata) Overview](/tech/en/identity-and-access/zhi-shi-guan-li-quan-xian-query-metadata-cha-xun-yuan-zi-liao-zong-lan.md). `queryMetadata` does not enter the LLM prompt; to let the AI "know" a value, use [contextData](/tech/en/serve/web-chat-sdk/web-chat-context-data.md).
{% endhint %}

### Security Considerations <a href="#identity-security" id="identity-security"></a>

{% hint style="danger" %}
**`contactId` is an identity credential**: anyone who obtains it can chat as that user and see their conversation history. Output `contactId` only to the user themselves on pages **after login**, and never put it in the source of public pages.

The requirements for `auth.sourceId` depend on whether signature verification is enabled:

* **Signature verification not enabled**: identity sync is a public endpoint, so a guessable `sourceId` means an identity that can be impersonated. Use an unguessable value (such as a UUID), and do not use sequential numbers, email addresses, or phone numbers.
* **Signature verification enabled**: security comes from the signature rather than from `sourceId` itself, so readable values such as student IDs, employee IDs, and registered email addresses can be used directly. The secret exists only on your server; without it, even someone who knows the `sourceId` cannot compute a valid signature.
  {% endhint %}

### Signature Verification <a href="#source-id-signature" id="source-id-signature"></a>

In Web Chat's login settings you can enable "Allow the embedding side to chat without login using a Source ID", so users who are already logged in to your website do not have to log in again in the chat window. The switch, secret, signature validity period, and verification failure log are on the login settings tab in the admin console; for how to operate them, see [Chat Without Login Using a Contact Source ID](https://docs.maiagent.ai/build/choose-channel/web-chat/source-id-access) in the user manual.

There are two verification methods, and signature mode is the recommended one:

| Verification method            | Behavior                                                                                                                    |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| Require signature verification | `ts` and `sig` are required; access is allowed only if verification passes                                                  |
| Source ID only                 | No verification; access is allowed as long as `sourceId` is provided. Suitable only for testing or closed internal networks |

**Signature algorithm**

```
HMAC-SHA256(secret, "{webChatId}.{sourceId}.{ts}")
```

Pass it as a lowercase hex string. `ts` is Unix seconds (UTC). The platform generates the secret in the admin console; it may exist only in your server's environment variables and must not be written into frontend code.

{% tabs %}
{% tab title="PHP" %}

```php
$ts  = time();
$sig = hash_hmac('sha256', "{$webChatId}.{$sourceId}.{$ts}", $SIGNING_SECRET);
```

{% endtab %}

{% tab title="Node" %}

```javascript
const ts = Math.floor(Date.now() / 1000);
const sig = crypto.createHmac('sha256', SIGNING_SECRET)
  .update(`${webChatId}.${sourceId}.${ts}`).digest('hex');
```

{% endtab %}

{% tab title="Python" %}

```python
ts = int(time.time())
sig = hmac.new(SIGNING_SECRET.encode(),
  f"{web_chat_id}.{source_id}.{ts}".encode(), hashlib.sha256).hexdigest()
```

{% endtab %}
{% endtabs %}

**Passing the signature from the frontend**

Hand the `ts` and `sig` computed by your backend to the SDK together with `sourceId`. If the user's identity is known when the page loads, put them in `auth` in config; if the signature is obtained only after login in an SPA, call `MaiAgent.auth.setup()`:

```javascript
// Option 1: put it in config; auth.setup() runs automatically once the SDK is ready
window.maiagentChatbotConfig = {
  webChatId: 'your-web-chat-id',
  baseUrl: 'https://chat.maiagent.ai/web-chats',
  auth: { sourceId, name, ts, sig },
}

// Option 2: call it manually once the SDK is ready
MaiAgent.auth.setup({ sourceId, name, ts, sig })
```

{% hint style="warning" %}
If the frontend example generated under "Integration Examples" in the admin console shows `setupAuth`, use `auth.setup` instead. The SDK exposes no `setupAuth` method, so pasting it as is throws a TypeError in the browser.
{% endhint %}

**Verification rules**

| Rule               | Description                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
| Validity period    | Configurable in the admin console as 1, 5, or 15 minutes; the default is 5 minutes                                    |
| Clock tolerance    | A 60-second skew is allowed; any timestamp more than 60 seconds in the future is rejected                             |
| Single use         | Each signature can be used successfully only once; the signature itself serves as the anti-replay nonce               |
| Platform binding   | The signature includes `webChatId`, so signatures for other Web Chats in the same organization cannot be reused       |
| Rejection response | All failure reasons produce the same external response, without revealing the reason or whether the `sourceId` exists |

{% hint style="warning" %}
A signature is valid for a single use, so **recompute it on every entry and never cache it**. The validity period limits how long you have to exchange this signature for a contact identity; it is not the lifetime of the conversation. Once the identity has been obtained, the conversation is not interrupted when the signature expires.
{% endhint %}

**How the validity period relates to logout**

The `contactId` you obtain has no expiration time of its own, and the platform currently has no endpoint to invalidate it. After a user logs out of your website, what you control is to stop issuing new signatures. The frontend can call `MaiAgent.auth.signOut()` to return the chat window to anonymous; for revoking tool credentials, see [Logout and Credential Revocation](/tech/en/serve/contacts/contact-credentials-sync.md#logout-revoke).

**Troubleshooting verification failures**

"View verification failure log" in the admin console's login settings lists recent failure events and their reasons. For the common causes of each reason, see [Verification Failure Log](https://docs.maiagent.ai/build/choose-channel/web-chat/source-id-access#verification-failures) in the user manual. The most common one, "signature mismatch", is usually caused by inconsistent secrets or an incorrectly assembled signature string (the separator is an ASCII period).

### Allowed Embed Origins <a href="#embed-origin-allowlist" id="embed-origin-allowlist"></a>

Conversation platform administrators can fill in their own list under <mark style="color:blue;">Basic Settings</mark> → <mark style="color:blue;">Allowed Embed Origins</mark> in the admin console; for the steps, see [Allowed Embed Origins](https://docs.maiagent.ai/build/conversation-settings/platform-settings/basic-settings#allowed-embed-origins) in the user manual. When the list is not empty, it restricts two things at the same time:

* **Embedding**: the browser blocks embedding from sites not on the list.
* **Identity sync**: both `auth` and setup-contact-credentials must pass the origin check. Server-to-server calls have no browser `Origin` header, so include `embedOrigin` in the body; see [Contact Identity Sync and Token Refresh](/tech/en/serve/contacts/contact-credentials-sync.md).

Matching rules:

| List state                                            | Behavior                                                                                |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Empty list (default)                                  | No origin restriction                                                                   |
| `https://example.com` listed                          | Exact match on scheme + host (+ port)                                                   |
| `*.example.com` listed                                | Covers `example.com` and all of its subdomains (any scheme/port)                        |
| The list has entries but all are in an invalid format | **Rejects all origins** (fail closed); it does not silently fall back to no restriction |

For Web Chats that are embedded externally and use identity features, we recommend always setting this list to narrow the origins that can obtain a `contactId`.

## 4. Configuration Reference <a href="#config-reference" id="config-reference"></a>

All fields supported by `window.maiagentChatbotConfig`. Setting an unknown field does not raise an error, but the console shows a `[maiagent] Unknown config options: ...` warning to help you check spelling.

### Basic <a href="#config-basic" id="config-basic"></a>

| Field           | Type                 | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                       |
| --------------- | -------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `webChatId`     | `string`             | ✅        | The unique identifier of the Web Chat                                                                                                                                                                                                                                                                                                                                                                             |
| `baseUrl`       | `string`             | ✅        | The Web Chat service address; for SaaS it is `https://chat.maiagent.ai/web-chats`                                                                                                                                                                                                                                                                                                                                 |
| `contactId`     | `string`             |          | Contact ID (used with backend integration; see [Identifying Users](#identify-users))                                                                                                                                                                                                                                                                                                                              |
| `auth`          | `object`             |          | Frontend identity settings: `sourceId` (required), `name`, `contextData`, `mcpCredentials`, and `ts` and `sig` (required when signature verification is enabled; see [Signature Verification](#source-id-signature)). `auth.setup()` runs automatically once the SDK is ready                                                                                                                                     |
| `queryMetadata` | `object` or `string` |          | Knowledge base retrieval scope filter (does not enter the LLM prompt)                                                                                                                                                                                                                                                                                                                                             |
| `locale`        | `string`             |          | Interface locale (such as `zh-TW`, `en`), also used as a reference for the AI reply language; for the supported list and the order of precedence, see [Section 5 of the MaiGPT mode page](/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md#locale). Priority: the locale the user manually chose in the window > config / `locale.set()` > the locale last used in this browser > the browser locale > `zh-TW` |

### Window Modes and Behavior <a href="#config-behavior" id="config-behavior"></a>

| Field                   | Type                                 | Default                   | Description                                                                                                           |
| ----------------------- | ------------------------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `enabledWindowModes`    | `string[]`                           | `['floating', 'sidebar']` | Available window modes; the first item is the default. See [Window Modes](#window-modes)                              |
| `allowWindowModeSwitch` | `boolean`                            | `true`                    | Whether to show the window mode switch button (meaningful only when two or more modes are enabled)                    |
| `showButton`            | `boolean`                            | `true`                    | Whether to show the chat button. When set to `false`, open the window programmatically with `MaiAgent.control.open()` |
| `showChatNotification`  | `boolean`                            | `false`                   | Whether to show a notification next to the button when a reply arrives while the window is closed                     |
| `chatNotificationTitle` | `string`                             |                           | The title text of the notification                                                                                    |
| `preventIosInputZoom`   | `boolean`                            |                           | Prevents iOS Safari from automatically zooming the page when the input box is focused                                 |
| `container`             | `string`, `HTMLElement`, or `object` |                           | inline mode only: the embed container; see [The Container for inline Mode](#inline-container)                         |
| `targetElement`         | `string` or `HTMLElement`            |                           | MaiGPT mode only: the embed container; see [MaiGPT mode](/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md)         |
| `maigptTitle`           | `string`                             |                           | MaiGPT mode only: the brand title in the sidebar; `MaiGPT` is shown by default                                        |

### Appearance <a href="#config-appearance" id="config-appearance"></a>

Size fields accept a number (treated as px), or a `'Npx'` or `'Nrem'` string.

| Field           | Type                 | Default                              | Description                                                          |
| --------------- | -------------------- | ------------------------------------ | -------------------------------------------------------------------- |
| `primaryColor`  | `string`             | `#1890ff`                            | Theme color (the main color of the button and interface)             |
| `buttonSize`    | `string` or `number` | `3rem`                               | Chat button size                                                     |
| `buttonRadius`  | `string`             | `50%`                                | Chat button corner radius                                            |
| `buttonIcon`    | `string`             |                                      | Image URL for the button icon (takes precedence over `openIconHtml`) |
| `openIconHtml`  | `string`             | Built-in SVG                         | Custom HTML for the button's "open" state                            |
| `closeIconHtml` | `string`             | Built-in SVG                         | Custom HTML for the button's "close" state                           |
| `windowWidth`   | `string` or `number` | `24rem`                              | Floating window width                                                |
| `windowHeight`  | `string` or `number` | `40rem`                              | Floating window height                                               |
| `windowRadius`  | `string`             | `0.75rem`                            | Floating window corner radius                                        |
| `boxShadow`     | `string`             | `0.125rem 0.125rem 0.5rem #00000044` | Button and window shadow                                             |

### Position <a href="#config-position" id="config-position"></a>

| Field                  | Type                 | Default   | Description                                          |
| ---------------------- | -------------------- | --------- | ---------------------------------------------------- |
| `buttonPositionBottom` | `string` or `number` | `16` (px) | Distance of the button from the bottom of the window |
| `buttonPositionRight`  | `string` or `number` | `16` (px) | Distance of the button from the right of the window  |
| `windowPositionBottom` | `string`             | `5rem`    | Distance of the floating window from the bottom      |
| `windowPositionRight`  | `string`             | `1rem`    | Distance of the floating window from the right       |
| `windowPosition`       | `string`             |           | Set to `'center'` to center the floating window      |

The chat button can be dragged, and its position is remembered in the browser. When the page loads again, if the remembered position is outside the current window bounds, the button automatically returns to a safe position.

### How the Button Position Is Determined <a href="#button-position-behavior" id="button-position-behavior"></a>

* `buttonPositionBottom` / `buttonPositionRight` are the only configuration source for the button position. The position the user drags the button to is stored only in that browser's window state and takes precedence over the configured values, until the user drags it again or clears the site data.
* When the chat window opens (by clicking the button or calling `MaiAgent.control.open()`), the floating window is positioned using the button's current position as its anchor, and the button itself is not moved; after the window closes, the button stays where it was.
* When the browser window is resized, the position the user dragged the button to scales proportionally and is clamped to the visible area; a button positioned by the configured values follows via CSS on its own.
* To fix the button position so users cannot move it, there is currently no setting to disable dragging. Instead, use `showButton: false` to hide the built-in button and place your own button that calls `MaiAgent.control.open()` (see the [full example](#full-example)).
* If the position looks "off", first check whether the page's CSS overrides the button container's `position`, `bottom`, or `right`, then clear that site's browser data to reset the remembered position.

## 5. SDK API <a href="#sdk-api" id="sdk-api"></a>

After the SDK initializes, you can operate it through the global `MaiAgent` object. Call all methods only after the SDK is ready (see the availability check in [Troubleshooting](#troubleshooting)).

### Window Control `MaiAgent.control` <a href="#api-control" id="api-control"></a>

| Method     | Description                                         |
| ---------- | --------------------------------------------------- |
| `open()`   | Opens the chat window                               |
| `close()`  | Closes the chat window                              |
| `isOpen()` | Returns whether the chat window is open (`boolean`) |

Equivalent global shortcuts are also available: `window.maiagentOpenChat()` / `window.maiagentCloseChat()`.

### Messages `MaiAgent.chat` <a href="#api-chat" id="api-chat"></a>

| Method              | Description                                            |
| ------------------- | ------------------------------------------------------ |
| `send(content)`     | Sends a text message as the user                       |
| `clearHistory()`    | Clears the message history of the current conversation |
| `newConversation()` | Starts a new conversation                              |

```javascript
MaiAgent.chat.send('Hello, I would like to learn about your services')
MaiAgent.control.open()
```

### Events `MaiAgent.events` <a href="#api-events" id="api-events"></a>

| Method                     | Description                                                           |
| -------------------------- | --------------------------------------------------------------------- |
| `on(eventType, callback)`  | Registers an event listener                                           |
| `off(eventType, callback)` | Removes an event listener (you must pass the same callback reference) |

Events you can listen to:

| Event          | When it fires                         | callback parameter                                                                         |
| -------------- | ------------------------------------- | ------------------------------------------------------------------------------------------ |
| `messageReply` | A reply from the AI Agent is received | A parsed object: `{ content: string, sender?: { name, avatar }, timestamp?: number, ... }` |
| `sdkReady`     | SDK initialization is complete        | —                                                                                          |
| `authReady`    | `auth.setup()` is complete            | —                                                                                          |
| `iframeReady`  | The chat window iframe is ready       | —                                                                                          |

```javascript
// Use the MaiAgent.EVENT_TYPES constants to avoid typos
MaiAgent.events.on(MaiAgent.EVENT_TYPES.MESSAGE_REPLY, (data) => {
  console.log('Reply received:', data.content)
})
```

### Locale and Speech <a href="#api-locale-speech" id="api-locale-speech"></a>

| Method                                 | Description                                                                                                                                                                                                                                                                |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MaiAgent.locale.set(lang)`            | Switches the interface locale, such as `'zh-TW'`, `'zh-CN'`, `'en'`; for the supported list, see [Section 5 of the MaiGPT mode page](/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md#locale). It does not override a locale the user has manually chosen in the window |
| `MaiAgent.speech.set(lang, provider?)` | Sets the language for speech recognition and synthesis; `provider` can specify the speech service provider (such as `'azure'`)                                                                                                                                             |

`locale` determines the interface text. It is also sent with every message to the AI as a reference for the reply language when the user's preferred language cannot be determined; when the user asks in another language, the AI usually answers in the language of the question (see [Role instructions: platform-added context](/tech/en/advanced-genai-tech/system-prompt.md#platform-context) and the user manual [Multilingual Support](https://docs.maiagent.ai/build/agent/multi-language-support#reply-language)). Switching the language from the Web Chat menu starts a new conversation; when `locale.set()` changes the locale and the user has not yet sent a message, Web Chat also reopens the conversation in the new locale.

### Identity `MaiAgent.auth` <a href="#api-auth" id="api-auth"></a>

| Method              | Description                                                                                                                                                                                                                                                                                                 |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setup(authConfig)` | Creates or maps a contact and applies the identity; returns `Promise<string \| null>` (the `contactId` on success). The `authConfig` fields are the same as the `auth` in config                                                                                                                            |
| `signOut()`         | Signs out the current identity, returns to the anonymous state, and clears `contextData`. It affects the frontend only; tool credentials already bound on the backend must be revoked separately, see [Logout and credential revocation](/tech/en/serve/contacts/contact-credentials-sync.md#logout-revoke) |

When config includes `auth`, the SDK calls `setup()` automatically, so you do not need to run it manually; call it manually only when a user logs in or out in an SPA, or when you need to update `contextData`.

## 6. Full Example <a href="#full-example" id="full-example"></a>

```html
<!doctype html>
<html lang="zh-TW">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MaiAgent Web Chat Integration Example</title>
  </head>
  <body>
    <h1>My Website</h1>

    <button onclick="MaiAgent.control.open()">Open Support</button>
    <button onclick="startNewChat()">New Conversation</button>

    <script>
      window.maiagentChatbotConfig = {
        webChatId: 'your-web-chat-id',
        baseUrl: 'https://chat.maiagent.ai/web-chats',
        primaryColor: '#007bff',
        enabledWindowModes: ['floating', 'sidebar'],
        locale: 'zh-TW',
        auth: {
          sourceId: 'user-12345',
          name: '王小明',
        },
      }
    </script>
    <script src="https://chat.maiagent.ai/js/embed.min.js" defer></script>

    <script>
      document.addEventListener('DOMContentLoaded', () => {
        MaiAgent.events.on(MaiAgent.EVENT_TYPES.MESSAGE_REPLY, (data) => {
          console.log('AI reply received:', data.content)
          // You can connect an analytics tool here, for example GA4:
          // gtag('event', 'chat_interaction', { event_label: 'bot_reply' })
        })
      })

      function startNewChat() {
        MaiAgent.chat.newConversation()
        MaiAgent.control.open()
      }
    </script>
  </body>
</html>
```

## 7. Troubleshooting <a href="#troubleshooting" id="troubleshooting"></a>

| Symptom                                                                                      | What to check                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| The button does not appear                                                                   | Whether `maiagentChatbotConfig` is defined before the SDK script; whether `webChatId` / `baseUrl` are correct; whether the console shows errors                                                                                                                                                                                                                                                                                                              |
| The console shows `Unknown config options: ...`                                              | Check the field spelling against the [Configuration Reference](#config-reference)                                                                                                                                                                                                                                                                                                                                                                            |
| `MaiAgent is not defined`                                                                    | The SDK has not finished loading; call it after `DOMContentLoaded`, or first check `typeof MaiAgent !== 'undefined'`                                                                                                                                                                                                                                                                                                                                         |
| Event listeners do not fire                                                                  | Confirm the event name is correct (using the `MaiAgent.EVENT_TYPES` constants is recommended); `off()` must receive the same function reference as `on()`                                                                                                                                                                                                                                                                                                    |
| The conversation disappears on another device                                                | Anonymous conversations are tied to the browser; to carry them across devices, set `auth` or `contactId` (see [Identifying Users](#identify-users))                                                                                                                                                                                                                                                                                                          |
| The identity does not take effect after setting `auth` (400 error)                           | If the Web Chat has "Allowed embed domains" configured, only pages on listed domains can complete identity sync; confirm the embedding page's domain has been added to the list                                                                                                                                                                                                                                                                              |
| The button is positioned incorrectly                                                         | Check whether the page CSS conflicts with the button styles; whether the position parameter format is correct (number, `px`, or `rem`); a dragged position is remembered in the browser, see [How the button position is determined](#button-position-behavior)                                                                                                                                                                                              |
| You want to embed the iframe yourself and communicate with the chat window via `postMessage` | Not supported. The `postMessage` messages between the SDK and the chat window iframe are an internal SDK protocol whose format changes between versions, and messages from other origins are not accepted. Load the SDK with the embed code, receive messages through [events](#api-events) (such as `messageReceived`), and send messages through [`MaiAgent.chat`](#api-chat); for a chat area fixed within the page, use [inline mode](#inline-container) |

Call the SDK safely:

```javascript
function safeOpenChat() {
  if (typeof MaiAgent !== 'undefined' && MaiAgent.control) {
    MaiAgent.control.open()
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/tech/en/serve/web-chat-sdk.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
