Install the widget

Two script tags, pasted once. The loader is served for your workspace, downloads without holding up your page, and pulls in the full chat when a visitor opens it, never before your page has loaded.

Last updated

The snippet#

Copy it from Settings, Installation in the dashboard, where it is filled in for your workspace. Paste it just before the closing </body> tag on every page that should show the chat. Most sites paste it once, into a shared layout or footer template.

Install snippet
<!-- Bettercom messenger -->
<script>
  window.bettercomConfig = { clientId: 'YOUR_WORKSPACE_ID' }
</script>
<script async src="https://YOUR_HOST/v1/widget/YOUR_WORKSPACE_ID"></script>

YOUR_WORKSPACE_ID is your workspace's short id, also shown on its own under Settings, Overview, Workspace ID. The Widget API reference calls the same value the organization short id. YOUR_HOST is the widget host, the address already in the snippet that Settings, Installation gives you. It is not the dashboard's address.

What each tag does#

TagWhat it does
The inline <script> Sets window.bettercomConfig, which the launcher reads once when it runs. The loader already has your workspace ID built in, so this tag is only needed to pass other fields: darkMode, user or sound.
<script async src=".../v1/widget/ID"> The launcher, served per workspace with your ID baked in. async lets the browser keep parsing and rendering your page while the file downloads.

So on a page that needs no options, the script tag alone is enough:

Loader only
<script async src="https://YOUR_HOST/v1/widget/YOUR_WORKSPACE_ID"></script>

Single-page apps#

Add the snippet once, to the HTML shell (for example index.html). The launcher follows in-page navigation on its own: it wraps history.pushState and history.replaceState and listens for popstate and hashchange, so every route change is recorded as a page view without router hooks.

If you load the script later from your own code, install the async stub first so calls made before the launcher arrives are queued instead of throwing.

Identify signed-in users#

If your visitors sign in to your site, pass their name and email so your team sees who they are and the widget skips its own identify form:

Pre-identified visitor
<script>
  window.bettercomConfig = {
    clientId: 'YOUR_WORKSPACE_ID',
    user: { name: 'Ada Lovelace', email: 'ada@example.com' }
  }
</script>

You can also set or change the identity at runtime with bettercom('identify', user). On a page that passes a user, the launcher fetches a small helper (helper.js, 2.3 KB compressed) after your page's load event to send it, so your team sees who the visitor is even if they never open the chat. The full chat still loads only when the visitor opens it. Without a userHash the identity is not verified, so do not treat it as authentication. The details, and how to verify it, are in the Widget API reference.

Allowed domains#

Under Settings, Installation, Allowed domains in the dashboard, list the sites the widget may load on. An entry can be an exact host such as acme.com or app.acme.com, or *.acme.com for any subdomain (not the apex).

Once the list has at least one entry, the loader and every widget request from any other origin are refused with HTTP 403, and the widget does not appear there. While the list is empty the widget loads anywhere and the dashboard shows a warning. localhost and 127.0.0.1 are always allowed, so local development keeps working.

Check the install#

  1. Open a page with the snippet.

    The launcher appears once the page has loaded: one second after the load event by default, or after your page's Largest Contentful Paint if you turned on Wait for the page to finish drawing (Settings, Widget, Loading).

  2. Watch the network panel.

    In your browser's developer tools, filter requests by widget. You should see the loader, then a settings request. conversation.js must not appear yet. If the page passes a signed-in user or an unread reply is waiting, helper.js appears after the load event; nothing else does.

  3. Open the chat.

    Click the launcher. conversation.js downloads now, once per page view, and the panel opens.

  4. Confirm in the dashboard.

    The setup wizard's install step switches to Widget detected after the widget first loads with your snippet, on localhost or your live domain.

If the launcher does not appear#

  • The loader answers 403. Either this page's domain is not on your allowed domains list, or the workspace ID in the script URL does not exist (for example a copy that lost a character). The response body says which. Add the domain (or a *. entry for its parent), or copy the snippet again from Settings, Installation.
  • The loader answers 400. The workspace ID in the script URL holds a character it cannot have, such as a space or a quotation mark picked up while copying. Copy the snippet again.
  • The browser blocked the script or the chat connection. Your site sends a Content Security Policy. Add the widget's sources as described in Performance and CSP.
  • Nothing has appeared yet. The launcher waits for the page to finish loading first. A page that keeps loading resources delays it; with Wait for the page to finish drawing on, it waits for at most 8 seconds.
  • It worked before and stopped. Check Service status: it shows whether the chat widget is answering right now, and whether your own browser can reach it.

Remove the widget#

Delete the two tags. To take the widget off a page without removing them, for example when a visitor signs out, call window.bettercom('shutdown'): it removes the widget and forgets the visitor on this browser. A later bettercom('boot'), or the next page load while the tags remain, starts it again as a new anonymous visitor.