Inbox & channels·Updated May 9, 2026 · 6 min read

Website Chat Widget

Add chat to your site

The Chatlane Website Chat Widget lets you add a floating chat bubble to your website. Visitors can chat with your team and (optionally) your AI agent directly from your site. All conversations appear in your Chatlane inbox alongside email, SMS, and other channels.

What the widget does

  • Shows a floating launcher in the bottom-right (or bottom-left) corner of your website.
  • Opens a chat window with a configurable welcome message and visitor-identification gate.
  • Sends messages into your Chatlane inbox as a web channel — exactly like every other channel.
  • Streams real-time replies to the visitor over a private channel (Reverb / WebSocket). No polling.
  • Reactivates the conversation when a visitor follows up on a previously closed thread.
  • Remembers the conversation across page refreshes and navigation, so returning visitors see their history when they open the widget.
  • Optional soft "ding" when a new agent or staff reply lands.

How replies work

Widget conversations behave like any other inbox channel. There is no widget-level AI configuration:

  • Whichever agent is attached to this inbox with Auto trigger enabled handles incoming widget messages — same as email, SMS, etc.
  • Reply behaviour (send / draft / note / auto) is the agent's inbox setting, not a per-widget override.
  • Teammates can reply to widget conversations directly from the inbox composer; visitors see the reply land in the widget within ~50 ms.

If no agent has Auto trigger enabled on the inbox, widget conversations are human-only — that's a valid setup.

Quick start

  1. Add a widget in Inbox Settings → Channels → Add Widget.
  2. Configure appearance, identification, and (optionally) edit the welcome and greeter copy.
  3. Copy the embed code from the Install tab.
  4. Add the script to your website's <head> or just before </body>.
  5. Test by visiting your site and clicking the launcher.

Configure the widget

Inbox Settings → Channels → Add Widget lands you on a dedicated configuration screen with five tabs:

Overview

At-a-glance summary of the widget's status, identification mode, position, and the inbox's auto-trigger replies setup. Use the toggle here (or in Danger Zone) to pause/resume the widget.

General

  • Widget name — internal label shown in your inbox settings; visitors don't see it.
  • Welcome message — shown when a visitor opens the chat, before they send their first message.
  • Visitor identification — Off, Optional, or Required (see table below).
  • Allowed origins — list of domains permitted to load the widget script. Anywhere else, the embed refuses to render.
  • Live toggle — pause embedding without deleting the widget.

Appearance

  • Brand color — used for the launcher fill, the chat header, and visitor message bubbles. Pick a swatch or paste any hex.
  • Bubble shape — three personalities:
    • Minimal — solid circle with a chat icon. Quiet, fits most sites.
    • Playful — gradient bubble with a notification dot.
    • Pro — pill chip with avatar + "Chat with us" label. Loudest CTA, ideal for landing pages.
  • Bubble icon — pick a preset (chat, sparkle, headset, send, bolt, smile) or paste a URL to a hosted SVG/PNG (≥ 64×64). Custom icons are auto-tinted white so they read on the brand-coloured launcher. The Pro shape always shows an avatar.
  • Header style — Gradient (default), Solid, or Dark (Black Pearl).
  • Position — Bottom Left or Bottom Right.
  • Greeter pop-up — show a tiny preview above the launcher when the widget loads (Playful style only). Configurable copy.
  • Play sound on new messages — soft two-note ding when a new agent or staff reply lands. Default on.

A live preview on the right shows both Closed · Launcher and Opened · Card states; every change updates instantly.

Install

Step-by-step embed instructions with copyable snippets:

  1. The basic <script> tag.
  2. The optional ChatlaneWidget('init', { … }) call to identify signed-in visitors.
  3. A "hidden launcher" recipe for when you want to control the open from your own button (or from a Contact Card hand-off).

The embed key lives here too, with a Rotate button that invalidates the current key.

Danger Zone

  • Pause widget — stop loading on every origin without deleting it.
  • Reset embed key — invalidate the current key. Live snippets stop loading until you redeploy.
  • Delete widget — permanent. Past conversations stay in the inbox; the embed stops rendering.

Add the widget to your website

Paste this just before </body>:

<!-- Chatlane Chat Widget -->
<script>
  window.ChatlaneWidget = window.ChatlaneWidget || function () {
    (window.ChatlaneWidget.q = window.ChatlaneWidget.q || []).push(arguments);
  };
</script>
<script src="https://your-chatlane-domain.com/widget.js?key=YOUR_EMBED_KEY" async></script>

The first inline script defines a tiny stub so ChatlaneWidget('init', { … }) calls work even before widget.js has finished loading (the script tag is async). The real runtime drains the queue on boot. This is the standard chat-widget loader pattern.

Use your real Chatlane URL and the embed key from the Install tab.

Pass visitor data (optional)

If your site already knows who the visitor is, pass their details so Chatlane can:

  • Skip the identification gate.
  • Match them to an existing contact by email.
  • Keep the same conversation continuous across devices.
<script>
  // Run AFTER the widget script loads.
  ChatlaneWidget('init', {
    key: 'YOUR_EMBED_KEY',
    user: {
      name: 'Jamie Chen',
      email: '[email protected]',
      phone: '+447480780705', // optional, E.164 format
    },
  });
</script>

Supported fields on user:

  • name — display name; replaces the name field on the identification gate.
  • email — used to look up an existing contact on this team and to keep the conversation continuous across devices.
  • phone — optional, E.164 format (e.g. +447480780705). When set, visitors who hand off to SMS or WhatsApp skip the phone field entirely — Chatlane already knows which number to match the inbound message on.

Important — only data passed via ChatlaneWidget('init', { … }) is trusted. Visitors who type their email into the widget's identification form are stored as metadata but not used to look up existing contacts. This stops a stranger typing your VIP customer's email into the widget and accessing their conversation history.

Entity inboxes (pass record context)

If the widget is attached to an entity inbox (e.g. an Orders or Subscriptions inbox), every conversation must be grouped by a CustomEntityRecord — not by visitor contact. Pass the record context on init so Chatlane knows which record the chat belongs to:

<script>
  ChatlaneWidget('init', {
    key: 'YOUR_EMBED_KEY',
    user: {
      name: 'Jamie Chen',
      email: '[email protected]',
    },
    entity: {
      slug: 'orders',           // CustomEntity.slug
      external_id: 'ORD-1042',  // CustomEntityRecord.external_id

      // …or use the internal record id when you have it:
      // record_id: 789,
    },
  });
</script>

Rules for entity:

  • Trusted only. Like user, the entity block is only honoured when the init call is also trusted (i.e. a user was passed). Untrusted runtime contexts ignore it.
  • One identifier is enough. Provide either record_id or external_id + slug. If both are sent, record_id wins.
  • Slug must match. When you pass slug, it must equal the inbox's customEntity.slug. A mismatch returns INVALID_ENTITY (HTTP 422) and the widget stops the send.
  • Team-scoped. The record must belong to the same team and the same custom_entity_id as the widget's inbox.
  • Required on entity inboxes. If the widget loads on an entity inbox without resolvable entity context, the API responds with ENTITY_REQUIRED (HTTP 422). The widget will not silently fall back to a contact-conversable conversation.
  • Ignored on standard inboxes. Sending entity to a widget on a non-entity inbox is a no-op — the call still succeeds as a normal contact-based conversation.

On entity inboxes, the visitor is still resolved as a Contact (so each message has the right sender for the inbox UI), but the conversation itself is filed under the entity record. That's the same shape an inbound email to inbox-{id}-orders-{external_id}@inbound.chatlane.io produces — so a widget chat and an email about the same order land on the same conversation.

Hide the default launcher

If you want to drive the open from your own button (or use a Contact Card with a "Live chat" row that hands off to the widget), suppress the floating bubble:

<!-- Either with a data attribute -->
<script
  src="https://your-chatlane-domain.com/widget.js?key=YOUR_EMBED_KEY"
  data-hidden-bubble="true"
  async
></script>

<!-- Or with a query param -->
<script src="https://your-chatlane-domain.com/widget.js?key=YOUR_EMBED_KEY&hidden=1" async></script>

<!-- Anywhere on the page -->
<button onclick="window.chatlane.openWidget()">Talk to us</button>

In hidden-bubble mode the widget still loads silently — realtime, agent triggering, identification all behave normally — but the chat window only opens when something calls window.chatlane.openWidget(). Contact Cards' "Live chat" row uses the same hook, so the two surfaces share one widget instance.

Visitor identification options

Setting What visitors see How Chatlane identifies them
Off No name/email form Anonymous visitor ID (or data you pass via ChatlaneWidget('init', { … }))
Optional Form shown; they can skip Visitor ID; if they fill the form, name/email stored as metadata only
Required Must enter name or email Same as Optional; ensures you have at least some identifier

In all three modes, data passed via ChatlaneWidget('init', { … }) from your authenticated site is trusted and links the conversation to an existing contact.

How it works (in brief)

  1. Visitor opens your site — The widget script loads and shows the launcher.
  2. Visitor sends a message — The widget sends it to Chatlane with the embed key, a visitor ID, and (optionally) trusted name/email.
  3. Chatlane — Finds or creates a contact, finds or creates a conversation for that inbox, saves the message, then triggers any auto-enabled agent on the inbox (same flow as email/SMS).
  4. Realtime delivery — When the agent (or a teammate) replies from the inbox, the message is broadcast over a private Reverb channel scoped to that conversation. The widget receives it within ~50 ms and renders it instantly, optionally playing the configured ding.
  5. Conversation reactivation — A new visitor message bumps the conversation back to active even if a teammate had closed it, so it surfaces again at the top of the inbox.
  6. Persistence — The conversation ID is stored in the visitor's browser (localStorage) so when they return, the widget resumes the same thread.

Widget conversations use the web message type and a widget inbox channel. Agent replies are sent as visible web messages; drafts and notes follow the agent's inbox setting and are filtered out of the visitor's view automatically.

Continue elsewhere (handoff)

Visitors can switch the conversation from web chat to email, SMS or WhatsApp without losing context. A small arrow button in the chat header opens a Continue elsewhere panel listing every other channel attached to the same inbox.

The flow:

  1. Visitor taps the handoff icon in the header.
  2. Picks a channel (e.g. Email — [email protected]).
  3. Enters any details we don't already have (email if going to email, phone for SMS/WhatsApp; name is always optional).
  4. We update their contact record on the server and show a confirmation card with a clickable mailto: / sms: / wa.me link plus a Copy button.
  5. The visitor sends a message via that channel from their own client. The inbound parser (email, Twilio, WhatsApp Cloud) matches the sender to their existing contact and reuses the same conversation — so the chat history stays continuous and your team picks up exactly where the web chat left off.

A few important notes:

  • No outbound message is sent by Chatlane. The visitor is the one who actually starts the conversation on the new channel; we only update the contact details so the inbound match works.
  • The widget stays open. Visitors can return to web chat at any time. Web chat and the new channel coexist on the same conversation.
  • Conflict-safe identity. If a visitor types an email or phone that's already on file for another contact in your team, we don't overwrite — we store it as widget_provided_email / widget_provided_phone metadata on the visitor's contact. The next inbound from that other channel still goes to whoever truly owns the address.
  • Auto-discovered. The handoff button only appears when the parent inbox has at least one eligible channel (email, SMS, WhatsApp / WhatsApp Cloud). No admin toggle to maintain.
  • Same identity rules. Trusted contact details from ChatlaneWidget('init', { user: { name, email, phone } }) are used as defaults and skip the form fields. If your site already knows the visitor's phone number, pass it on init and SMS / WhatsApp handoffs will go straight to the confirmation card.
  • Confirmation tells them what we'll match on. The confirmation card spells out the exact email or phone Chatlane is using to identify their inbound (e.g. "send a message from your email [email protected] to [email protected]") so the visitor knows which client to send from.

Multiple widgets per inbox

You can run more than one widget on the same inbox — for example one for your marketing site and one for your help center, each with its own appearance and identification policy. Inbox Settings → Channels → Add Another creates a new widget alongside the existing one.

Troubleshooting

Widget doesn't appear

  • Confirm the script tag is loaded (check the Network tab for widget.js).
  • Confirm the embed key matches the one in the Install tab.
  • Check that your domain is in the Allowed origins list (or the list is empty for any-origin development).

Messages send but no realtime updates

  • Open the browser console — the Echo/Reverb connection should be open. If chatlane.test/widget/broadcasting/auth returns 403, the visitor's localStorage may have a stale visitor ID; clear it and reload.
  • The widget broadcasts inline (no queue worker required). If you're self-hosting Reverb, make sure the Reverb daemon is reachable from the browser at the host configured under REVERB_HOST.

Agent doesn't reply

  • Confirm the agent is attached to the inbox with Auto trigger enabled.
  • The widget itself doesn't link to a specific agent — it relies on the inbox's auto-trigger flow exactly like email/SMS.
  • If the agent's inbox action is auto, make sure a routing/auto policy is set on the agent or the inbox attachment.
  • Check the queue worker is running so the agent job processes (php artisan queue:work). If you start a worker after deploying, run php artisan queue:restart so workers reload code.

Conversations don't show in the inbox

  • Confirm you're looking at the correct inbox and that no inbox filters are hiding the conversation.
  • A new visitor message reactivates a closed conversation back to active automatically.

Contact Card hand-off doesn't open the chat

  • Confirm both widget.js and contact-card.js are loaded.
  • The widget script must run before the visitor taps the "Live chat" row — window.chatlane.openWidget() is exposed by the widget bundle.

Best practices

  • Agent setup: Attach an agent to the inbox with Auto trigger if you want instant replies. Agent responses can include simple HTML (paragraphs, lists, links), which the widget and inbox both display.
  • Identification: Start with "Off" to test, then switch to "Optional" or "Required" if you want to collect names and emails. Use "Required" only when you need every conversation to have an identifier.
  • Trusted users: If you have logged-in visitors, pass their name and email with ChatlaneWidget('init', { … }) for proper contact matching and cross-device continuity.
  • Hosted icons: If you upload a custom bubble icon, use a high-contrast monochrome SVG/PNG — the runtime tints it white to sit on the brand-coloured launcher.

Security and limits

  • Embed keys are meant to be public (in your site's HTML). Each key is scoped to one inbox + widget configuration and validated against the Allowed origins list.
  • Realtime auth — every Reverb private-channel subscription is authenticated by visitor ID + embed key, then verified against the conversation's contact ownership.
  • Rate limiting applies to the widget API (60 req/min per IP).
  • Contact linking uses the visitor's localStorage ID by default; email is only used for lookups when it comes from your site via ChatlaneWidget('init', { … }), so a stranger typing your customer's email in the widget can't see their existing conversations.
  • Storage — Visitor and conversation IDs are stored in the browser (localStorage); they may not persist in strict private browsing.

Need more help?

Was this article helpful?
Your feedback helps us improve our docs.