InstallAll pages

Install

One script tag, one public key, and one line in an allowlist. The whole install is reversible from the dashboard, and nothing on this page needs a deploy once it is done.

Four steps#

  1. 01

    Copy your website key

    In the dashboard, open Websites, pick the site, and copy the key under Website key. It starts with wk_.

    The key is public and belongs in your page source. It identifies a website; it does not authenticate anyone. Nothing sensitive is reachable with the key alone.

  2. 02

    Paste the snippet before </body>

    The same snippet, pre-filled with your key, is on the website’s dashboard page under Install the widget.

    the embed snippet
    <script>
      window.$connect = window.$connect || [];
      window.CONNECT_WEBSITE_KEY = "wk_xxxxxxxxxxxxxxxxxxxxxxxx";
      // optional: window.CONNECT_TOKEN_ID = "<per-user token from your backend>";
      (function () {
        var s = document.createElement("script");
        s.src = "https://<your-connect-host>/widget/v1.js";
        s.async = 1;
        document.head.appendChild(s);
      })();
    </script>
  3. 03

    Allow your site’s origin

    Add every origin the widget will run on to Allowed origins on the same dashboard page. Until you do, the widget refuses to boot. See Allowed origins for the exact format — this is the step that trips people up.

  4. 04

    Reload and send a message

    The launcher appears in the bottom corner. Send yourself a message and watch it arrive in the dashboard inbox.

Allowed origins#

Connect checks the origin of the page the widget is embedded in, and boots only for origins on the website’s list. An origin that is not on it gets a 403 and the widget quietly does nothing.

The match is an exact string comparison, which means:

  • Scheme, host, and port, and nothing else. No path, no trailing slash. https://acme.com/ does not match https://acme.com.
  • https://acme.com and https://www.acme.com are different origins. So are http:// and https://, and so is every subdomain. List each one.
  • A non-default port is part of the origin: http://localhost:5173, not http://localhost.
  • An empty list allows nothing. A brand-new website boots nowhere until you add the first origin.
allowed origins — one per line
https://acme.com
https://www.acme.com
https://shop.acme.com
http://localhost:5173
staging and local development are ordinary entries · remove them when you are done

Content-security policy#

If your site sends a Content-Security-Policy header, three directives need to know about your Connect host.

script-srcrequired
Your Connect host, so the loader at /widget/v1.js can run.
frame-srcrequired
Your Connect host, so the chat window at /widget/frame.html can be framed. Some older browsers read child-src instead.
img-srcrecommended
The launcher shows the available agent’s avatar, and that image is loaded by your page. Allow https://gravatar.com, plus wherever your team’s profile pictures are hosted if they signed in with Google or uploaded their own. Without it the launcher falls back to its icon — the widget still works.
a policy that works
Content-Security-Policy:
  script-src  'self' https://connect.example.com;
  frame-src   https://connect.example.com;
  img-src     'self' data: https://gravatar.com;

Single-page apps#

Nothing to do. The loader wraps history.pushState and listens for popstate, so client-side route changes are reported to Connect on their own and the agent sidebar tracks the visitor’s current page through the whole session.

Install the snippet once in your app shell. Do not add or remove it per route: a second injection is ignored, but tearing the script out mid-session drops the visitor’s live connection.

Signed-in visitors#

Set window.CONNECT_TOKEN_ID to a stable, unguessable value your backend mints for the signed-in user, and Connect maps it to the same visitor record on every device and browser they use. Without it, a visitor is anonymous per browser and a phone and a laptop look like two people.

token id, rendered per user
<!-- rendered by your server, for a signed-in customer -->
<script>
  window.$connect = window.$connect || [];
  window.CONNECT_WEBSITE_KEY = "wk_xxxxxxxxxxxxxxxxxxxxxxxx";
  window.CONNECT_TOKEN_ID = "usr_9f2a…";  // stable, unguessable, per user
  // …loader snippet…
</script>

Knowing when it is ready#

You never have to wait. $connect starts life as a plain array, so commands pushed before the loader arrives are queued and replayed in order the moment it does. Push identity and session data straight from your page — no readiness check, no polling.

If you do need the moment the queue drains, define window.CONNECT_READY_TRIGGER before the snippet.

optional readiness hook
window.CONNECT_READY_TRIGGER = function () {
  // The queue has been swapped for the live object and every
  // command pushed before boot has run.
  $connect.push(["do", "chat:open"]);
};

What it stores in the browser#

Useful if you maintain a cookie or privacy page. Connect sets no cookies. It uses localStorage in two separate places.

On your origin#

connect_launcher_<website-key>
The launcher’s last known colour, icon, and side. Cached so the button paints in your brand colour immediately instead of flashing the default green while the widget boots. No visitor data.

Inside the chat iframe, on the Connect origin#

connect_anon_id
The anonymous visitor id, so a returning visitor keeps their conversation.
connect_geo
A cached copy of the coarse location shown to your agents.
connect_appearance_<website-key>
The resolved palette, so the chat window paints before boot.
connect_nudge_shown
Marks that this visitor already saw the first-visit nudge, so it fires once.
connect_widget_sound
The visitor’s own choice about the notification chime, on or off.

Because that second set lives on the Connect origin rather than yours, it is partitioned per site by the browser and is not readable from your page.

Confirming it worked#

  • The launcher is in the corner and opens a chat window.
  • window.__CONNECT_LOADED__ is true in the console.
  • $connect.push(["do", "chat:open"]) in the console opens it.
  • A message you send appears in the dashboard inbox within a second.

If any of those fail, Troubleshooting lists what each symptom means.