Skip to content

Web Chat

Ed Mozley edited this page Jul 16, 2026 · 3 revisions

Web chat channel

Put a chat window on any website and every conversation becomes a normal FreeITSM ticket. A visitor drops a few lines of <script> onto their site, a floating chat launcher appears, and their messages land in the inbox in the right company — answered by analysts from the same reading pane they use for email and WhatsApp. It's a third inbound channel that reuses the whole ticket pipeline.

Status. Live: the embeddable widget, the pre-chat name+email prompt, visitor → ticket, analyst replies delivered back to the visitor, per-company routing, the full security model (public key + origin allowlist + per-conversation token + rate limiting), and — as of part three — office hours, AI answers (assist/deflect) and escalation (talk to a person / email me back). The one piece still to wire: sending the offline reply out from the company's service-desk mailbox (email-when-away); the toggle saves but delivery isn't built yet. This page documents the whole design, marking what is live versus planned.


What it is, in one line

A web chat widget is the self-hosted twin of a WhatsApp number: it drives one messaging channel (channel_type = 'webchat', provider = 'freeitsm', no external provider or credentials), so once a message arrives it flows through the same ticket, inbox, thread and reply machinery as every other channel. There is nothing new on the analyst's side — a web chat ticket looks and behaves like a WhatsApp ticket.

How a chat becomes a ticket

  1. The visitor opens the widget and (if the widget asks for it) enters their name and email — the pre-chat gate, so every ticket has a real requester.
  2. They type a message. It's ingested onto the ticket membrane as a Web chat-origin ticket, pinned to the widget's company.
  3. It appears in the inbox exactly like any other ticket. The analyst replies from the normal reading-pane composer.
  4. The reply is delivered back into the visitor's chat window (the widget polls for new messages every few seconds).
  5. One conversation is one ticket; further messages thread into it.

Setting it up: from zero to a live widget

Two sides: create the widget inside FreeITSM, then paste one snippet onto the website.

1. (Once per install) Set the Public base URL

The embed snippet points browsers at your FreeITSM host to load widget.js. FreeITSM builds that URL from the Public base URL — the same setting the WhatsApp channel uses, at the top of Tickets → Settings → Messaging. Set it to the address the outside world reaches this install on (e.g. https://itsm.yourcompany.com). If it's blank, FreeITSM falls back to whatever address you're browsing from, which is fine for local testing but not for a real embed. The host must be reachable over HTTPS from the customer's site, and served over HTTPS itself (browsers block a chat widget loaded over plain HTTP on a secure page).

2. Create the widget

Tickets → Settings → Web chat → Add. Fill in:

  • Name (e.g. "Acme website"), and on a multi-company install the Company these conversations belong to.
  • Greeting, accent colour, launcher text — the look of the widget.
  • Allowed website(s) — one origin per line, e.g. https://www.acme.com (and https://acme.com if both are used). Only these sites may show the widget. Leave blank only while testing.
  • Leave Ask for name + email first on unless you have a reason not to.
  • Optionally set office hours, email-when-away and AI answers (see Settings).

Click Save. FreeITSM mints the widget's public key and shows the embed snippet right there in the modal — ready to copy.

3. Paste the snippet onto the website

The snippet looks like this (your real key replaces the placeholder):

<script>
  (function(d){
    var s=d.createElement('script');
    s.src="https://itsm.yourcompany.com/api/webchat/widget.js";
    s.async=true;
    s.setAttribute('data-freeitsm-widget',"wc_XXXXXXXX");   // your public widget key
    d.head.appendChild(s);
  })(document);
</script>

Paste it just before the closing </body> tag on every page that should show the chat window (in most CMSes this is a "custom code / footer scripts" box, so it applies site-wide). The widget renders itself inside a Shadow DOM, so the host site's CSS can't touch it and vice-versa — it looks the same on any site.

4. Check it's wired in

Reload the site — the launcher appears in the corner. Send a test message; it should arrive in Tickets as a Web chat-origin ticket in the chosen company. Reply from the reading pane; the reply appears back in the widget within a few seconds.

If the launcher doesn't appear, it's almost always the Allowed website(s) list not matching the site's real origin (scheme + host, no path, no trailing slash), or the Public base URL pointing somewhere the browser can't reach. Both are deliberate: a widget that isn't on an allowed origin is refused.

Never regenerate the key casually — it's baked into every page carrying the snippet. There's no "rotate key" button today; deleting and recreating a widget mints a new key and means re-pasting the snippet everywhere.

Settings — every control

Managed under Tickets → Settings → Web chat (its own tickets.webchat permission, separate from the WhatsApp credentials tab — the widget key is public, so there are no secrets to protect here).

Identity & appearance

Control What it does
Name Internal label; also the title shown on the chat panel.
Company Pins every conversation from this widget to one company (multi-company installs).
Greeting The opening line shown when the chat opens.
Accent colour Hex colour for the launcher, header and the visitor's message bubbles.
Launcher text The label on the floating button (e.g. "Chat to us").
Allowed website(s) The site origins permitted to embed this widget (one per line). Empty = any site (testing only).
Ask for name + email first The pre-chat identity gate. On by default, so every ticket has a real requester.

Availability — office hours (live)

Control What it does
Office hours Pick an existing SLA business-hours calendar, or "Always open". Outside those hours the widget shows your offline message and still takes the enquiry as a ticket to answer later.

Email delivery (configured; sending still pending)

Control What it does
Email replies to the visitor if they've left the chat If an analyst replies while the visitor is no longer watching the chat, send that reply to the email they gave, so the conversation continues by email. Requires a mailbox for that company (see Email direction).

AI answers (live)

Control What it does
Answer from the Knowledge base with AI When a visitor asks a question, AI drafts an answer before a person is involved — from this widget's company's articles plus shared ones, and only those marked public (Knowledge).
When AI answers Assist — always raise a ticket as well. Deflect — only raise a ticket if the visitor escalates.
Offer a live agent The AI always lets the visitor ask to speak to a person.
Offer a ticket by email The AI lets the visitor raise a support ticket — created with an AI summary in the body and the full chat log attached as a .txt file.

Security & authentication

This is the part worth understanding, because a chat widget is the only bit of FreeITSM a random member of the public can reach.

The widget key is public, not a secret. It ships in the customer's page source — anyone can View Source and read it. So it is treated as a public address label, never a password. It grants exactly one thing: the ability to start a conversation, and only from an allowed origin. It cannot read tickets, cannot read anyone's chat, and grants no admin.

A per-conversation token is the real capability. When a visitor starts a chat, they get a private token stored only in their browser. Every send and every poll must present it. Reading or posting to an existing conversation needs that token — so even though the widget key is visible to everyone, one visitor can never read another visitor's chat.

Origin allowlist. Each widget lists the website addresses allowed to embed it. Requests are checked against it using the browser's Origin header, falling back to the Referer origin for same-origin embeds (a page served from the same host as FreeITSM sends no Origin). If someone copies your key onto evil-site.com, the origin won't match and it's refused. This is a strong deterrent against key-copying — not a cryptographic guarantee, since Origin/Referer can be absent outside a real browser, which is exactly why the per-conversation token, not the origin, is what protects a conversation's contents.

Rate limiting. New conversations are capped per IP per minute, and messages per conversation per minute, to blunt abuse of the public endpoints.

Email: who it comes from

A common worry: if we email the customer (someone@gmail.com), aren't we spoofing gmail? No. The customer's email is only ever a recipient, never a sender. Any outbound email goes from your own company service-desk mailbox (the same authenticated Microsoft/Google/SMTP mailbox that handles that company's email tickets), to the customer — an ordinary reply from your domain, identical to answering an email ticket.

The practical consequence: offline-email only works if that company has a mailbox configured, because a web chat ticket arrives through the widget, not through a mailbox, so FreeITSM has to pick the company's service-desk mailbox to send from. No mailbox → nothing to send from → the toggle can't act.

The AI "raise a ticket by email" route doesn't send anything at the moment it's raised — it just creates a ticket (customer as requester, AI summary as body, chat log attached). Outbound only happens later if an analyst replies, again from your mailbox.

Multi-company

A widget is pinned to a company, so its conversations are isolated to that company exactly like WhatsApp numbers and mailboxes. On a single-company install none of this is visible — it behaves as you'd expect.

Knowledge base scoping. The AI answers only from the widget's own company's articles plus ones shared with every company, and only from articles an author has marked public. See Knowledge.

Every article defaults to "Analysts only" — including ones written before that setting existed. So after upgrading, the widget's AI stays quiet until you mark some articles public. That is deliberate: nothing written for internal eyes reaches a stranger unless someone chooses it.

Status: what's live

Area State
Embed widget (launcher + Shadow-DOM panel), pre-chat name/email, polling delivery ✅ Live
Public endpoints (config / start / send / poll) + full security model ✅ Live
Visitor → ticket in the right company, analyst replies back to the visitor ✅ Live
Settings: appearance, origins, availability, email, AI toggles (save/read) ✅ Live
AI answers (assist/deflect), office-hours gating, escalation (agent / email) ✅ Live
Email-when-away — actually sending the reply out from the company mailbox 🚧 Next build — toggle present, delivery not yet wired

For the engineering detail — tables, endpoints, the channel reuse, and the resume plan — see the Web Chat: Developer Guide.

Roadmap — still to build

AI answers, office-hours gating and escalation shipped in part three. What's left, in rough priority order:

  1. Email-when-away delivery — the only remaining part-three piece. When an analyst replies and the visitor has left (or on an "email me back" escalation), email the reply from the company's service-desk mailbox to the visitor (requires that company to have a mailbox; see Email direction). The ticket and AI summary are already created — this is purely the sending step.
  2. Per-company Knowledge scopingdone. AI answers are scoped to the widget's company + shared articles, and to articles marked public. See Knowledge.
  3. Live delivery upgrade — replace polling with SSE for instant replies without the 3-second poll.
  4. Nice-to-haves — a proper "rotate key" action; letting the analyst's name (rather than the channel name) label agent replies in the widget; typing indicators / unread badges; file uploads from the visitor.

See also the project_webchat_channel memory note and the Developer Guide → Resume plan.

Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally