> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Embedding the chat widget (script tag)

Use the script-tag embed to add the Runtype chat widget to an HTML page without installing a framework package. For React apps, see [Embedding the chat widget (React)](/user-guide/products-surfaces/embedding-the-chat-widget-react).

## Before you begin

Complete the following prerequisites before you get the embed code:

* Create a **Product** with a chat **Surface**. For more information, see [Setting up a chat surface](/user-guide/products-surfaces/setting-up-a-chat-surface).
* Link at least one **Capability** to the **Surface**.
* Create a client token and add your website origin to **Allowed Origins**.

## Get the embed code

Copy the snippet from the workbench or the classic dashboard. The snippet includes the client token and your saved widget settings.

### In the workbench

To copy the snippet in the workbench, follow these steps:

1. Open the chat **Surface** and select **Embed**.
2. Select **Script tag**.
3. In the **token** menu, select a client token, or select **Create token…** to create one.
4. If you selected a live token, paste the token value that you saved when you created it into **Saved live token**. Runtype stores only a hint of a live token, so the workbench can't fill in the value for you.
5. Click **Copy**.

**Copy** stays unavailable until the snippet contains a working token.

To have a coding agent add the widget for you, go to **Build it with a coding agent**, select **Claude Code**, **Cursor**, or **Codex**, and click **Copy prompt**.

### In the classic dashboard

To copy the snippet in the classic dashboard, follow these steps:

1. Open your **Product** and select the chat **Surface**.
2. Select the **Ship** tab.
3. Select **code it myself** in the tool selector.
4. Select **Script tag** under **Integration format**.
5. In the token menu after **with**, select a client token, or select **Create token…** to create one.
6. Click **Copy code**.

If you leave **a placeholder** selected in the token menu, the snippet contains `YOUR_CLIENT_TOKEN`. Replace it with your client token before you connect the widget.

### From the CLI

To create an Agent, a client token, and a script-tag snippet from a terminal, run `runtype persona init`:

```bash
runtype persona init --agent-name "Support" --origins https://example.com --print-snippet
```

This path creates a new Agent and doesn't need a Product or a Surface. Set `--origins` to the origins that host the widget. If you omit `--origins` in a non-interactive run, the token allows any origin.

## Install the widget

To install the widget on an HTML page, paste the copied snippet before the closing `</body>` tag. Paste it unchanged. The snippet is one `script` tag that loads the widget installer and carries the client token and your saved settings in its `data-config` attribute.

To add the widget with default settings, you can write this minimal tag instead:

**`Script tag`**

```html title="Script tag"
<script
  src="https://cdn.runtype.com/persona/latest/install.global.js"
  data-runtype-token="YOUR_CLIENT_TOKEN"
></script>
```

Replace `YOUR_CLIENT_TOKEN` with your client token.

To keep a production page on a fixed widget version, replace `latest` in the script URL with a version number, such as `4.24.0`. The installer loads the rest of the widget from the same versioned path.

### Script tag attributes

The installer reads the following attributes from its `script` tag:

| Attribute            | Value                | Use                                                                              |
| -------------------- | -------------------- | -------------------------------------------------------------------------------- |
| `data-runtype-token` | Client token         | Authenticates the widget.                                                        |
| `data-config`        | JSON object          | Widget settings. The copied snippet puts the token and your saved settings here. |
| `data-agent-id`      | Agent ID             | Selects an Agent that the client token allows.                                   |
| `data-flow-id`       | Flow ID              | Selects a Flow that the client token allows.                                     |
| `data-api-url`       | URL                  | Sends requests to a different Runtype API URL.                                   |
| `data-preview-param` | Query parameter name | Shows the widget only on URLs that carry this parameter.                         |

To test the widget on a live page before visitors can see it, add `data-preview-param="chat_preview"` to the tag. The widget then appears only on URLs that include `?chat_preview=1`.

## Configure the widget

In the workbench, use **Appearance** to configure the widget. In the classic dashboard, click the selected style, such as **Clean style**, on **Ship**. Copy the snippet again after you save, because the snippet carries your saved settings. You can configure the following settings:

* **Theme**: Set colors, borders, and other styles.
* **Welcome message**: Set the first message that visitors see.
* **Launcher**: Set the chat bubble's position and style.

## Control the widget from your page

The installer dispatches a `persona:chat-ready` window event when the chat loads. The event's `detail` is the widget handle. With the default floating launcher, the chat loads when a visitor first opens it.

**`Listen for replies`**

```html title="Listen for replies"
<script>
  window.addEventListener('persona:chat-ready', (event) => {
    const handle = event.detail
    handle.on('assistant:complete', (message) => {
      console.log('Reply', message)
    })
  })
</script>
```

The handle has `open()`, `close()`, `submitMessage(text)`, and `destroy()` methods. It emits the `user:message`, `assistant:message`, `assistant:complete`, `widget:opened`, and `widget:closed` events.

The `data-config` attribute holds JSON only. To pass functions, such as event callbacks, set `window.siteAgentConfig` in a script that runs before the installer script.

## Use client tokens

Use a client token to authenticate a browser chat widget. Don't put an API key in client-side code. Client tokens have the following properties:

* **Public**: Designed for browser code. Origin restrictions and scoping limit what a copied token can do.
* **Scoped**: Scoped to a **Surface**, or to specific Agents or Flows.
* **Origin-restricted**: Limited to browser origins that match **Allowed Origins**.

Client tokens use the `ct_live_` or `ct_test_` prefix. Create and manage them in **Access & keys** in the workbench or **Auth** in the classic dashboard. For more information, see [Client tokens and domain restrictions](/user-guide/products-surfaces/client-tokens-and-domain-restrictions).

## Restrict widget origins

Set **Allowed Origins** on the client token in **Access & keys** or **Auth** to control which website origins can use the token. Enter each origin with its scheme, such as `https://example.com`.

Use `*` to allow any origin. For production, list the exact origins that host the widget. Test tokens also allow localhost origins automatically.

In the workbench, a chat **Surface** can also have its own **Allowed origins** list in **Access & keys**. A request with a token that belongs to that Surface must match both the token list and the Surface list. This also applies to localhost requests from a test token.

## Verify the installation

To verify that the widget works, follow these steps:

1. Save and deploy your HTML changes.
2. Load the page that contains the script.
3. Open the chat launcher.
4. Send a message and confirm that the widget returns a response.
5. In the workbench, open the Surface's **Embed** section and check **Origins seen · last 7 days** for your page's origin.

## Troubleshoot the widget

Use the following checks when the widget doesn't work. The browser's network panel shows the status code and error message for each failed chat request.

### Widget doesn't appear

Check the following items:

* Place the script before `</body>`.
* Check the browser console for errors.
* If you set `data-preview-param`, load the page with that query parameter.
* If your site sends a Content-Security-Policy header, allow `https://cdn.runtype.com` in `script-src` and `style-src`, and `https://api.runtype.com` in `connect-src`. A blocked script fails without a visible error on the page.

### Widget appears but doesn't respond

Check the following items:

* Confirm that the **Surface** status is **Active**. An inactive Surface returns `403` with `Chat surface is not active`.
* Confirm that the **Product** has active **Capabilities**.
* Confirm that the page origin matches the token's **Allowed Origins** and, in the workbench, the Surface's **Allowed origins**. An unmatched origin returns `403` with `Origin not allowed`. If **Origins seen · last 7 days** shows your origin as **Not allowed**, click **Allow** to add it to the selected token.
* If a request returns `401` with `Invalid client token`, the token value is wrong or the token was regenerated. Update the snippet with the current token value.
* If a request returns `403` with `Client token is inactive` or `Client token is no longer active`, the token was disabled. Update the snippet with an active token.
* Review execution logs for errors.

For more origin checks, see [Troubleshoot rejected requests](/user-guide/products-surfaces/client-tokens-and-domain-restrictions#troubleshoot-rejected-requests).

### Conversation stops responding

Check the following items:

* If a request returns `429` with `Session message limit reached`, the conversation reached the token's message limit per session. The default is 100 messages. Start a new conversation, or raise `maxMessagesPerSession` (up to 1000) on the token through the [Client Tokens API](/api-reference/runtype-api/client-tokens/list-client-tokens).
* If a request returns `429` with a limit message, your account reached an execution limit. If it returns `402`, your account reached its spending limit. Check your usage and billing.

## Next steps

Continue with these guides to customize and extend the widget:

* [Widget theming and customization](/user-guide/products-surfaces/widget-theming-and-customization): change the widget's appearance and behavior.
* [Client tokens and domain restrictions](/user-guide/products-surfaces/client-tokens-and-domain-restrictions): manage the origins that can use the client token.
* [End-user identity and credentials](/developer-guides/guides/end-user-identity): pass a proof from your backend so a visitor's history follows them across devices.
* [Conversation history in an embedded widget](/developer-guides/guides/client-conversation-history): let visitors return to earlier conversations.
* [Build the widget from code](/user-guide/getting-started/quickstart-from-agent-to-chat-widget#build-the-widget-from-code): create the Agent and its client token with the SDK.