6.5 KiB
Embedding the Support Widget
The widget adds a chat launcher to any web page. It loads a single script, shows a floating button, and opens the support chat in an iframe. Visitors can keep several requests open and switch between them.
Quick start
Replace https://support.example.com with the address of your deployment and YOUR_CHANNEL_ID with the channel ID from the dashboard.
<script>
window.AgentDeskConfig = {
channelId: "YOUR_CHANNEL_ID",
}
</script>
<script src="https://support.example.com/sdk/agent-desk-sdk.min.js" async></script>
Put both tags before </body>. The AgentDeskConfig block must run before the SDK script. The widget then mounts itself and the launcher appears in the bottom corner.
Title, subtitle, theme color, position and width are read from the channel settings in the dashboard, so you normally do not set them in code.
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
channelId |
string |
– | Required. The channel the widget belongs to. |
baseUrl |
string |
script origin | Address of the Agent Desk server. |
apiBaseUrl |
string |
baseUrl |
Use when the API is served from a different origin. |
widgetBaseUrl |
string |
script origin | Where the chat page is loaded from. |
language |
"zh-CN" | "en-US" |
server setting | Interface language. Any en* value selects English. |
position |
"left" | "right" |
channel setting | Side of the screen for the launcher and window. |
themeColor |
string |
channel setting | Accent color, for example #0f6cbd. |
width |
string |
380px |
Width of the chat window on desktop. |
title, subtitle |
string |
channel setting | Header texts. |
externalId |
string |
browser-local ID | Stable ID of your own user. |
signExternalId |
(id) => {signature, issuedAt} |
– | Proves externalId is genuine. See Identified visitors. |
externalName |
string |
– | Display name, used when the visitor is first created. |
getUserToken |
() => string | Promise<string> |
– | Returns a JWT issued by your system. |
On screens up to 480px wide the chat opens full screen.
JavaScript API
AgentDeskWidget.open() // Promise<void>, opens the chat
AgentDeskWidget.close() // minimizes the chat
AgentDeskWidget.mount(cfg) // mounts with an explicit config
AgentDeskWidget.destroy() // removes launcher and chat
AgentDeskWidget.getChatUrl() // Promise<string>, URL for a standalone page or new tab
Open the chat from your own button:
<button onclick="AgentDeskWidget.open()">Contact support</button>
React and Next.js
Load the script once on the client. In Next.js (App Router):
"use client"
import Script from "next/script"
export function SupportWidget({ channelId }: { channelId: string }) {
return (
<>
<Script id="agent-desk-config" strategy="afterInteractive">
{`window.AgentDeskConfig = ${JSON.stringify({ channelId })}`}
</Script>
<Script
src="https://support.example.com/sdk/agent-desk-sdk.min.js"
strategy="afterInteractive"
/>
</>
)
}
Config that contains functions (signExternalId, getUserToken) cannot be serialized. Mount it manually instead:
useEffect(() => {
const script = document.createElement("script")
script.src = "https://support.example.com/sdk/agent-desk-sdk.min.js"
script.onload = () =>
window.AgentDeskWidget?.mount({
channelId,
externalId: user.id,
signExternalId: (id) => fetch(`/api/support/sign?id=${id}`).then((r) => r.json()),
})
document.body.appendChild(script)
return () => window.AgentDeskWidget?.destroy()
}, [channelId, user.id])
Identified visitors
By default a visitor is an anonymous guest identified by a browser-local ID. To link chats to your own users, pass externalId and a signature, so nobody can impersonate another user:
signature = hex(HMAC-SHA256(identitySecret, channelId + "\n" + externalId + "\n" + issuedAt))
issuedAt is the current Unix time in seconds. The server only accepts a signature younger than identity.maxAgeMinutes, so the widget requests a fresh one every time the chat opens.
Important
Compute the signature on your server. The identity secret must never be shipped to the browser.
Node.js endpoint:
import crypto from "node:crypto"
app.get("/api/support/sign", requireLogin, (req, res) => {
const issuedAt = Math.floor(Date.now() / 1000)
const payload = `${CHANNEL_ID}\n${req.user.id}\n${issuedAt}`
const signature = crypto
.createHmac("sha256", process.env.AGENT_DESK_IDENTITY_SECRET)
.update(payload)
.digest("hex")
res.json({ signature, issuedAt })
})
Go:
func sign(secret, channelID, externalID string) (string, int64) {
issuedAt := time.Now().Unix()
mac := hmac.New(sha256.New, []byte(secret))
fmt.Fprintf(mac, "%s\n%s\n%d", channelID, externalID, issuedAt)
return hex.EncodeToString(mac.Sum(nil)), issuedAt
}
Note that the endpoint uses your logged-in user, not a value from the query string, as the externalId it signs. An unsigned or invalid pair never breaks the widget: the visitor simply stays a guest.
Content Security Policy and CORS
If your site sets a CSP, allow the Agent Desk origin:
script-src https://support.example.com;
frame-src https://support.example.com;
connect-src https://support.example.com;
When the widget and API live on different origins, add your site to the allowed origins of the deployment so the config request succeeds.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| No launcher appears | channelId is missing or AgentDeskConfig is defined after the SDK script. Check the browser console for channelId is required. |
| Launcher appears, chat stays blank | The browser blocks the iframe. Check CSP frame-src and any X-Frame-Options set by a proxy on the Agent Desk origin. |
| Visitor always shows as guest | The signature is missing, expired or computed with the wrong secret or payload order. Check server clock drift against identity.maxAgeMinutes. |
| Wrong language | Set language explicitly, otherwise the server default is used. |
| Widget duplicated in a single-page app | Call AgentDeskWidget.destroy() when the component unmounts. |
| Chat shows "Something went wrong" | Choose Reload chat. If it persists, open the console and report the logged Support chat crashed error. |