# Embed Chat Widget

Drop your AVCodex agent onto any web page: a customer support portal, an integrator's client microsite, an internal tech-services dashboard, or the QR-code landing page taped inside a conference room.

Two embed options. Pick the one that fits your layout.

## Embed Options

| Option | Best For |
|--------|----------|
| **Chat Widget** | Floating bubble in a corner, doesn't take page space |
| **Iframe** | Dedicated chat panel inside a page (room support page, integrator portal, internal wiki) |

---

## Option 1: Chat Widget (Floating Bubble)

The chat widget adds a floating chat bubble to the corner of any page. Click it and your AVCodex agent opens in an overlay. It's lightweight (~5KB gzipped), uses Shadow DOM for CSS isolation, and runs on any site (WordPress, Webflow, an integrator's marketing page, an internal SharePoint page, a Q-SYS web UI).

### Quick Start

Add a single `<script>` tag before the closing `</body>` tag on your site:

```html
<script src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"></script>
```

That's it. The widget auto-detects your agent from the subdomain and renders a floating chat bubble in the bottom-right corner.

### Getting the Widget Code

**1. Open Share Settings**

In your AVCodex agent, click **Publish > Share** in the navigation.

**2. Find the Widget Card**

Look for the **Widget** card on the Share page.

**3. Copy the Code**

Click the code snippet to copy it to your clipboard:

```html
<script src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"></script>
```

**4. Add to Your Site**

Paste the code before the closing `</body>` tag.

### Customizing with Data Attributes

Customize the widget with `data-*` attributes on the script tag:

```html
<script
  src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"
  data-position="bottom-left"
  data-greeting="Need a hand with the room?"
  data-greeting-delay="3000"
  data-color="#0E5C66"
  data-bubble-size="64"
></script>
```

#### Attribute Reference

| Attribute | Default | Description |
|-----------|---------|-------------|
| `data-app-id` | *(auto-detected from subdomain)* | Agent slug, e.g. `room-support`. Required when hosting the script from a different domain |
| `data-position` | `bottom-right` | Widget position: `bottom-right`, `bottom-left`, `top-right`, `top-left` |
| `data-theme` | `auto` | Color theme: `light`, `dark`, `auto` (follows the user's OS preference) |
| `data-greeting` | *(none)* | Greeting message shown as a preview bubble above the chat button |
| `data-greeting-delay` | `2000` | Delay in milliseconds before the greeting appears (0-30000) |
| `data-auto-open` | *(disabled)* | Automatically open the chat after this many milliseconds (e.g. `5000` for 5 seconds) |
| `data-color` | *(agent brand color)* | Bubble background color as a hex value (e.g. `#0E5C66`) |
| `data-bubble-size` | `60` | Bubble diameter in pixels (48-80) |
| `data-bubble-shape` | `circle` | Bubble shape: `circle` or `rounded` (rounded rectangle) |
| `data-icon-url` | *(agent logo)* | Custom icon URL for the bubble (falls back to agent logo, then a chat icon) |
| `data-z-index` | `2147483646` | CSS z-index for the widget container |
| `data-hide-on-mobile` | `false` | Set to `true` to hide the widget on mobile viewports |

#### Config Priority

Data attributes win. If you set a value via `data-*`, it overrides server-side configuration. Full priority order:

1. **Data attributes** on the `<script>` tag (highest)
2. **Builder widget config** (saved in the Widget tab)
3. **Agent brand styles** (logo, primary color)
4. **Defaults** (lowest)

### Configuring in the Builder

You can also configure the widget visually in the Builder:

1. Open your agent in the Builder
2. Go to **Publish > Widget**
3. Adjust position, size, greeting, and other settings
4. See changes reflected in the live preview
5. Changes save automatically and update the widget in real time

The Builder Widget tab covers the same options as data attributes, plus a live preview showing how the widget will look on the host site.

### Widget Features

- **CSS isolation**: Uses Shadow DOM, so your site's CSS won't fight the widget and vice versa.
- **Instant open**: Preloads the chat iframe on hover, so it opens immediately when clicked.
- **Greeting bubble**: Optional animated preview message to encourage interaction (good for end-user QR-code support pages).
- **Responsive**: Full-screen overlay on mobile, positioned popup on desktop.
- **Accessible**: Full keyboard navigation, ARIA labels, focus management, reduced-motion support.
- **Lightweight**: ~5KB gzipped (versus 42KB for the legacy widget).

---

## JavaScript API

The widget exposes a `window.AVCodexWidget` API for programmatic control. Use it to open and close the chat, identify users, and listen for events from your own JavaScript.

### Basic Controls

```javascript
// Open the chat
AVCodexWidget.open();

// Close the chat
AVCodexWidget.close();

// Toggle open/closed
AVCodexWidget.toggle();

// Check if chat is open
if (AVCodexWidget.isOpen()) {
  console.log("Chat is open");
}

// Remove the widget completely (cannot be undone)
AVCodexWidget.destroy();
```

### User Identification

Identify users so chat history persists across sessions and you can see who's chatting in your dashboard. Useful when the agent is sitting on a logged-in integrator portal or a tech-services console.

#### Client-Side (Simple)

Pass the user's email and optional name directly. Simplest path for sites where the chat doesn't require authentication, like a public room-support page reached by QR code:

```javascript
AVCodexWidget.setUser({
  email: "tech@integrator.example",
  name: "Field Tech"
});
```

#### Server-Side (Secure)

For agents with authentication enabled, use server-side token generation to verify the user's identity from your backend. Prevents spoofing.

**Step 1: Generate a token on your server**

Call the Builder API from your backend to get a bearer token:

```bash
curl -X POST https://your-agent.app.avcodex.com/api/v1/apps/{APP_ID}/consumers/auth \
  -H "Authorization: Bearer YOUR_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "programmer@integrator.example", "name": "Lead Programmer"}'
```

Response:

```json
{
  "data": {
    "token": "eyJhbGciOi...",
    "consumer": {
      "id": "...",
      "email": "programmer@integrator.example",
      "name": "Lead Programmer"
    }
  }
}
```

**Step 2: Pass the token to the widget**

Send the token to your frontend and pass it to `setUser`:

```javascript
AVCodexWidget.setUser({
  email: "programmer@integrator.example",
  name: "Lead Programmer",
  token: "eyJhbGciOi..."  // Bearer token from your server
});
```

The token authenticates the chat session inside the iframe without cookies, which keeps things working in cross-origin embeds (an integrator portal hosting an AVCodex agent on a different domain).

> **Note:** You need a Builder API key with **write** scope to call the `/consumers/auth` endpoint. Generate one in **Settings > API Keys**.

### Events

Listen for widget events to react to user interactions:

```javascript
// Listen for an event (returns an unsubscribe function)
const unsubscribe = AVCodexWidget.on("message", (data) => {
  console.log("New message:", data.role, data.content);
});

// Stop listening
unsubscribe();

// Alternative: manually remove a listener
function onOpen() { console.log("Chat opened"); }
AVCodexWidget.on("open", onOpen);
AVCodexWidget.off("open", onOpen);
```

#### Event Reference

| Event | Payload | Description |
|-------|---------|-------------|
| `open` | `{}` | The chat overlay was opened |
| `close` | `{}` | The chat overlay was closed |
| `ready` | `{}` | The chat iframe finished loading and is ready to use |
| `message` | `{ role, content }` | A new message was sent or received. `role` is `"user"` or `"assistant"` |

### Full API Reference

| Method | Signature | Description |
|--------|-----------|-------------|
| `open()` | `() => void` | Open the chat overlay |
| `close()` | `() => void` | Close the chat overlay |
| `toggle()` | `() => void` | Toggle the chat open/closed |
| `isOpen()` | `() => boolean` | Returns `true` if the chat is currently open |
| `setUser(user)` | `(user: { email: string, name?: string, token?: string }) => void` | Identify the current user |
| `on(event, cb)` | `(event: string, cb: Function) => () => void` | Subscribe to an event. Returns an unsubscribe function |
| `off(event, cb)` | `(event: string, cb: Function) => void` | Unsubscribe a specific callback from an event |
| `destroy()` | `() => void` | Remove the widget from the page entirely |

---

## Examples

### Open the Agent from a Custom Button

Useful on a room-support page where the user expects to tap a "Get Help" button rather than hunt for a floating bubble:

```html
<button onclick="AVCodexWidget.open()">Get help with this room</button>

<script src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"></script>
```

### React Integration

```jsx
import { useEffect } from "react";

function App() {
  useEffect(() => {
    // Load the widget script
    const script = document.createElement("script");
    script.src = "https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js";
    script.async = true;
    document.body.appendChild(script);

    return () => {
      // Clean up on unmount
      if (window.AVCodexWidget) {
        window.AVCodexWidget.destroy();
      }
      script.remove();
    };
  }, []);

  return (
    <button onClick={() => window.AVCodexWidget?.open()}>
      Ask the room agent
    </button>
  );
}
```

### Next.js Integration

```jsx
"use client";

import Script from "next/script";

export default function ChatWidget() {
  return (
    <Script
      src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"
      strategy="afterInteractive"
      data-greeting="Need help with the conference room?"
    />
  );
}
```

### Auto-Open After a Delay

Open the chat automatically after 5 seconds. Handy on a new-hire onboarding page for AV programmers:

```html
<script
  src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"
  data-auto-open="5000"
></script>
```

### Track Conversations in Analytics

```javascript
AVCodexWidget.on("open", () => {
  // Google Analytics
  gtag("event", "chat_opened", { event_category: "engagement" });
});

AVCodexWidget.on("message", (data) => {
  if (data.role === "user") {
    gtag("event", "chat_message_sent", { event_category: "engagement" });
  }
});
```

### Conditional Widget Loading

Only load the widget on certain pages. Example: only on the support section of a managed-services portal.

```javascript
if (window.location.pathname.startsWith("/support")) {
  const script = document.createElement("script");
  script.src = "https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js";
  script.dataset.greeting = "Having trouble with a room? Tell us what's happening.";
  document.body.appendChild(script);
}
```

### Server-Side Auth with React

```jsx
import { useEffect } from "react";

function ChatWidget({ user }) {
  useEffect(() => {
    if (!user || !window.AVCodexWidget) return;

    // Call your backend to get a widget token
    fetch("/api/chat-token", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: user.email, name: user.name }),
    })
      .then((res) => res.json())
      .then(({ token }) => {
        window.AVCodexWidget.setUser({
          email: user.email,
          name: user.name,
          token,
        });
      });
  }, [user]);

  return null;
}
```

---

## Option 2: Iframe Embed

The iframe embeds your AVCodex agent directly in the page layout. Use this when you want the chat to be a fixed part of the page (a dedicated "Ask the Room Agent" panel inside a building's conference-room landing page) rather than a floating overlay.

### Getting the Iframe Code

**1. Open Share Settings**

In your AVCodex agent, click **Publish > Share** in the navigation.

**2. Find the Iframe Card**

Look for the **Iframe** card on the Share page.

**3. Copy the Code**

Click the code snippet to copy it:

```html
<iframe src="https://your-agent.app.avcodex.com" height="800px" width="100%" frameborder="0" title="Your Agent Name"></iframe>
```

**4. Add to Your Site**

Paste the iframe code into your HTML where you want the chat to appear.

### Customizing the Iframe

**Adjust dimensions:**

```html
<iframe
  src="https://your-agent.app.avcodex.com"
  height="600px"
  width="400px"
  frameborder="0"
  title="Room Support Chat"
></iframe>
```

**Responsive container:**

```html
<div style="width: 100%; height: 80vh; min-height: 400px;">
  <iframe
    src="https://your-agent.app.avcodex.com"
    style="width: 100%; height: 100%; border: none;"
  ></iframe>
</div>
```

---

## Platform-Specific Instructions

### WordPress

**For Widget:**
1. Go to **Appearance** > **Theme Editor** (or use a plugin like "Insert Headers and Footers")
2. Add the widget code before `</body>` in your theme's footer

**For Iframe:**
1. Edit your page or post
2. Add a **Custom HTML** block
3. Paste the iframe code

### Webflow

1. Go to **Project Settings** > **Custom Code**
2. For widget: Paste in "Footer Code"
3. For iframe: Add an **Embed** element to your page

### Squarespace

**For Widget:**
1. Go to **Settings** > **Advanced** > **Code Injection**
2. Paste in the "Footer" section

**For Iframe:**
1. Add a **Code Block** to your page
2. Paste the iframe code

### Shopify

**For Widget:**
1. Go to **Online Store** > **Themes** > **Edit code**
2. Find `theme.liquid` and paste before `</body>`

**For Iframe:**
1. Edit the page where you want the chat
2. Add a **Custom Liquid** section
3. Paste the iframe code

### Wix

**For Widget:**
1. Go to **Settings** > **Custom Code**
2. Click **Add Code in the Body - End**
3. Paste the widget script tag

**For Iframe:**
1. Add an **HTML iframe** element to your page
2. Set the source URL to your agent URL

### Framer

**For Widget:**
1. Go to **Site Settings** > **Custom Code**
2. Add the widget script in the "End of `<body>` tag" section

**For Iframe:**
1. Add a **Code** component
2. Paste the iframe embed code

---

## Cookies and Cross-Origin Embeds

AVCodex automatically handles cross-origin cookie restrictions for iframe and widget embeds. When the agent detects it's running inside an iframe on a different domain, it sets cookies with `SameSite=None` so the browser allows them in the cross-site context. Both anonymous and authenticated chat work out of the box in iframe embeds, no extra configuration required.

> **Note:** Some browsers (notably Safari with Intelligent Tracking Prevention) may still block third-party cookies in certain configurations. If users have trouble keeping their chat sessions in an iframe embed, consider:
>
> 1. **Set up a custom domain** that's a subdomain of your site (best for iframe embeds, same-site cookies just work).
> 2. **Use server-side auth with `setUser()`** to pass a bearer token via PostMessage (bypasses cookies entirely).

### How Server-Side Auth Bypasses Cookie Issues

For agents with authentication enabled, or for maximum compatibility with strict browser privacy settings, the `setUser({ token })` approach avoids cookies entirely:

1. Your server calls `POST /api/v1/apps/{APP_ID}/consumers/auth` with the user's email.
2. The API returns a bearer token.
3. Your frontend passes the token to `AVCodexWidget.setUser()`.
4. The widget sends the token to the iframe via PostMessage.
5. The iframe uses the bearer token for authentication instead of cookies.

This works in cross-origin contexts because PostMessage is not affected by cookie restrictions.

You can configure these settings in:
- **Settings** page for custom domains
- **Access** page for authentication settings

## Content Security Policy (CSP)

If your site uses a strict Content Security Policy, allow the widget script and iframe:

```
frame-src *.app.avcodex.com;
script-src your-agent.app.avcodex.com;
```

Replace with your custom domain if you have one configured.

If you're using a nonce-based CSP, add the nonce to the script tag:

```html
<script nonce="YOUR_NONCE" src="https://your-agent.app.avcodex.com/w/chat/avcodex-widget.js"></script>
```

---

## Troubleshooting

### Widget Not Appearing

1. **Check code placement**: Widget code goes before `</body>`, not inside `<head>`.
2. **Check for conflicts**: Other scripts may interfere. Check the browser console for errors.
3. **Check agent status**: Ensure your agent is published and active.
4. **Check CSP**: Your site's Content Security Policy may block the script or iframe.
5. **Check mobile**: If `data-hide-on-mobile="true"` is set, the widget won't appear on mobile.

### Widget Appears But Chat Won't Open

1. **Check the console**: Look for network errors or CORS issues.
2. **Check the agent URL**: Confirm the agent is reachable at the URL in the script `src`.
3. **Check authentication**: If auth is enabled, use `setUser()` with a token or set up a custom domain.

### Iframe Shows "Session Not Found"

This usually means the browser is blocking cookies in a cross-origin iframe context. AVCodex handles this automatically, but strict privacy settings can still get in the way:

1. **Safari ITP**: Safari aggressively blocks third-party cookies. Use a custom domain (subdomain of your site) or server-side auth with `setUser({ token })`.
2. **Chrome Incognito**: Third-party cookies may be blocked by default in Incognito.
3. **Browser extensions**: Privacy extensions (uBlock Origin, Privacy Badger) may block cross-site cookies.

### Iframe Not Loading

1. **Check the URL**: The iframe `src` must match your agent's URL exactly.
2. **CSP headers**: Your site's Content Security Policy may block iframes.
3. **X-Frame-Options**: Some hosting providers set `X-Frame-Options: DENY` by default.

### Styling Issues

1. **Z-index conflicts**: Use the `data-z-index` attribute to adjust widget stacking order.
2. **Container overflow**: For iframe, ensure parent elements don't have `overflow: hidden`.
3. **Mobile display**: Use `data-hide-on-mobile="true"` if the widget conflicts with your mobile layout.
4. **Double widget**: If you see two widgets, make sure the script tag is only included once. The widget prevents double-initialization, but old v1 and new v2 scripts can coexist.

### User Identity Not Working

1. **Check `setUser` timing**: Call `setUser()` after the widget script has loaded. Listen for the `ready` event if needed:
   ```javascript
   AVCodexWidget.on("ready", () => {
     AVCodexWidget.setUser({ email: "tech@integrator.example" });
   });
   ```
2. **Check token validity**: Server-side tokens expire. Generate a fresh token on each page load.
3. **Check API key scope**: The Builder API key must have **write** scope to call `/consumers/auth`.

*AVCodex · Your AV expertise. Amplified by AI.*
