1. Live Chat Widget
Onebear
  • One Bear Public API
    • Webhooks
    • Getting started
    • Customers
      • List customers
      • Create a customer
      • Get a customer
      • Update a customer
    • Messages
      • List messages in a room
      • Get a message
    • Orders
      • List orders
      • Get an order
    • Products
      • List products
      • Get a product
    • Rooms
      • List rooms
      • Get a room
    • Schemas
      • CreateCustomerRequest
      • CustomerAddress
      • CustomerContactPerson
      • UpdateCustomerRequest
  • Live Chat Widget
    • Live Chat Widget - Setup & Integration Guide
    • OneBear Live Chat Widget — คู่มือตั้งค่าและการผสานระบบ
  • API Channel
    • API Channel - Setup & API Reference
    • OneBear API Channel — คู่มือตั้งค่าและ API Reference
  1. Live Chat Widget

Live Chat Widget - Setup & Integration Guide

How to put a OneBear live-chat bubble on your own website, so that visitor messages land in the same agent console — with the same AI agent, auto-reply, assignment, tags, and follow-ups — as LINE, Facebook, or any other channel.
The widget is a one-line script tag. No OAuth, no server work required for the basic setup. Two optional steps (identified mode and programmatic control) need a small amount of code on your side.
Related: api-channel-setup.md — for connecting an external system over REST instead of embedding a widget.

1. How it works#

Your website
  │
  │  <script> …/widget/loader.js       ← the loader you paste
  ▼
Launcher bubble  ──click──▶  iframe panel (hosted by OneBear)
  │
  │  POST /api/v1/widget/{publicKey}/session      (bootstrap)
  │  WebSocket /hubs/widget                       (send + receive)
  ▼
OneBear API  ──▶  agent console (new or existing conversation)
                    AI agent / auto-reply / assignment / tags / follow-up
                       │
                       └──▶ agent or AI reply streams back to the panel in real time
Key points:
The chat UI runs inside an iframe served by OneBear — it cannot be broken by your site's CSS, and your site's JavaScript cannot read the conversation.
Only the small loader.js runs on your page: it draws the launcher bubble, applies your theme colour, and opens/closes the iframe.
Messages flow through the exact same pipeline as every other channel. Room lifecycle (New → InProgress → Resolved), AI, auto-reply, tags, and follow-ups all work with no extra configuration.
Visitors can be anonymous (default) or identified (you prove who they are with an HMAC — see §6).

2. Create the widget in the console#

1.
Sign in to OneBear → Settings → Integrations.
2.
Click Connect on Live Chat Widget.
3.
Fill in the settings:
FieldWhat it does
Widget nameInternal label. Shown in the console inbox so agents know which site the visitor came from. Required.
Theme colourHex colour for the launcher bubble and the panel's accents. Defaults to #41C3A8.
Welcome messageFirst message the visitor sees when the panel opens. Leave blank for none.
Allowed website originsComma-separated list of origins allowed to load the widget. Leave blank to allow any site. See §5 — this is the #1 trap.
Require visitors to enter name + email before chattingShows a short pre-chat form before the visitor can type. Off by default. Which fields it asks for is configurable after create (below).
Out-of-hours messageShown when a visitor opens the chat outside the shop's business hours. Blank falls back to a generic string.
4.
Click Create widget.
Four more sections appear on the widget's settings page afterwards, which cannot be set at create time:
Identified mode — reveal / rotate the identity secret (§6).
AI reply name and AI reply avatar — the name and photo visitors see on replies written by the AI. Leave both blank and AI replies stay unnamed. Replies written by a human always carry that agent's own name and photo (§4).
Pre-chat form fields — set Name and Email to Required / Optional / Hidden, and add up to 8 of your own custom fields, in the order they should be asked, each optionally mandatory.
Custom fields your page may send — pick which fields your site is allowed to push values into. Empty by default (§10).
Every setting except the name can be changed later from Settings → Integrations → your widget → Widget settings. Changes take effect on the visitor's next page load — no need to re-paste the snippet.

3. Embed the snippet#

Open Settings → Integrations → your widget. The Embed code section shows a snippet already filled in with your publicKey:
Paste it just before </body> on every page where the bubble should appear.
Copy the snippet from the console rather than from this document — the console fills in the correct host for your environment.
The publicKey is not a secret. It is visible in your page source by design, which is why the allowed-origins list (§5) is what actually protects the widget.
Loading is async; the script never blocks your page render.
Common installs: paste into your site footer template (WordPress footer, Shopify theme.liquid, Next.js _document/root layout, Google Tag Manager custom HTML tag).
Once pasted, reload the page — the bubble appears bottom-right. Send a test message and confirm it shows up in the OneBear inbox.

4. What the visitor sees#

The panel has three tabs:
TabContents
MessagesThe live conversation, plus the welcome message and attachment button.
ArticlesYour published Help Center articles — browsable by category, keyword-searchable, with per-article feedback.
SearchAsk AI — the visitor types a question and gets an answer grounded in your articles, with source links.
The Articles and Ask AI tabs read from the Help Center for the same company. If you have not published any articles, those tabs render empty — publish articles in Help Center → Studio to fill them. (Ask AI consumes AI credit — see §10.)
What the visitor sees in the Messages tab:
Delivery status on their own messages (sending / sent / failed), with a retry affordance when a send fails.
A typing indicator while an agent is composing — and agents see one while the visitor types.
An unread badge on the launcher, plus a notification chime, when a reply arrives while the panel is closed (mutable — see §7).
An out-of-hours banner when the shop is closed per its business hours (Settings → Shop info), showing your away message and asking for an email so a reply can reach them.
The pre-chat form, if enabled (§2), before the first message. Returning and identified visitors skip it.
Scrollable product cards (carousel) when the AI or an automation rule sends them; tapping a card action sends that choice as the visitor's own message.
Reply-to (quote). The visitor can quote any message in the thread; the quote block is rendered above their message, and tapping a quote scrolls back to the original when it is still loaded.
Older history on scroll-up — the panel opens on the most recent 50 messages and loads the next 50 each time the visitor scrolls to the top (§8).
An inline satisfaction rating when the conversation is resolved and CSAT is enabled (Settings → Satisfaction). The visitor rates it inside the panel — no link out to a separate page.
Video and file playback for anything an agent sends: video plays in the panel, other files download.
Their own messages in every panel they have open — a second tab, or phone + desktop for an identified visitor, receives the same message they just sent elsewhere.
Full-screen on phones. On a phone-sized viewport the panel opens edge-to-edge instead of as a floating card, tracks the on-screen keyboard, and cannot be pinch-zoomed out of shape. On desktop it stays a 380×560 card above the launcher.
Only messages that actually reached them. An agent message that failed to send is excluded from widget history, so it can never resurface on the visitor's next visit after the agent already retyped it.
Who the visitor sees replying. A reply written by a human carries that agent's own name and profile photo (initials when there is no photo). A reply written by the AI carries the AI reply name + avatar configured for the widget (§2) — leave both blank and AI replies show only a neutral support mark with no name. System rows (greetings, notices) have no avatar at all.
Attachments. Visitors can attach files from the composer, one file at a time:
KindAccepted
ImagesPNG, JPEG, GIF, WebP — plus HEIC/HEIF, which travel as a download because no browser except Safari can display them
DocumentsPDF, TXT, CSV, Word (.doc, .docx), Excel (.xls, .xlsx, .xlsm), PowerPoint (.ppt, .pptx)
VideoMP4, MOV, WebM
AudioMP3, M4A, AAC, OGG, WAV
Max size 25 MB per file.
The gate is the file extension, not the content type the browser reports — the same .csv arrives as text/csv, application/vnd.ms-excel or nothing at all depending on the visitor's OS. Anything that the media endpoint could serve as active content (.html, .htm, .svg, .xhtml) and every script/executable extension is rejected.
Anything else is rejected with an error in the panel. Uploads count against your company's storage quota.

5. Allowed website origins (read this one)#

Allowed website origins is a list of origins that may open a widget session. If a page's origin is not on the list, the session request is rejected with ORIGIN_NOT_ALLOWED and the panel shows a connection error.
Rules:
Blank list = any site may embed the widget. Fine while testing; tighten it before going live.
Entries must be full origins — scheme included, no path:
✅ https://example.com
✅ https://shop.example.com
❌ example.com (no scheme)
❌ https://example.com/contact (path)
No wildcards. https://*.example.com does not work — list each subdomain you actually use.
A trailing slash is tolerated (https://example.com/ matches), and matching is case-insensitive.
http://localhost:3000 and http://127.0.0.1:5173 are ordinary origins — add them explicitly if you test locally.
Security note: the origin is reported by the loader running on your page, so it is a best-effort filter, not an authentication boundary. It stops the widget from being embedded on the wrong site by mistake or by casual copy-paste; it does not stop a determined attacker who crafts requests by hand. If you need to know who the visitor really is, use identified mode (§6).
If the bubble appears but the panel never connects, this list is the first thing to check.

6. Identified mode (verified visitors)#

By default a visitor is anonymous — OneBear only knows "browser that has chatted before". If your site has logged-in users, identified mode lets you tell OneBear who is chatting, verified with an HMAC so a visitor cannot impersonate someone else.

6.1 Get the identity secret#

Settings → Integrations → your widget → Identified mode → Reveal secret.
The secret is not the publicKey. It must never reach the browser.
Rotate secret immediately revokes the old value. Any page still sending a hash computed with the old secret will fail to start a session — deploy the new secret first, then rotate, or accept a brief outage.
Revealing and rotating both require the SettingsChannelsManage permission.

6.2 Compute userHash on your server#

userHash = hex( HMAC-SHA256( key = identitySecret, message = userId ) )
Only userId is signed — name and email are not part of the hash, so you can change a display name without recomputing anything.
Node.js
C#
PHP
Python
Lowercase or uppercase hex both work — the server parses it case-insensitively.

6.3 Pass it to the widget#

Render the hash into the page server-side (or fetch it from your own authenticated endpoint):
What changes in identified mode:
Conversation history follows the user, not the browser — the same person sees their history on a new device or after clearing cookies.
The pre-chat form is skipped (OneBear already has the name and email).
Agents see the real name, email and phone on the customer record instead of an anonymous visitor.
With avatarUrl set, agents see the user's own profile photo on the conversation instead of initials.
name, email, phone and avatarUrl are optional. email and phone are written to the customer's Email and
Phone fields only when those are still empty — if an agent has corrected either by hand, the
hand-typed value stays. Send contact details here rather than through customFields: a custom field
is a separate value that list filters, duplicate detection and search do not treat as an email or a
phone number.
avatarUrl must be an absolute https: URL of at most 2048 characters — a relative path, an
http: URL (the agent console is https and would block it as mixed content), a data: URI or
anything unparseable is dropped silently, leaving whatever photo the visitor already had. A bad
avatar never fails the session.
Only userId is covered by the hash. name, email, phone and avatarUrl ride along unsigned,
so treat them as a convenience for your own authenticated users, not as proof of identity.
Failure modes:
Wrong or stale hash → INVALID_IDENTITY_HASH, session refused. (It does not silently fall back to anonymous.)
identify present but no identity secret configured → IDENTITY_SECRET_NOT_CONFIGURED.
Never compute the hash in browser JavaScript. Doing so publishes the secret and lets anyone impersonate any user.

7. Programmatic control#

Extra options on window.OneBearWidget:
OptionTypeDefaultWhat it does
hideLauncherbooleanfalseSuppresses the floating bubble. Use when you open the panel from your own UI.
autoOpenbooleanfalseOpens the panel as soon as the loader mounts.
position'left' | 'right''right'Which side the launcher and panel anchor to.
soundbooleantrueNotification chime when a reply arrives while the panel is closed. Set false to mute.
customFieldsRecord<string, string>—Push your own custom-field values onto the customer record. Identified sessions + allowlisted fields only (§10).
Once the loader has mounted it assigns three functions you can call:
Important if your site has login: call reset() when a user signs out. Otherwise the next person on that
browser sees the previous person's conversation. reset() removes the iframe and clears the stored visitor token.
Typical pattern — your own "Contact support" menu item, no bubble:
open and close are assigned asynchronously (after loader.js finishes downloading), so guard with && — or hide your button until the loader has mounted — if a visitor might click within the first few hundred milliseconds.

8. Conversation continuity#

Anonymous visitors. The loader stores a visitor token in your page's localStorage. The same browser returning later resumes the same conversation, keeps its history, and skips the pre-chat form. Clearing site data, using a different browser, or private mode all start a fresh conversation. (The token is kept on the host page, not inside the iframe, because browsers partition or block third-party iframe storage.)
Identified visitors. Continuity is keyed to your userId, so history follows the person across devices.
Session lifetime. A widget session token is valid for 24 hours by default; the panel transparently bootstraps a new one on the next page load. The conversation is not limited by that — it is bounded by your company's chat-history retention setting.
History displayed. The panel loads the most recent 50 messages when it opens, and loads the next 50 each time the visitor scrolls to the top — subject to your history-retention window. Messages an agent failed to send are excluded (§4).
Multiple open panels. A visitor with the widget open in two tabs — or, when identified, on phone and desktop at once — sees their own outgoing messages in all of them, not just the one they typed in.

9. AI agent and handoff#

The widget is a normal channel, so the AI agent works the same way it does on LINE or Facebook.
Turning AI on for the widget: Settings → AI → Channels → toggle your widget on. The AI master switch must also be on. Once enabled, the AI answers visitors and hands off to a human when it decides it should — or when a human takes over from the console.
Handoff is entirely internal — you do not implement anything. This is the main difference from the API Channel:
API ChannelLive Chat Widget
Where the chat UI livesYour appOneBear's iframe
Who delivers replies to the end userYou, via your outbound webhookOneBear, straight into the panel
AI → human handoffFires a handoff webhook event so you stop your own botHandled inside OneBear: AI stops replying, the room is assigned to an agent, the visitor keeps chatting in the same panel
Work required on your sideWebhook endpoint, signature verification, bot suppressionNone
From the visitor's point of view nothing happens on handoff — the replies simply start coming from a person. No webhook, no conversationId, no signature verification.
Auto-reply rules, greeting messages, tags, auto-assignment, and follow-up scheduling all apply to widget rooms exactly as configured for other channels.

10. Limits and what the widget cannot do#

Rate limits. Limits are per widget (or per visitor session), so traffic on one site never affects another:
WhatLimit
Launcher appearance lookup (per page load)600/min per widget
Session bootstrap (per panel open)60/min per widget
Sending a message20/min per visitor · 300/min per widget
Help article browse / search60/min per visitor session
Attachment upload20/min per visitor session
"Ask AI" in the help tab10/min per visitor · 60/min per company
Exceeding a limit returns HTTP 429 with a Retry-After header (the message limit surfaces as an error in the panel; the visitor can retry shortly). The appearance response is cached for 60 seconds, so ordinary browsing does not re-request it on every page — one consequence is that a change to the theme colour reaches visitors within about a minute rather than instantly.
"Ask AI" consumes AI credit. Each visitor question spends one AI credit from your plan's allowance, the same as an AI chat reply. When the allowance runs out the Ask tab stops answering; Articles and Search keep working. If you do not want visitors spending credit this way, leave the AI channel toggle off for the widget (§9).
Data the widget can send about a visitor:
FieldHow
userIdidentify (identified mode only)
Nameidentify, or the pre-chat form
Emailidentify, or the pre-chat form
Phoneidentify
Your own custom fieldscustomFields (§7) — identified mode only, and per-field opt-in required
Browsing contextCollected automatically, no setup (below)
Browsing context is read off your page automatically and shown to agents in the customer panel:
pageUrl, pageTitle, referrer, language, timezone, screen, viewport, userAgent. Query
parameters that look sensitive (tokens, passwords) are replaced with … before sending, and every value
is length-capped.
Custom fields from your page let you push things like an order id, plan tier, or account type onto the
customer record via customFields — behind two deliberate gates: (1) identified sessions only, because
an anonymous visitor can claim to be anyone, and (2) an explicit per-field allowlist under Settings →
Integrations → your widget
, empty by default. Fields that are not allowlisted are silently ignored, not stored.
Other current limits:
The pre-chat form asks for name + email (each Required / Optional / Hidden) plus up to 8 of your own custom fields (§2). It cannot collect a phone number into the customer's Phone field — a phone typed into a custom field stays a custom field, which list filters, duplicate detection and search do not read as a phone number. Use identify for that (§6).
The panel layout is not customisable beyond the theme colour.
Attachments: 25 MB per file, one file at a time, extensions limited to the allowlist in §4.
Messages have a maximum length enforced by the server; very long pastes are rejected rather than truncated.
Quick-reply buttons do not work on the widget. If an auto-reply rule or the AI attaches them, the widget renders nothing for them (carousel cards do render).
There is no client-side JavaScript event API — you cannot subscribe to "message received" or "conversation started" on your page. open(), close() and reset() are the only hooks (§7).

11. Security#

publicKey is public. It is meant to be readable in your page source. It identifies the widget; it grants nothing beyond starting a visitor session.
The identity secret is a credential. Server-side only, in an environment variable or secret manager. Never in client code, never in a repo.
Allowed domains are a guardrail, not authentication (§5). Combine with identified mode when identity matters.
The panel is sandboxed in an iframe on OneBear's origin — your site's scripts cannot read the conversation, and vice versa.
Uploads are type- and size-restricted (§4) and served back through an authenticated media endpoint, not as public URLs.
Rotating the identity secret is immediate. Deploy the new secret to your servers first if you want zero downtime.
Every widget session is scoped to one company and one channel — a session token issued for widget A can never read widget B's conversations.

12. Troubleshooting#

SymptomLikely cause
No bubble at allSnippet not on the page, or a console error. Check DevTools → Console for [OneBear widget] window.OneBearWidget.publicKey is required, and confirm loader.js returns 200 in the Network tab.
Bubble appears, panel shows a connection errorOrigin not in Allowed website origins (§5). Confirm the exact origin, including scheme and port.
Panel loads but nothing arrives in the console inboxWidget disconnected/inactive in Settings → Integrations, or you are looking at a different company.
INVALID_IDENTITY_HASHHash computed over the wrong value (sign userId only), wrong secret, or the secret was rotated after the page was rendered.
Identified mode does nothingidentify is incomplete — all of userId and userHash are required.
Pre-chat form keeps reappearingExpected for a brand-new browser. If it repeats for a returning visitor, localStorage is blocked (private mode, cookie banner).
History empty after a browser changeExpected for anonymous visitors — history is per-browser. Use identified mode for cross-device history.
Attachment rejectedOver 25 MB, or an extension outside the allowlist (§4).
Articles / Search tabs emptyNo published Help Center articles for this company.
AI never answersAI master switch off, or the widget channel is toggled off in Settings → AI → Channels (§9).
Bubble overlaps your own floating buttonSet position: 'left', or use hideLauncher: true and drive the panel from your own UI (§7).

13. FAQ#

Does it work on a single-page app?
Yes. The loader mounts once and survives client-side navigation. Don't inject the snippet on every route change — include it once in your root layout.
Can I have more than one widget?
Yes — create one per site or brand. Each gets its own publicKey, theme, and allowed-origin list, and shows up as a separate channel in the console. Widget channels count against your plan's channel limit.
Can I restyle the panel?
The theme colour is configurable; the panel layout is not. Custom CSS on your page cannot reach inside the iframe by design.
Can I put it inside Google Tag Manager?
Yes — a Custom HTML tag with the snippet works. For identified mode you'd need the hash available to GTM, which usually means a server-rendered data-layer variable.
Does it need cookie consent?
The widget stores a visitor token in localStorage to keep the conversation going. Whether that requires consent under your policy is your call — the widget works without it, at the cost of a fresh conversation each visit.
What happens outside business hours?
Same as any channel: your greeting/auto-reply and AI business-hours configuration apply. Visitors can still send messages; agents pick them up later.
Can I trigger the panel from a chat link in an email?
Use autoOpen: true on a specific landing page (§7), e.g. behind a ?support=1 query param.
Is the transcript available to my own systems?
Not through the widget itself. Widget conversations live in the console like any other room. Use the One Bear Public API (Settings → Developer → API keys) to read rooms and messages, or subscribe to Webhooks (message.received, room.created, …) to receive them as they happen — both are documented in the One Bear Public API section of this site.

14. Mobile apps (Android · iOS · Flutter)#

The same conversation can be embedded in your own mobile app. The SDKs wrap the widget panel in a native WebView behind a small API, so your app never talks to the panel directly. They use the same widget (publicKey, theme, allowed origins, identity secret) as the web snippet — nothing extra to create in the console.
PlatformPackageMinimum
Flutter (Android + iOS)onebear_chat on pub.dev — flutter pub add onebear_chatFlutter 3.24 · Dart 3.5 · Android minSdk 24 · iOS 14
iOS (native)OneBearChat via Swift Package Manager or CocoaPods from github.com/GofiveCorp/onebear-iosiOS 14 · Swift 5.9
Android (native)co.gofive.onebear:onebear-chat — ships inside the Flutter plugin today; contact us for the standalone artifactminSdk 24 · compileSdk 35
The API is the same on every platform:
Events (ChatOpened, ChatClosed, NewMessageReceived, LinkTapped, ErrorOccurred) are exposed as a stream on Flutter and a delegate on iOS/Android.
What differs from the web snippet:
No floating launcher. You provide the entry point (a menu item, a tab, a button) and call present(); use unreadCount for a badge.
Full-screen only, with the platform's back gesture / edge swipe to leave the chat.
Attachments work on Android and on the web; on iOS they are not yet available in the SDK.
No push notifications from the SDK — a reply that arrives while the app is closed is shown the next time the chat opens.
Links sent by an agent open in Custom Tabs (Android) / SFSafariViewController (iOS); help-center articles and embedded videos render inside the chat.
Keep the SDK up to date — the mobile panel is bundled inside the package, so fixes (keyboard handling, zoom, article embeds) ship as package releases rather than reaching you automatically like the web widget does.
Modified at 2026-09-16 08:19:25
Previous
UpdateCustomerRequest
Next
OneBear Live Chat Widget — คู่มือตั้งค่าและการผสานระบบ
Built with