Skip to content

Web Chat

Ed Mozley edited this page Jul 15, 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, and the full security model (public key + origin allowlist + per-conversation token + rate limiting). Configured but not yet wired to behaviour: the AI answers and office-hours / email-when-away toggles exist in Settings and save correctly, but the runtime that acts on them is the next build (see Status). 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 (configured; runtime pending)

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; runtime 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 (configured; runtime pending)

Control What it does
Answer from the Knowledge base with AI When a visitor asks a question, AI drafts an answer from your published Knowledge articles before a person is involved.
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 caveat. The Knowledge module is not yet company-scoped, so when AI answers are switched on the AI currently draws from all published articles regardless of the widget's company. Per-company scoping will follow when Knowledge multi-tenancy lands.

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 behaviour, office-hours gating, email-when-away sending, escalation 🚧 Next build β€” toggles present, runtime 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

The settings for these are already in place; the behaviour is the next work. In rough priority order:

  1. AI answers runtime β€” actually call the Knowledge base AI on a visitor message, show the answer in the widget, and honour assist vs deflect. The building blocks (includes/knowledge/kb_ai.php, includes/webchat/ai.php) are committed but not yet invoked.
  2. AI escalation β€” the "talk to a person" and "raise a ticket by email" buttons, the latter creating a ticket with an AI summary + the full chat log as a .txt attachment. In deflect mode this is also what first creates the ticket (the pre-ticket transcript lives in webchat_messages).
  3. Office-hours gating β€” read the chosen SLA calendar to decide open/closed; when closed, show the offline message and take the enquiry as a ticket to answer later.
  4. Email-when-away β€” when an analyst replies and the visitor has left, email the reply from the company's service-desk mailbox to the visitor (requires that company to have a mailbox; see Email direction).
  5. Per-company Knowledge scoping β€” once the Knowledge module is multi-tenant, scope AI answers to the widget's company (today it draws from all published articles).
  6. Live delivery upgrade β€” replace polling with SSE for instant replies without the 3-second poll.
  7. 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