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

# Script Tag Integration

> Instructions on how to integrate Dial AI into your application using a simple script tag.

Dialai can be embedded in your application via a single script tag. This method gives you a floating chat button (bottom-right) that opens the AI agent in a modal when clicked — your visitors talk to the agent without leaving your application.

## Prerequisites

Before you begin, you'll need:

* A Dial AI chat ID (see [Creating a Chat ID](#creating-a-chat-id) below)
* The backend URL for your Dial AI instance
* A unique anchor ID for mounting the chat button

## Creating a Chat ID

A **chat ID** is the UUID of a Web Point of Contact. Create one under **Agent > Points of Contact > Web** — see [iframe integration](/iframe-integration#creating-a-chat-link) for the step-by-step.

<Frame caption="Web POC Generate tab — the chat UUID is on the Edit tab; this tab tunes embed-time options">
  <img className="block dark:hidden" src="https://mintcdn.com/dialai/UQE9bvis-x7kIgSE/images/web-poc-generate-light.png?fit=max&auto=format&n=UQE9bvis-x7kIgSE&q=85&s=cc863c2745d84c7bfeed98bc5e2ebbb9" alt="Web POC Generate tab in light mode" width="1920" height="911" data-path="images/web-poc-generate-light.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/dialai/UQE9bvis-x7kIgSE/images/web-poc-generate-dark.png?fit=max&auto=format&n=UQE9bvis-x7kIgSE&q=85&s=10ab22e1449b8480fe865756e69a457f" alt="Web POC Generate tab in dark mode" width="1920" height="911" data-path="images/web-poc-generate-dark.png" />
</Frame>

## Basic Integration

Add the following to your HTML file, replacing the placeholder values as needed:

1. Load the widget script in your `<head>` tag:

```html theme={null}
<script defer type="module" src="https://cdn.dialai.ca/v1/esm/{version}/dial-ai.js"></script>
```

Replace `{version}` with a specific release version (e.g. `0.0.1329`) from the [versions manifest](https://cdn.dialai.ca/v1/versions.json).

2. Add the mount point and initialization script:

```html theme={null}
<!-- Mount point for the chat widget (renders in the bottom right) -->
<span id="your-anchor-id"></span>

<script>
document.addEventListener("DOMContentLoaded", function() {
    window.renderModal("your-anchor-id", {
        iconUrl: "https://www.yourdomain.com/path/to/icon.svg",
        chatId: "YOUR_CHAT_ID",
        backendUrl: "https://your-dialai-instance.com"
    });
});
</script>
```

## Configuration Options

The `renderModal` function accepts the following configuration options:

* `iconUrl` (string, required): URL to the icon that will be displayed in the chat button
* `chatId` (string, required): Your unique Dial AI chat identifier
* `backendUrl` (string, required): The base URL of your Dial AI instance
* `brandVariants` (object, optional): Custom color scheme for the chat interface (see [Brand Variants](#brand-variants) below)

## Brand Variants

By default, the widget uses the theme configured for the chat link's tenant. If you need to override this to match your site's branding, pass a `brandVariants` object with color values on a scale from 10 (darkest) to 160 (lightest):

```js theme={null}
brandVariants: {
    10: "#001018",
    20: "#002030",
    30: "#003049",
    40: "#004062",
    50: "#00507a",
    60: "#006093",
    70: "#0070ab",
    80: "#0080c4",
    90: "#0090dc",
    100: "#00a1f5",
    110: "#10adfe",
    120: "#2bb6ff",
    130: "#45bfff",
    140: "#60c8ff",
    150: "#7ad1ff",
    160: "#95dafe"
}
```

Omit `brandVariants` to use the tenant's default theme.

## CORS Configuration

The widget script is served from our CDN with permissive CORS headers (`Access-Control-Allow-Origin: *`), so it can be loaded from any domain without additional configuration.

## Troubleshooting

* If the chat button doesn't appear, check your browser's console for any error messages
* Ensure the anchor element with the specified ID exists in the DOM when the script runs
* Make sure the chat ID and backend URL are correct

## Example Implementation

Here's a complete example of how to integrate the chat button into a simple HTML page:

```html theme={null}
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My App with Dial AI</title>
    <script defer type="module" src="https://cdn.dialai.ca/v1/esm/0.0.1329/dial-ai.js"></script>
</head>
<body>
    <h1>Welcome to My App</h1>
    <p>Click the chat button in the bottom right to get help.</p>

    <!-- Mount point for the chat widget -->
    <span id="dial-ai-button"></span>

    <script>
    document.addEventListener("DOMContentLoaded", function() {
        window.renderModal("dial-ai-button", {
            iconUrl: "https://cdn.dialai.ca/v1/esm/0.0.1329/favicon.ico",
            chatId: "00000000-0000-0000-0000-000000000000",
            backendUrl: "https://demo.dialai.ca"
        });
    });
    </script>
</body>
</html>
```

## Versions

The widget is hosted on our CDN at `https://cdn.dialai.ca/v1/esm/{version}/dial-ai.js`. You can find available versions in the [versions manifest](https://cdn.dialai.ca/v1/versions.json). Pin to a specific release version to avoid unexpected changes.

## Internal-Agent embed: page-aware integrations

In addition to the customer-facing chat (`renderModal`), the same bundle exposes `renderInternalAgentApp` — used to embed an **internal agent** (back-office assistant) into your own internal tooling. This embed exposes two host-page hooks that let the agent become aware of, and act on, the page it's running inside.

### `onNavigate` — let the agent drive page navigation

When an agent tool result includes the field `X-DialAI-Navigation-Path`, the widget invokes your `onNavigate(path)` callback. Use this to wire the agent into your app's router so the agent can say "I've opened the customer record for you" and actually open it.

```html theme={null}
<div id="agent-app"></div>
<script defer type="module" src="https://cdn.dialai.ca/v1/esm/{version}/dial-ai.js"></script>
<script>
  document.addEventListener("DOMContentLoaded", function() {
    window.renderInternalAgentApp("agent-app", {
      tenantId: "your-tenant",
      backendUrl: "https://your-instance.dialai.ca",
      onNavigate: (path) => {
        // Route using your framework, e.g.:
        // window.history.pushState({}, "", path); router.refresh();
        window.location.href = path;
      }
    });
  });
</script>
```

The callback fires exactly once per tool completion that contains a navigation path. The path is a string — the host decides how to interpret it (absolute URL, relative route, anchor). If `onNavigate` is omitted, navigation results are silently ignored.

### `onMessageSend` — inject page context with every user message

Before each user message is sent to the agent, the widget calls your `onMessageSend()` callback. Return `{ contextAdditions: string }` and the string is appended to the outgoing user message after a delimiter, so the agent can see what's currently on the page without the user having to type it.

```html theme={null}
<div id="agent-app"></div>
<script defer type="module" src="https://cdn.dialai.ca/v1/esm/{version}/dial-ai.js"></script>
<script>
  document.addEventListener("DOMContentLoaded", function() {
    window.renderInternalAgentApp("agent-app", {
      tenantId: "your-tenant",
      backendUrl: "https://your-instance.dialai.ca",
      onMessageSend: () => ({
        contextAdditions: [
          `Current page URL: ${window.location.href}`,
          `Selected record ID: ${document.querySelector("[data-selected-id]")?.dataset.selectedId ?? "none"}`,
          `Filters: ${JSON.stringify(window.__appState?.filters ?? {})}`
        ].join("\n")
      })
    });
  });
</script>
```

The widget appends the returned string to the user's message using a `<!-- dialai-context -->` marker; the agent sees both the original user input and the appended context.

<Warning>The callback must be **synchronous** — return a value immediately, don't await anything. Use cached/derived values that are already in memory.</Warning>

Common things to pass via `contextAdditions`:

* The current route, page title, or page-level entity ID.
* The currently-selected item (row in a table, item in a list).
* Active filters or search terms the user has applied.
* The viewport-visible portion of a long document, if the agent should "see what the user sees".

### When to use this vs the chat-link iframe

|                          | `renderInternalAgentApp`                                             | Customer chat (`renderModal` / iframe)    |
| ------------------------ | -------------------------------------------------------------------- | ----------------------------------------- |
| Audience                 | Your internal staff.                                                 | Your end customers.                       |
| Auth                     | Operator-authenticated session (the host page is already signed in). | Anonymous; uses a Web POC chat link UUID. |
| Page hooks               | `onNavigate`, `onMessageSend`.                                       | None — chat is sandboxed in an iframe.    |
| Conversation persistence | Backed by the internal-agent SSE stream.                             | One-shot chat session per visitor.        |

Pick `renderInternalAgentApp` when you're building an in-house tool where the agent should *do things* in your app on the user's behalf. Pick `renderModal` (or the [iframe](/iframe-integration)) when you're putting a customer-facing AI agent on a public page.

## ESM vs UMD

The widget is available in two module formats:

* **ESM** (recommended) — loaded with `<script type="module">`. This is the current format used for all new releases (`/v1/esm/{version}/dial-ai.js`).
* **UMD** (legacy) — loaded with a plain `<script>` tag. Older releases (pre-0.0.1323) were published in UMD format and remain available at `/v1/umd/dial-ai-{version}.umd.js` for backward compatibility.

New integrations should use the ESM format. Existing UMD integrations will continue to work but will not receive new releases.
