Setup is a single <script> tag. Signed identity for logged-in users and VIP customers is optional.
Bu belgeler şu an yalnızca İngilizce. Türkçe yardım için support@embrchat.com adresine yazabilirsiniz.
01How it works
The EmbrChat widget is added to your site with a single <script> tag. The tag only draws a small launcher button on your page; it lives inside a Shadow DOM, so it never clashes with your CSS.
- The chat window runs in an isolated iframe on
widget.embrchat.com. Your page and the iframe talk only through origin-verifiedpostMessagecalls. - Visitors start as anonymous guests. You can verify your logged-in users with a signed identity or a site token.
- Conversations are first answered by the assistant using approved content, and handed off to an agent when needed or when the visitor asks.
02Quick start
Get your site key from the Setup page in the panel and place the code below on every page, right before the closing </body> tag.
<script type="module" src="https://widget.embrchat.com/v1/widget.js"
data-widget-key="wk_YOUR_SITE_KEY"
data-channel="web"
async></script>Allowed origins
The widget only opens on sites listed under Allowed origins in the panel. The list can have up to 10 lines and only https is accepted.
| Format | Example | Description |
|---|---|---|
| Exact match | https://www.yoursite.com | This origin only |
| Single-level wildcard | https://*.yoursite.com | shop.yoursite.com, app.yoursite.com … |
| Numeric range | https://store{1-50}.com | Up to 5,000 domains; you must own all of them |
Pages where the widget is hidden
To hide the widget on specific pages such as checkout, add paths under Excluded pages in the panel:
/checkout
/account/payment
/campaign/*03Tag attributes
| Attribute | Default | Description |
|---|---|---|
data-widget-key required | — | Your site key from the panel (wk_…) |
data-channel | web | Channel the conversation is routed to (see Channels) |
data-lazy | false | If true, the widget loads only when the button is clicked; online-visitor tracking is disabled |
data-send-title | true | Shows the page title to agents; disable with false |
data-launcher | default | none hides the launcher; open the chat from your own button |
data-locale | auto | Fixed language such as en-GB, de-DE, tr-TR |
data-position | right | Launcher position: right / left |
EmbrChatSettings, the Settings value wins.04Settings: window.EmbrChatSettings
For advanced configuration, define a settings object before the script tag:
<script>
window.EmbrChatSettings = {
channel: "web",
visitor: { name: "Jane Doe", email: "jane@example.com", memberId: "U-1042" },
identity: { payload: "...", signature: "..." }, // signed identity (section 6)
siteToken: null, // or a JWT (section 7)
onSessionExpired: async () => { /* fetch a fresh identity */ },
lazy: false,
sendPageTitle: true,
launcher: "default",
locale: "en-GB"
};
</script>
<script type="module" src="https://widget.embrchat.com/v1/widget.js" data-widget-key="wk_..." async></script>| Field | Type | Description |
|---|---|---|
visitor | object | Unverified name, email and member ID. Shown to agents with an unverified label |
identity | object | payload + signature — verified identity |
siteToken | string | Your site's own JWT — secret-less verification |
onSessionExpired | function | Called when the signature or token expires |
channel, lazy, sendPageTitle, launcher, locale | — | Same as the tag attributes |
05Visitor types
| Type | How it's identified | What agents see | History |
|---|---|---|---|
| Guest | No identity provided | Random visitor name, page, device type | Open conversation on the same device |
| Member (verified) | Signed identity or site token | Verified name, email, member ID, attributes | Last 180 days |
| VIP | segment: "vip" in the signed identity | Member details + VIP badge; routed to the VIP queue | Last 180 days |
visitor is always marked as unverified.06Signed identity (HMAC)
To verify your logged-in users, sign their identity on your server and print it on the page. You'll find the signing secret under Setup → Identity verification in the panel.
Payload fields
| Field | Required | Description |
|---|---|---|
userId | Yes | Unique user ID in your system |
issuedAt | Yes | Unix timestamp (seconds). Signatures older than 10 minutes are rejected |
name, email | No | Details shown to agents |
segment | No | member or vip |
attributes | No | Up to 10 key–value pairs (e.g. plan, city) |
Signature: hex(HMAC_SHA256(secret, payload)) — computed over the exact same payload string you send to the page.
import { createHmac } from "node:crypto";
export function chatIdentity(user) {
const payload = JSON.stringify({
userId: String(user.id),
name: user.name,
email: user.email,
segment: user.isVip ? "vip" : "member",
attributes: { plan: user.plan },
issuedAt: Math.floor(Date.now() / 1000)
});
const signature = createHmac("sha256", process.env.CHAT_SECRET)
.update(payload).digest("hex");
return { payload, signature };
}<?php
$payload = json_encode([
'userId' => (string) $user->id,
'name' => $user->name,
'email' => $user->email,
'segment' => $user->is_vip ? 'vip' : 'member',
'issuedAt' => time(),
], JSON_UNESCAPED_UNICODE);
$signature = hash_hmac('sha256', $payload, getenv('CHAT_SECRET'));import hmac, hashlib, json, os, time
payload = json.dumps({
"userId": str(user.id),
"name": user.name,
"email": user.email,
"segment": "vip" if user.is_vip else "member",
"issuedAt": int(time.time()),
}, ensure_ascii=False, separators=(",", ":"))
signature = hmac.new(os.environ["CHAT_SECRET"].encode(), payload.encode(), hashlib.sha256).hexdigest()Using it on the page
<script>
window.EmbrChatSettings = {
identity: <?= json_encode(['payload' => $payload, 'signature' => $signature]) ?>,
onSessionExpired: async () => {
const r = await fetch("/api/chat-identity", { credentials: "include" });
if (r.ok) window.EmbrChat.setIdentity(await r.json());
}
};
</script>07Site session token (JWT, secret-less)
If your site already uses JWT-based sessions, you can pass that token instead of sharing a separate secret. EmbrChat verifies it with the method you configure in the panel:
- JWKS URL or PEM public key (RS256 / ES256)
- HS256 shared key
- Verification endpoint: the token is sent to your server with an
Authorization: Bearer …header and is accepted if it returns200
window.EmbrChatSettings = { siteToken: getAccessToken() };
// When the token is refreshed:
window.EmbrChat.setSiteToken(newJwt);
// On logout:
window.EmbrChat.reset();Rules
expis required; when the token expires,onSessionExpiredis called.- An algorithm allowlist is enforced;
noneis always rejected. issandaudare checked if configured in the panel.- Name and email are not taken from the token; only
subis used as the user ID.
08Login and logout
Login without a page reload: when the user logs in, fetch a fresh signature from your server and pass it to the widget. Any open guest conversation is linked to the member account.
const identity = await fetch("/api/chat-identity").then(r => r.json());
window.EmbrChat.setIdentity(identity);Logout: reload the page, or remove and reload the widget. The widget doesn't keep session data in cookies or localStorage, so the previous user's conversations are not visible after logout.
window.EmbrChat.destroy();
// then re-add the script tag or reload the page09Server-to-server session (optional)
When the signature can't be printed on the page (for example in an in-app web view), you can open a session directly from your server.
POST https://api.embrchat.com/v1/widget/sessions/exchange
content-type: application/json
x-operator-timestamp: 1767225600
x-operator-signature: hex(HMAC_SHA256(secret, "1767225600." + body))
{
"widgetKey": "wk_YOUR_SITE_KEY",
"subjectRef": "U-1042",
"authLevel": "member",
"issuedAt": 1767225600,
"profile": { "name": "Jane Doe", "email": "jane@example.com", "segment": "member" }
}{ "token": "ses_9f2c…", "expiresIn": 3600 }Pass the returned token on the page with window.EmbrChat.setSession(token). The timestamp must be within ±5 minutes of server time.
10JavaScript API: window.EmbrChat
| Method | Description |
|---|---|
open() / close() / toggle() | Opens, closes or toggles the chat window |
setIdentity({ payload, signature }) | Provides a signed identity after login |
setSiteToken(jwt, origin?) | Provides or refreshes the site token |
setVisitor({ name?, email? }) | Updates unverified guest details |
setSession(token) | Provides the server-to-server session token |
pageView() | Reports a page change in an SPA |
setLocale('en-GB') | Changes the widget language |
on(event, fn) / off(event, fn) | Adds or removes an event listener |
reset() | Clears the identity and restarts as a guest |
destroy() | Removes the widget from the page entirely |
To open the chat from your own button:
<a href="#" onclick="window.EmbrChat?.open(); return false;">Live support</a>?. before calling the API or wait for the ready event.11Events
| Event | When | Data |
|---|---|---|
ready | The widget has loaded and the API is ready | — |
open / close | The window was opened / closed | — |
conversation:start | A new conversation started | { conversationId } |
conversation:handoff | The assistant handed the conversation to an agent | { conversationId, queue } |
message:received | The visitor received a new message | { from: 'bot'|'agent' } |
conversation:end | The conversation was closed | { conversationId, rating? } |
window.addEventListener("EmbrChat:ready", () => {
window.EmbrChat.on("conversation:handoff", (e) => {
analytics.track("chat_handoff", { queue: e.queue });
});
});12Language
The default language is English. The widget language is chosen in this order:
- The
data-localeattribute orlocalesetting - The language the visitor previously picked in the widget
- The page's
<html lang>value - The site default in the panel
Visitors can switch language from inside the window. Texts you enter in the panel, such as the greeting and form labels, are defined per language under Widget appearance → Text language. When your site changes language:
window.EmbrChat?.setLocale("de-DE");13Single-page apps (React, Vue, Next.js)
Add the tag once to your app shell (index.html, _document, app.html). Hash changes and back/forward navigation are detected automatically. After navigations done with history.pushState, report the page change:
// Next.js (App Router)
"use client";
import { usePathname } from "next/navigation";
import { useEffect } from "react";
export function ChatPageView() {
const pathname = usePathname();
useEffect(() => { window.EmbrChat?.pageView(); }, [pathname]);
return null;
}// Vue Router
router.afterEach(() => window.EmbrChat?.pageView());14Channels
Channels decide which team and queue a conversation goes to. They are created in the panel under Advanced configuration → Channels; adding a channel or changing its audience requires approval from two admins.
| Audience | Who gets in |
|---|---|
everyone | Everyone, including guests |
members | Verified members only |
vip | Verified members with segment: "vip" only |
- A channel can require a pre-chat form (name, email, topic) for guests. Verified members and visitors with a
memberIdskip the form. - Requests about account security, payments and personal data always go to the specialist queue, regardless of channel settings; the assistant never answers them.
15Online visitors and proactive messages
While the page is visible, the widget sends a heartbeat every 30 seconds. Agents see who is on the site in the panel and can start a chat. The visitor then sees an invitation card with the agent's name, not the message content.
- An agent can't start a new chat while the visitor is talking to the assistant or waiting in a queue.
- Under Proactive messages in the panel you can set automatic greeting rules for specific pages (e.g. after 20 seconds on the pricing page).
- To turn this off entirely, use
data-lazy="true".
16File attachments
| Type | Extensions | Max size |
|---|---|---|
| Image | JPG, PNG, WebP, GIF | 10 MB |
| 10 MB | ||
| Audio | MP3, M4A, OGG, WAV | 20 MB |
| Video | MP4, WebM, MOV | 50 MB |
Up to 3 files can be sent at once. SVG, HTML and office documents are rejected for security reasons. All files are quarantined and scanned before they are shared; they can't be opened on the agent side until marked clean.
17Content Security Policy (CSP)
If your site uses a CSP, add the following sources:
script-src https://widget.embrchat.com
frame-src https://widget.embrchat.com
connect-src https://api.embrchat.comImages and files load through the API, so no extra img-src entry is needed.
18Data sent and privacy
| Data | When | Notes |
|---|---|---|
| Page URL | Every page view | Query string (?…) and hash (#…) are never sent |
| Page title | Every page view | Disable with data-send-title="false" |
| Referrer domain | On first load | Domain only, not the full URL |
| Device type | On first load | desktop / mobile / tablet |
| Name, email, member info | If provided | From visitor, signed identity or token |
| Messages and files | During a conversation | Over an encrypted connection (TLS) |
| Language | On first load | — |
| Random device ID | On first load | Contains no personal data |
The widget uses no cookies and does no fingerprinting. In localStorage it only keeps the chat-device and chat-lang keys; a dismissed banner is stored in sessionStorage.
19Platform guides
WordPress
- Open Appearance → Theme File Editor → footer.php, or use a plugin such as “WPCode / Insert Headers and Footers”.
- Paste the code into the Footer area and save.
- With Elementor, use Elementor → Custom Code and choose Body – End as the location.
- If you use a caching plugin (LiteSpeed, WP Rocket), clear the cache.
Shopify
- Go to Online Store → Themes → … → Edit code.
- In
layout/theme.liquid, paste the code right before</body>. - For logged-in customers, you can fill
visitorinside a{% if customer %}block.
Wix / Squarespace / Webflow
Add the code under Settings → Custom code, set the location to Body – end and apply it to All pages.
Google Tag Manager
- Create a New tag → Custom HTML and paste the code.
- Choose All Pages (DOM Ready) as the trigger.
- If GTM strips the
type="module"attribute, use this loader instead:
<script>
(function(){
var s = document.createElement("script");
s.type = "module"; s.async = true;
s.src = "https://widget.embrchat.com/v1/widget.js";
s.setAttribute("data-widget-key", "wk_YOUR_SITE_KEY");
document.body.appendChild(s);
})();
</script>20Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
| The button doesn't appear | Domain not in allowed origins; page is excluded; data-launcher="none"; CSP script-src missing |
| “Can't connect right now” | CSP connect-src or frame-src missing; ad blocker; wrong key |
| Member shows as a guest | Signature older than 10 minutes (cached page); payload changed after signing; wrong secret |
| VIP not routed to the VIP channel | No segment: "vip" in the payload; VIP channel not active in the panel |
| Agent can't see the visitor | data-lazy="true" is on; tab is in the background; visitor idle for more than 60 s |
| Stale page shown in an SPA | pageView() not called after pushState |
| “Chat restricted” card | The visitor was blocked by an agent; check the Blocked list in the panel |
21Before going live
- The tag loads exactly once on every page
- The live domain (including www) is in the allowed origins
- Signatures are generated on every page load; the secret stays on the server
- Cached pages don't contain stale signatures
- CSP updated (script, frame, connect)
- Guest, member and VIP flows tested separately
- Your privacy policy mentions the live chat tool
The Setup page in the panel generates a ready-made snippet for your site. If you get stuck, write to support@embrchat.com.