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

# Web widget

> One script tag puts the same agent that answers WhatsApp on your own website.

The web widget is a chat launcher you add to your site with a single script tag.
A visitor clicks it, types, and is answered by **the same agent** that runs your
outreach and answers [inbound WhatsApp](/inbound) — same knowledge base, same
policies, same voice, and the same shared inbox when it hands off to a person.

Nothing about it is a separate support bot with its own configuration to keep in
sync. What is different from WhatsApp is who the visitor is: nobody signs in, so
there is no phone number and no email address until the conversation produces
one.

## Install

Copy the snippet from the Boom app — **Settings → Channels → Web widget →** your
widget — and paste it before the closing `</body>` tag of every page that should
show the widget. It looks like this:

```html theme={null}
<script async src="https://www.useboom.ai/widget/v1/w/bw_live_YOUR_INSTALL_KEY"></script>
```

Copy it rather than typing it. The origin in the snippet is pinned by the
deployment you are looking at, so a hand-written one will point at the wrong
environment.

The install key in that URL is **public** — it ships in your page source, and it
has to. It identifies the widget; it does not authorize anything. What controls
where your widget may be embedded is the origin list below.

<Note>
  `async` is deliberate. The script does not block your page, and the launcher
  button appears a moment after the rest of the page has painted.
</Note>

## Allow your site's origin

A widget only renders on origins you list, and the list starts empty. Add every
origin that will host it under **Allowed origins** on the same settings page.

Matching is **exact string equality**, on the origin a browser actually sends:

| Add                                                       | Not                               |
| --------------------------------------------------------- | --------------------------------- |
| `https://example.com`                                     | `example.com` (no scheme)         |
| `https://www.example.com` — separately, if you serve both | `*.example.com` (no wildcards)    |
| `https://staging.example.com`                             | `https://example.com/` (no paths) |

So `https://example.com` does **not** cover `https://www.example.com`, and a
staging domain needs its own entry.

<Warning>
  A widget with an empty origin list installs cleanly and never opens. The
  launcher button appears — the script that draws it does not know which site
  loaded it — and clicking it produces an empty panel. If that is what you are
  seeing, this list is the first thing to check.
</Warning>

## If your site sets a Content-Security-Policy

Two directives need our origin. Add them to whatever your site already sends:

```
script-src  https://www.useboom.ai
frame-src   https://www.useboom.ai
```

`script-src` covers both the snippet above and the launcher bundle it loads.
`frame-src` covers the chat panel, which is an iframe served from our origin.

You do **not** need a `connect-src` entry. The panel makes its own network
requests, but it makes them from inside that iframe, so they are governed by our
policy rather than your page's. The launcher itself — the button and the greeting
bubble, which are the only parts living in your document — makes no requests at
all.

Serve the page over HTTPS. The widget's session cookie is `Secure`, so a visitor
on an `http://` page will not keep a conversation across reloads.

## Customize how it looks

Everything visual lives under **Appearance** on the same settings page, with a live
preview beside it. You pick one accent colour and Boom derives the rest, so the text
drawn on it stays legible whatever you choose:

| Setting                      | What it changes                                                                                                                                                     |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Accent colour, logo, icon    | The launcher button, the panel header, and the avatar on each reply                                                                                                 |
| Position and offset          | Which corner the launcher sits in, and how far off the edges                                                                                                        |
| Theme                        | `Light`, `Dark`, or `Auto` — `Auto` follows the visitor's own system setting                                                                                        |
| Title, greeting, placeholder | The panel header, the bubble shown before the visitor writes, and the composer's hint text                                                                          |
| Launcher label               | The words beside the icon on the button — keep it to a few, since the button truncates rather than growing across your page. Leave it blank for the icon on its own |
| Suggested questions          | Up to four one-tap chips shown on an empty panel                                                                                                                    |

Every piece of copy is per-language — English, Spanish, and Portuguese — and the
visitor gets the language their browser asks for, falling back to English for
anything you leave blank. A suggested question left blank in one language simply
does not appear for those visitors.

<Note>
  A suggested question **is** the message. Tapping one sends that exact text as the
  visitor's first message, so write them the way a customer would ask, and expect the
  agent to answer them the way it answers anything else — they are not scripted
  replies.
</Note>

Below the composer the panel shows a **"Powered by Boom"** link. That line is not
configurable.

<Note>
  The panel does not tell visitors on its own that they are talking to an AI. Where you
  need that disclosure — the EU AI Act asks for it wherever it is not otherwise obvious —
  put it somewhere you control. The greeting is the natural place: it is the first thing
  a visitor reads, and it is yours to write in all three languages.
</Note>

## What a visitor's session is

A visitor is anonymous. The only thing tying somebody who comes back to the
transcript they left is a cookie on our origin, set the moment they send a first
message and good for **30 days**.

That cookie is `Partitioned` (CHIPS), which has two consequences worth knowing:

* It survives in browsers that block ordinary third-party cookies, which is now
  most of them.
* It is scoped to **your** site. The same person visiting two different customers
  of ours is two unrelated visitors, and the same widget embedded on two of your
  own domains keeps two separate conversations. The widget is not a cross-site
  identity.

<Note>
  A visitor who blocks cookies for our origin can still use the widget, but every
  message starts a new conversation, and the panel is empty again after a reload.
  There is no login to fall back on.
</Note>

## Controlling the widget from your page

The snippet installs one global. It works from the moment the tag runs, so you
can call it from a button in your own header without waiting for anything to
load:

```js theme={null}
Boom('open');    // also 'close' and 'toggle'
```

Re-running the snippet is safe. A single-page app that re-executes it on a route
change gets the widget that is already there, not a second button.

## When nothing answers

Answering is a setting, not a default, and it resolves exactly the way
[inbound](/inbound) does: per channel, falling back to your organization.

<Note>
  If no agent is configured to answer the widget, or the configured agent is
  disabled, the visitor's message is still received and stored — it waits in the
  shared inbox for a person — and the panel stops waiting and offers a retry
  rather than spinning. Boom does not pick an agent on your behalf, because
  guessing which agent should speak for you is worse than staying quiet.
</Note>

Replies a person sends from the inbox arrive in the visitor's open panel in
realtime, minutes later if that is when they are written.

## Rate limits

Per visitor, **30 messages a minute**. Per widget, **120 first-messages a
minute** from visitors who have no session yet — set far above a real site's
arrival rate, so it throttles a loop rather than a launch. A throttled request is
refused before anything is stored.

## Rotating the install key

**Rotate** on the settings page mints a new key and invalidates the old one
immediately. The snippet on your site then points at a key that no longer
resolves, and the widget stops appearing until you paste the new one — so rotate
when you are ready to deploy the change, not before.

Rotating does not touch conversations, transcripts, or appearance.

## Troubleshooting

| What you see                             | Almost always                                                                                      |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------- |
| No launcher button at all                | The snippet is not on the page, or the install key was rotated and the page still has the old one. |
| Launcher appears, panel opens empty      | Your origin is not in the allowed-origins list — check scheme, `www`, and port.                    |
| Panel opens, visitor's message stays put | Your CSP is missing `frame-src`, or a `script-src` gap kept the launcher's own bundle out.         |
| Transcript gone after a reload           | Cookies for our origin are blocked, or the page is served over `http://`.                          |
| Message sent, nothing ever replies       | No answering agent is configured for the channel, or the one configured is off.                    |

## Related

<CardGroup cols={2}>
  <Card title="Inbound conversations" icon="message-square" href="/inbound">
    The same answering behavior, reached from WhatsApp instead of your site.
  </Card>

  <Card title="The agent" icon="user" href="/the-agent">
    What briefs the agent that answers, and when it hands off to a person.
  </Card>

  <Card title="Extraction" icon="table" href="/extraction">
    Why a cold widget conversation has a transcript but no typed fields.
  </Card>

  <Card title="Use MCP" icon="plug" href="/use-mcp">
    Read widget conversations from an AI tool.
  </Card>
</CardGroup>
