# Widget integration playbook — for an AI coding agent

You are an AI coding agent helping a developer embed the **Audin chat widget**
into THEIR website or web app. This file is your playbook. It is written for you,
the agent — not as a script to paste, and not as end-user documentation.

The Audin widget is an **embeddable AI chat**: the developer adds a small HTML
snippet to their site and a conversational assistant appears, either as a
**floating popup** bubble or as an **inline embedded** chat inside a container of
their layout. The widget renders inside an **isolated iframe** served from
`https://widget.audin.ai`; the developer does not bundle any chat code. Its
appearance, the connected bot and the messages are configured in the Audin
**dashboard** (`app.audin.ai`) — the only thing the embed code controls is the
visitor's **user data**.

There is **no API key in the widget**: a widget is identified by a public
`data-widget-id` obtained from the dashboard.

---

## How to use this guide (meta-instructions)

Read these before you write any code.

1. **Do NOT assume the developer's stack.** There is no single "right"
   integration — it adapts to their site, framework (or no framework), CMS, and
   whether the page is a classic multi-page site or a single-page application
   (SPA).
2. **Do NOT just dump code.** The snippets below are *illustrative and
   adaptable*, not a copy-paste solution. Translate the *principles* into the
   developer's actual stack (their templating engine, CMS, framework component
   lifecycle).
3. **Interview first.** Ask the questions in the next section BEFORE
   implementing. Wait for the answers. The integration is shaped by them — the
   choice of mode (popup vs embedded) alone changes the snippet AND the available
   API surface.
4. **Respect the mode asymmetry.** Popup and embedded do **not** have the same
   API. Don't promise `open()`/`close()` on embedded, or `open`/`close` events on
   embedded — they don't exist (see "Per-mode API surface").
5. **Verify as you go.** After each meaningful step, confirm with the developer
   that it matches their app's conventions, that the snippet sits in the right
   place, and that the widget actually appears.

---

## Questions to ask the developer FIRST

Ask these (adapt wording to the conversation). Do not proceed to implementation
until you have enough answers to choose a concrete path.

### 1. Which mode — popup or embedded?

- A **floating popup** bubble in the bottom-right corner that opens the chat in an
  overlay (`window.AudinChat`, loaded from `widget.js`)? This is the classic
  "live chat" mode — just one snippet, no layout to manage.
- Or an **inline embedded** chat rendered inside a `<div>` of their own layout
  (`window.AudinEmbed`, loaded from `widget-embedded.js`, with a container
  `<div>` and a `data-target="#selector"` attribute)? This needs the developer to
  decide *where* in the layout it lives and to size the container.
- Both can **coexist** on the same page (a popup and an inline chat together) —
  they are distinct objects initialised separately.

The answer determines the snippet, which global object you use, and which methods
and events are available.

### 2. Framework / is it an SPA?

- Which **framework / environment** is the site? (plain HTML/CMS, WordPress,
  React, Vue, Angular, Svelte, Next.js, an existing template…)
- Is it a **single-page application**? If so, the loader/`init()` must run **once**
  for the app's lifetime — **not** on every route change — and you should call
  `destroy()` only on genuine teardown (e.g. a section that must hide the widget),
  not on every navigation. The widget and the in-progress conversation persist
  across internal navigations.
- Where in their templating does a snippet that should appear on every page live
  (a shared footer/layout, the CMS theme)?

### 3. Logged-in user data?

- Do they have an **authenticated user** whose `{ email, phone, fullName,
  customData }` should be passed to `init()` so the conversation is linked to a
  **lead** in their Audin Account? If yes, where do they read that user from
  (session, auth context, profile store)?
- For anonymous visitors, `init()` is simply called with no arguments — a
  per-browser `visitorId` is auto-managed in `localStorage`.
- Privacy: pass **only** data they have the right to pass (an authenticated user
  with an existing relationship). Don't put sensitive or unnecessary data into
  `customData`.

### 4. Where does the snippet live, and is there a CSP?

- The snippet must be placed **before `</body>`** on every page the widget should
  appear on. Where is that in their stack?
- Do they manage a **Content Security Policy**? If yes, they must allow
  `https://widget.audin.ai` for the script and the iframe, plus the widget's
  realtime connection. (See "Pitfalls".)

### 5. Do they need to react to events?

- Do they want to **react to widget events** (e.g. analytics/tracking)? In
  **popup** mode you can subscribe to `open`, `close` and `message`; in
  **embedded** mode the only event is `message`. If they ask for "fire when the
  chat opens" on an embedded widget, explain it never fires (embedded is always
  inline — no open/close state).

Use the answers to pick: which snippet (popup vs embedded), where it lives, whether
to pass `user` data, whether to call `init()` once-and-only-once (SPA), and which
events to wire up.

---

## Key facts & constraints

- **`data-widget-id` is public, not a secret.** It comes from the Audin dashboard
  (`app.audin.ai`) when you create/configure a widget, and it ends up in the
  page's public HTML — that's expected and safe. (Contrast: the **Account API
  Key** is admin-equivalent and must stay server-side — it is *not* used by the
  widget at all. If you find yourself reaching for an API key here, you've taken
  a wrong turn; the widget needs only the `data-widget-id`.)
- **Scripts are hosted by Audin.** The loader injects `https://widget.audin.ai/widget.js`
  (popup) or `https://widget.audin.ai/widget-embedded.js` (embedded). The
  developer hosts none of the chat code.
- **The widget is an isolated iframe** served from `https://widget.audin.ai` —
  isolated from the page's DOM and styles (and vice versa). For most sites no
  extra configuration is needed.
- **Appearance, bot and messages are dashboard-side, not code-side.** Colours,
  style, position, the connected bot/assistant and all texts are configured in
  the dashboard via the `data-widget-id`; the widget fetches that config at load.
  The **only** code-level option is the `user` data passed to `init()`. Do not
  look for code theming/locale/text options — they don't exist.

---

## Integration principles (apply to the chosen stack)

These are principles, not a fixed recipe. The snippets are **ADAPTABLE EXAMPLE**
— rewrite them idiomatically for the developer's framework/CMS. Use the literal
placeholder `IL-TUO-WIDGET-ID` until you have the real one from the dashboard.

### Principle 1 — Popup: async loader → `AudinChat.init()`

For the floating-popup mode, drop the async loader before `</body>`. It injects
`widget.js` with the `data-widget-id`, and on `onload` calls `AudinChat.init()`.

```html
<!-- ADAPTABLE EXAMPLE — popup. Place before </body>. -->
<script>(function(w,d,s,u,i){var f=d.getElementsByTagName(s)[0],
j=d.createElement(s);j.async=true;j.src=u;
j.setAttribute('data-widget-id',i);
j.onload=function(){w.AudinChat.init()};
f.parentNode.insertBefore(j,f);
})(window,document,'script','https://widget.audin.ai/widget.js','IL-TUO-WIDGET-ID');</script>
```

### Principle 2 — Embedded: a container `<div>` + loader + `data-target` → `AudinEmbed.init()`

For inline mode, add a container `<div>` where the chat should appear (give it a
size), then the loader for `widget-embedded.js` with a `data-target` pointing at
that container, calling `AudinEmbed.init()` on `onload`.

```html
<!-- ADAPTABLE EXAMPLE — embedded. The container must exist when init() runs. -->
<div id="audin-chat" style="width: 400px; height: 600px;"></div>
<script>(function(w,d,s,u,i){var f=d.getElementsByTagName(s)[0],
j=d.createElement(s);j.async=true;j.src=u;
j.setAttribute('data-widget-id',i);
j.setAttribute('data-target','#audin-chat');
j.onload=function(){w.AudinEmbed.init()};
f.parentNode.insertBefore(j,f);
})(window,document,'script','https://widget.audin.ai/widget-embedded.js','IL-TUO-WIDGET-ID');</script>
```

The container can have any `id` and size; just keep `data-target` matching its
selector, and give it non-zero dimensions.

### Principle 3 — Pass the logged-in user for lead matching

If the developer has an authenticated user, pass it to `init()`. Read the values
from their app state and inject them at init time. All fields are optional —
pass only what they actually have.

```js
// ADAPTABLE EXAMPLE — read the user from THEIR auth/session.
const currentUser = getLoggedInUser(); // from the developer's system

AudinChat.init({               // or AudinEmbed.init({...}) — same options object
  user: {
    email: currentUser.email,
    phone: currentUser.phone,     // E.164 recommended, e.g. +39123456789
    fullName: currentUser.name,
    customData: { plan: currentUser.plan } // free key/value context
  }
});
```

For an anonymous visitor, just call `init()` with no arguments. A per-browser
`visitorId` is auto-managed in `localStorage` regardless.

### Principle 4 — Call `init()` once (especially in an SPA)

`init()` is **idempotent**: calling it more than once is a **no-op**. In an SPA,
run the loader/`init()` once at app startup and **let the widget live** across
route changes — do not re-init or mount/unmount it per navigation (recreating the
iframe is wasteful and interrupts the conversation). If a specific section must
hide the widget entirely, use `destroy()` there and `init()` again to restore it;
to merely show/hide the popup, prefer `open()`/`close()` (popup only).

```js
// ADAPTABLE EXAMPLE — SPA: initialise once, e.g. in a top-level effect/bootstrap.
let initialised = false;
function ensureWidget() {
  if (initialised) return;        // guard: init() is a no-op anyway, but don't spam it
  AudinChat.init({ user: currentUser ? { email: currentUser.email } : undefined });
  initialised = true;
}
```

### Principle 5 — Subscribe to events (mode-aware)

Subscribe with `on(event, cb)` on the right global object, after the script has
loaded (the same place you call `init()`). **Popup** emits `open`, `close`,
`message`; **embedded** emits `message` only.

```js
// ADAPTABLE EXAMPLE — popup.
AudinChat.on("open", () => {/* e.g. track: chat opened */});
AudinChat.on("close", () => {/* e.g. track: chat closed */});
AudinChat.on("message", (data) => {/* a message arrived in the conversation */});

// ADAPTABLE EXAMPLE — embedded (message only).
AudinEmbed.on("message", (data) => {/* a message arrived in the inline chat */});
```

Treat the `message` payload (`data`) as an **opaque value** for simple
integrations (logging, activity tracking) — its internal structure is not part of
the public surface and may change.

---

## Per-mode API surface (ACCURATE — the two modes are NOT at parity)

The popup and embedded objects share the core model (`init`, `on`, `destroy`) but
differ deliberately. Do not claim parity.

### `AudinChat` — popup (`widget.js`)

- **Methods:** `init(options?)`, `open()`, `close()`, `destroy()`, `on(event, cb)`.
- **Events:** `open`, `close`, `message`.
- `open()` / `close()` open or close the bubble programmatically (e.g. from your
  own "Help" button) without the user clicking.

### `AudinEmbed` — embedded (`widget-embedded.js`)

- **Methods:** `init(options?)`, `destroy()`, `on(event, cb)` — **ONLY**. There is
  **NO `open()` / `close()`** (it's always inline).
- **Events:** `message` — **ONLY**. There is **NO `open` / `close`** event.
- Requires the `data-target` container to exist in the DOM when `init()` runs.

### `init` options (both modes)

```ts
init(options?: {
  user?: {
    email?: string;
    phone?: string;
    fullName?: string;
    customData?: Record<string, unknown>;
  };
}): void
```

`user` is the **only** documented option. There are no appearance/locale/text
options — those are dashboard-side. The `visitorId` is auto-managed in
`localStorage` (no option for it).

---

## Pitfalls

- **Snippet must be before `</body>`.** If it's somewhere it doesn't run (or isn't
  in the page's actual template), the widget never loads.
- **`init()` must actually be called.** The dashboard's "base" snippet does it in
  the loader's `onload` (`w.AudinChat.init()` / `w.AudinEmbed.init()`). If you
  customise the snippet, make sure `init()` still runs after the script loads.
- **Calling `init()` more than once is a no-op.** Don't rely on a second `init()`
  to apply new `user` data; initialise once with the data you have.
- **Embedded needs its container.** The `<div>` referenced by `data-target` must
  exist (with non-zero size) when `init()` runs, or the widget can't mount; the
  console shows something like `[AudinEmbed] Target container not found: <selector>`.
- **CSP can block the script/iframe/connection.** With a restrictive Content
  Security Policy, allow `https://widget.audin.ai` for `script-src`, for
  `frame-src` (or `child-src`), and for the realtime `connect-src`. Symptom: the
  widget doesn't appear and the console shows CSP errors.
- **Appearance is dashboard-side.** Don't search for code-level theming, locale or
  text options — change them in the dashboard via the `data-widget-id`.
- **Subscribing to `open`/`close` on embedded never fires.** Embedded is always
  inline (no open/close state); only `message` fires. Use the right object
  (`AudinChat` vs `AudinEmbed`) and the right event for the mode.
- **Missing `data-widget-id`.** If the placeholder `IL-TUO-WIDGET-ID` is left in,
  the console shows e.g. `[AudinChat] Missing data-widget-id` (or `[AudinEmbed] …`)
  and nothing loads.

---

## References

- Widget overview — https://doc.audin.ai/docs/widget
- Installation (the three snippets) — https://doc.audin.ai/docs/widget/installazione
- User data & lead matching — https://doc.audin.ai/docs/widget/dati-utente
- API & events (per-mode surface) — https://doc.audin.ai/docs/widget/api-ed-eventi
- Configuration & behavior (dashboard vs code, SPA, CSP) — https://doc.audin.ai/docs/widget/configurazione-e-comportamento
- Troubleshooting & FAQ — https://doc.audin.ai/docs/widget/troubleshooting
- Audin dashboard (create the widget, get the `data-widget-id`) — https://app.audin.ai

The widget pages above are the authoritative reference for the embed snippets,
the per-mode API/events and the dashboard-vs-code boundary. When in doubt, defer
to them.
