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

# Install the widget

> Configure and embed the live chat widget on your site.

The chat widget lets website visitors message your team in real time. You
configure its appearance and behaviour in **Settings → Chat Widget**, then
drop the generated snippet on your site.

<Note>
  The widget is backed by a **Chat Widget channel**. If you haven't created one yet, the settings screen shows a
  **Create Your Chat Widget** card. Enter a **Widget Name** (defaults to `Support Chat`) and click **Create Chat
  Widget**. Once the channel exists, the full settings tabs below appear.
</Note>

## Configuration

All settings live under **Settings → Chat Widget**, split across tabs.
Changes are saved with the **Save changes** bar that appears at the bottom of
the page once you edit something. They are not saved automatically as you type.

<Tabs>
  <Tab title="Settings">
    The **Settings** tab holds the core configuration.

    **Appearance**

    | Field | Control | Default | What it does |
    | - | - | - | - |
    | Greeting | Multi-line text | `Hi! How can we help?` | The first message visitors see when they open the widget. |
    | Suggested questions | List | Two starter questions | Up to four one-tap questions under the greeting, and the **Offer "Talk to a person"** switch. See [Suggested questions and page prompts](/comms/chat/page-prompts). |
    | Brand color | Color picker | `#6366f1` | The header and the launcher button. |
    | Accent color | Color picker | Same as brand color | Buttons, the visitor's own messages, and quick replies. Leave it empty to match the brand color. |
    | Theme | Auto / Light / Dark | Auto | **Auto** follows your website's light or dark mode. **Light** and **Dark** fix it. |
    | Header | Solid / Tint / Minimal | Solid | **Solid** fills the header with the brand color, **Tint** uses a pale wash of it, and **Minimal** keeps the header plain with a brand-colored avatar. |
    | Corners | Sharp / Soft / Round | Round | How rounded the panel, messages, and buttons are. |
    | Launcher | Ask bar / Icon / Icon and label | Ask bar for new widgets, Icon for widgets made before September 30, 2026 | **Ask bar** shows an "Ask anything…" bar at the bottom center of the page. Clicking it grows the chat up out of the bar. **Icon** shows a round chat button in a corner, and **Icon and label** adds a label beside the icon. |
    | Bar placeholder / Launcher label | Text | `Ask anything…` / `Chat with us` | The text in the bar, or beside the icon. Hidden for **Icon**. Up to 40 characters. |
    | Show the bar | After scrolling / Right away | After scrolling | For **Ask bar** only. **After scrolling** keeps the bar out of the way of your page's first screen until a visitor scrolls half a screen down; it shows straight away when a chat is under way, a page prompt appears, or the page is too short to scroll. Once shown, it stays for the rest of the visit. |
    | Position | Bottom left / Bottom right | Bottom right | Where the corner launcher sits. Hidden for **Ask bar**, which is always at the bottom center. |
    | Panel | Solid / Glass | Glass for new widgets, Solid for widgets made before September 30, 2026 | **Glass** lets your page show through the open chat, softly blurred. Messages and the message box stay solid so they're easy to read. On a phone the chat is always solid. |
    | Message sounds | Switch | On | A soft tick when a visitor sends a message, and a chime when a reply arrives. Sounds only play while the chat is open and the tab is in view. |

    If a visitor moves to another page while the chat is open, it stays open on the new page, with anything they typed and hadn't sent. On a phone it closes instead, so it doesn't cover every page they visit.

    Visitors put the chat away with the **⌄** in its header. The **⋯** beside it starts a new chat, opens past chats, or ends this one. **End chat** asks once, then closes the conversation for your team as if a teammate had closed it, and offers **Start a new chat** in place of the message box, with a 👍 / 👎 asking whether the chat helped. A rating shows in the conversation as a note only your team sees ("Visitor rated this chat: Helpful 👍"). The same happens when a teammate closes a chat. While a chat is under way, the **Ask bar** says "Continue your chat…" instead of "Ask anything…", and shows the newest reply with a dot when one arrives while the chat is put away.

    A live preview of the real widget sits beside these controls and updates as you change them, before you save. Text on the brand and accent colors is picked for you: a light color such as yellow gets dark text, so the header and buttons stay readable.

    **Page prompts** and **Office hours and replies** come next. See
    [Suggested questions and page prompts](/comms/chat/page-prompts#page-prompts) and
    [Office hours and email follow-up](/comms/chat/office-hours).

    **Features**

    | Toggle | Default | What it does |
    | - | - | - |
    | Live Chat | On | Lets visitors chat with your team in real time. |
    | Meeting Scheduling | Off | Lets visitors book meetings directly from the widget. |
    | File attachments | On | Lets visitors send screenshots and documents, up to 10 MB each. See [Files and Emoji](/comms/chat/files). |

    **Visitor Sync**

    | Field | Control | What it does |
    | - | - | - |
    | Sync Visitors to Object | Searchable dropdown (clearable) | The object visitors are added to once they give a name or email. Leave it empty to use your **Contacts** object. |
    | Only add visitors your server has identified | Switch (off) | Only visitors with an [identity token](/dev/api-reference/widget-identity) are added to your CRM. Everyone can still chat. |

    <Tip>
      Pair **Sync Visitors to Object** with the **Intake Form** tab so the
      contact details a visitor submits are written straight to a record. See
      [Visitors](/comms/chat/visitors) for how synced visitor data behaves.
    </Tip>

    **Known customers** sets what a customer your server identified sees: a greeting by
    name, their record's owner, and booking on the owner's calendar. See
    [Known customers](/comms/chat/known-customers).
  </Tab>

  <Tab title="AI Agent">
    The **AI Agent** tab turns on the assistant that answers visitors from your website,
    sets its persona, and decides when it hands a chat to your team. See
    [AI Agent](/comms/chat/ai-agent).
  </Tab>

  <Tab title="Knowledge">
    The **Knowledge** tab scans your website for the AI agent and the widget's Help tab,
    and lists the sites you've scanned. See
    [What it answers from](/comms/chat/ai-agent#what-it-answers-from) and
    [Help in the widget](/comms/chat/help).
  </Tab>

  <Tab title="Intake Form">
    The **Intake Form** tab chooses what a visitor is asked in the chat. The questions
    appear as a card after their first message, so nobody fills anything in before they
    can ask.

    **Pre-Chat Form** serves one of your [forms](/comms/forms/overview). Its answers
    create a record with an owner, the same as the form's public link, and show on the
    conversation. Pick a published form, or create one from the dropdown. A draft form
    isn't shown to visitors until you publish it.

    **Legacy pre-chat questions** is the widget's own question list. Its answers only set
    the visitor's name, email and phone. Once you pick a form, these questions stop
    showing.

    * Add questions with **+ Name**, **+ Email**, **+ Phone** and **+ Custom**.
    * Each question has a label, a type (Text, Email, Phone or Select), an optional
      placeholder hint, and a **Required** switch.
    * A Select question takes a comma-separated list of options.

    Answers can start workflows that react to a visitor sharing their contact info. The
    card is skipped for a visitor your server identified, a visitor who already has a
    conversation, and a visitor whose name or email your site passed in. A returning
    visitor isn't asked again for a name, email or phone number they gave before.
  </Tab>

  <Tab title="Workflows">
    The **Workflows** tab surfaces widget-related workflows and starter
    templates. It links out to the full workflow editor. You can automate
    visitor interactions around these moments:

    | Trigger | Fires when |
    | - | - |
    | A visitor starts a chat | A visitor opens a new chat. |
    | A visitor shares contact info | A visitor submits the intake form or otherwise provides their details. |
    | A message matches a keyword | A visitor's message contains a keyword you configured. |
  </Tab>

  <Tab title="Preview">
    The **Preview** tab renders the real widget using your current, unsaved appearance and intake settings. Click the
    launcher and send a test message to see how it looks. The preview is local only. Test messages are not delivered to
    your team.
  </Tab>

  <Tab title="Install">
    The **Install** tab generates the embed code.

    * A **publishable API key** is required to embed the widget on an
      external site. If you don't have one, the tab shows a **No Publishable
      Key** notice with a **Create Application & Key** button that generates
      it for you.
    * Once a key exists, the tab shows your **API Key**, a **React /
      Next.js** snippet, an **HTML / Script Tag** snippet, and your
      **Channel ID**, each with a copy button.
  </Tab>
</Tabs>

## Embedding the widget

After generating a key on the **Install** tab, copy one of the snippets it
provides and add it to your site.

<Steps>
  <Step title="Create a publishable key">
    On the **Install** tab, click **Create Application & Key** if you don't
    already have a publishable key.
  </Step>

  <Step title="Copy the snippet">
    Copy either the **React / Next.js** component snippet or the **HTML /
    Script Tag** snippet. Both already include your API key and Channel ID.
  </Step>

  <Step title="Add it to your site">
    Paste the HTML snippet just before the closing `</body>` tag, or render
    the React component in your app. The widget appears in the position you
    chose under **Settings → Appearance**.
  </Step>
</Steps>

<Tip>
  Always copy the snippet straight from the **Install** tab. It is wired to your specific widget. It already includes
  your publishable key and channel, so you don't need to fill in any IDs by hand. The widget picks up the greeting
  and appearance you set on the **Settings** tab.
</Tip>

### How the script loads

The HTML snippet loads a small script, about 7 KB, that draws the launcher. The
chat itself downloads when a visitor reaches for it: their pointer moves over the
launcher, they tab to it, or they tap it. Until then your pages load as if the
widget weren't there, and nobody is counted as a visitor. A visitor who clicks
before the chat has arrived sees a spinner in the launcher, and the chat opens as
soon as it's ready.

The chat loads straight away for a visitor with a conversation under way, so your
team's replies keep arriving, and on a page with a [page note](/comms/chat/page-prompts)
due to show.

To host the files yourself, serve `widget.js` and `widget-app.js` from the same
folder. Both ship in the `@custral/widget` package.

### Control the chat from your page

The script adds a `CustralChat` object to your page. Every call works before the
chat has loaded: `open()` loads it first.

| Call | What it does |
| - | - |
| `CustralChat.open()` | Opens the chat. |
| `CustralChat.openNewMessage("…")` | Opens a new chat with that message in the box, ready to send. Nothing is sent until the visitor sends it. |
| `CustralChat.close()` | Closes the chat. |
| `CustralChat.toggle()` | Opens the chat if it's closed, and closes it if it's open. |
| `CustralChat.hide()` | Takes the launcher off the page. `open()` still works, so your own buttons can open the chat. |
| `CustralChat.show()` | Puts the launcher back. |
| `CustralChat.on(event, handler)` | Calls `handler` when something happens, and returns a function that stops it. |

| Event | `handler` receives | When |
| - | - | - |
| `open` | nothing | The chat opens. |
| `close` | nothing | The chat closes. |
| `reply` | `{text, from, name}` | A reply arrives from your team (`from: "team"`) or your AI agent (`from: "assistant"`). |
| `unread` | a number | The count of replies the visitor hasn't seen changes. It counts up while the chat is closed or the tab is in the background, and goes back to 0 when they open the chat. |

```js theme={null}
CustralChat.on("unread", (count) => {
  document.title = count > 0 ? `(${count}) Acme` : "Acme";
});
```

To open the chat from a button or a link with no code at all, add
`data-custral-open`. With a value, it opens a new chat with that message ready
to send:

```html theme={null}
<button data-custral-open>Contact support</button>
<a href="/contact" data-custral-open="Question about pricing">Ask about pricing</a>
```

The chat starts loading as soon as a visitor points at one of these. If the chat
can't open (for example, the key was removed), a link with `data-custral-open`
still goes to its own `href`. When the chat closes, focus goes back to the button
or link that opened it.

### Identifying a known visitor

If your visitors sign in to your site, tell the widget who they are. Mint an
[identity token](/dev/api-reference/widget-identity) on your server and pass it as
`identityToken`, in `CustralChat.init` or on `<CustralChatWidget>`. The visitor is then
recognised on every device and treated as a [known customer](/comms/chat/known-customers):
greeted by name, with their record's owner.

You can also pass a `visitor` with a name and email. It skips the intake questions, but
anyone could send any email that way, so it only fills empty fields on a record and never
replaces what's there.

### Signing a visitor out

The widget remembers its visitor in the browser, so a returning visitor picks
up where they left off. On a shared computer, that means the next person to use
your site picks up where the **previous** person left off, conversations
included. When someone signs out of your site, have the widget forget them:

```js theme={null}
function signOut() {
  // `?.` so a widget script that failed to load never breaks your sign-out.
  window.CustralChat?.reset();
  // …then your own sign-out
}
```

`reset()` forgets this browser's visitor and restarts the widget as a new,
anonymous visitor. It does not reuse the `identityToken` the widget was loaded
with. To sign the next person in without a page load, remove the widget and
load it again with their token:

```js theme={null}
CustralChat.shutdown();
CustralChat.init({publishableKey: "pk_…", identityToken: nextUsersToken});
```

In a React app, call `resetCustralChat()` from `@custral/widget` instead. See the
[React SDK](/dev/sdks/react#signing-a-visitor-out).

<Note>
  Conversations of a visitor signed in with an [identity token](/dev/api-reference/widget-identity) stay theirs even
  without `reset()`: the widget only reopens them on a page that passes that person's token. `reset()` covers everything
  else, such as a chat from someone who never signed in, or a site that identifies visitors with `visitor` instead of a
  token.
</Note>

## Security

Anything a visitor types into the chat, an email included, is unverified: anyone can
type a customer's address. To vouch for a signed-in customer, your server mints an
identity token with `POST /v1/widget/visitor-tokens`, using your secret key, and your page
passes the short-lived token to the widget. Never mint it in the browser. See
[Widget identity](/dev/api-reference/widget-identity).

A visitor with a token can replace the details on their record, where a typed email only
fills empty fields. To keep everyone else out of your CRM, turn on **Only add visitors
your server has identified** under **Visitor Sync**.

## Troubleshooting

| Symptom | Likely cause | What to check |
| - | - | - |
| Settings screen only shows a "Create Your Chat Widget" card | No Chat Widget channel exists yet | Enter a name and click **Create Chat Widget**; the full tabs appear once the channel is created. |
| Changes don't take effect | Settings aren't saved automatically | Click **Save changes** on the bar at the bottom of the tab. |
| Saved appearance or page notes don't show on your site | Your site's copy is refreshed every few minutes | Wait 5 minutes, then reload the page. |
| Install tab shows "No Publishable Key" | No publishable API key has been generated | Click **Create Application & Key** on the **Install** tab, then copy the generated snippet. |
| Widget appears on the wrong side of the page | **Position** set to the other option | Set **Position** under **Settings → Appearance** to Bottom Right or Bottom Left and save. |
| Live chat doesn't connect for visitors | **Live Chat** feature is off | Enable the **Live Chat** toggle under **Settings → Features** and save. |
| Visitors aren't appearing as records | No name or email given yet, or only identified visitors are added | Ask for an email on the **Intake Form** tab, and check the switch under **Visitor Sync**. See [Visitors](/comms/chat/visitors). |
| The intake card never shows | No published form and no legacy questions, or the visitor skips it | Pick a published form on the **Intake Form** tab. The card shows after the first message, to visitors who aren't identified yet. |

## Related

<CardGroup cols={2}>
  <Card title="Chat & Widget overview" icon="comment" href="/comms/chat/overview">
    How the embeddable widget and human hand-off fit together.
  </Card>

  <Card title="Visitors" icon="users-viewfinder" href="/comms/chat/visitors">
    How widget visitors are tracked and synced to records.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.