Add IntelliDesk to your website or app
Install the chat widget
Paste this before </body> on every page where the chat should appear. Your snippet, with your address and agent filled in, is in the dashboard under Deploy → Install.
<script src="https://YOUR_APP/widget.js" data-agent="AGENT_SLUG_OR_ID" async></script>data-agentis the agent's slug or id. Renaming the slug later doesn't break pages that use the old one.- To keep other websites from showing your agent, list your domains in Deploy → Chat widget → Embed → Allowed domains. Subdomains of a listed domain are allowed too.
- Two scripts with different
data-agentvalues on one page each add their own chat. data-originpoints the script at IntelliDesk when you servewidget.jsfrom your own server.
To call the widget from your own code (below), add the queue first: calls made before widget.js loads wait and run once it has.
<script>
// Lets your code call intellidesk(...) before widget.js has loaded: calls wait in a queue.
window.intellidesk = window.intellidesk || function () {
(window.intellidesk.q = window.intellidesk.q || []).push(arguments);
};
window.intellideskConfig = {
showFloatingInitialMessages: true,
floatingInitialMessagesDelay: 2,
floatingMessagesOncePerSession: true
};
</script>
<script src="https://YOUR_APP/widget.js" data-agent="AGENT_SLUG_OR_ID" async></script>Embed it in the page (iframe)
Shows the chat inside your layout instead of as a floating bubble: a help page, a support tab in your app. Allowed domains apply to the page the iframe is on.
<iframe
src="https://YOUR_APP/embed/AGENT_SLUG_OR_ID"
title="Chat"
width="420" height="720"
allow="microphone; clipboard-write"
style="border:0;border-radius:16px"
></iframe>The iframe is always visible and has no JavaScript API. To open, close or identify from code, use the widget.
Referrer-Policy: no-referrer can't show a domain-locked agent. Keep the default policy (strict-origin-when-cross-origin) or use the widget.React, Next.js and other single-page apps
Load the script once, in your root layout. The widget follows page changes on its own, so the agent and your team see which page a visitor is on.
// app/layout.tsx (Next.js App Router): the queue, then the widget, once for every page.
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script id="intellidesk-queue" strategy="beforeInteractive">
{`window.intellidesk = window.intellidesk || function () {
(window.intellidesk.q = window.intellidesk.q || []).push(arguments);
};`}
</Script>
<Script src="https://YOUR_APP/widget.js" data-agent="AGENT_SLUG_OR_ID" strategy="afterInteractive" />
</body>
</html>
);
}When your app knows who is signed in, identify them. Call resetUser on logout, so the next person on that browser starts fresh instead of seeing their chat:
"use client";
// Signs the user in to the chat once your app knows who they are.
import { useEffect } from "react";
declare global {
interface Window { intellidesk?: (...args: unknown[]) => void }
}
export function ChatIdentity({ user }: { user: { id: string; name: string; email: string } | null }) {
useEffect(() => {
if (!user) return;
// Your server signs the user id (see /docs/identity) and returns { user_id, user_hash }.
fetch("/api/intellidesk-identity")
.then((r) => r.json())
.then((signed) => window.intellidesk?.("identify", { ...signed, name: user.name, email: user.email }));
}, [user?.id]);
return null;
}
// In your logout handler:
window.intellidesk?.("resetUser");JavaScript API
The global is window.intellidesk. Every method also works in the queue form, intellidesk("open").
window.intellidesk.open(); // open the chat
window.intellidesk.open({ message: "Hi" }); // open it and send a message
window.intellidesk.close();
window.intellidesk.resetChat(); // new conversation, same visitor
window.intellidesk.setInitialMessages(["Welcome back."]);
window.intellidesk.identify({ user_id: "u_123", user_hash: "…", name: "Ada" });
window.intellidesk.resetUser(); // on logout: a new anonymous visitor
window.intellidesk.addEventListener("ready", (e) => console.log("chat loaded", e.agentId));
window.intellidesk.addEventListener("open", () => analytics.track("chat_opened"));
window.intellidesk.addEventListener("user-message", (e) => console.log(e.message));
window.intellidesk.addEventListener("assistant-message", (e) => console.log(e.message, e.conversationId));
window.intellidesk.addEventListener("error", (e) => console.warn("chat unavailable:", e.reason));
// The same calls in the queue form, which also works before widget.js has loaded:
window.intellidesk("open");
window.intellidesk("identify", window.intellideskUserConfig);Methods
| Method | What it does |
|---|---|
| open(options?) | Opens the chat. { message: "…" } also sends that message. |
| close() | Closes the chat. |
| resetChat() | Starts a new conversation. The visitor, and their earlier chats, stay. |
| setInitialMessages(list) | Replaces the greeting shown before the visitor writes. |
| identify(user) | Signs in a user: user_id + user_hash, or token (a JWT). Other fields (name, email, plan…) are shown to the agent. Someone else signing in on the same browser gets a new chat. See Identity verification. |
| resetUser() | Call on logout. Forgets the user and starts a new anonymous visitor: no chat, no history, no saved pages. |
| addEventListener(type, fn) | Listens for an event (below). |
| removeEventListener(type, fn) | Stops listening. |
Events
| Event | Fires when | Details |
|---|---|---|
| ready | The chat has loaded. A listener added later still gets it. | agentId |
| open | The chat opens. | agentId |
| close | The chat closes. | agentId |
| user-message | The visitor sends a message. | message, agentId |
| assistant-message | The agent finishes a reply. | message, agentId, conversationId |
| error | The chat can't load, or can't chat with this visitor. | reason: domain_not_allowed, agent_not_found, identity_required or load_failed |
Options
Set window.intellideskConfig before the script. Keys you set override the ones saved on Deploy → Chat widget → Content.
| Option | What it does |
|---|---|
| hideLauncher | No floating bubble: open the chat from your own button. Same as data-launcher="hidden" on the script tag. |
| showFloatingInitialMessages | Shows the greeting next to the bubble before the chat is opened. |
| floatingInitialMessagesDelay | Seconds before that greeting appears (0 to 10, default 2). |
| floatingMessagesOncePerSession | Shows it once per browser session (default true). |
Open the chat from your own button
<script>
window.intellideskConfig = { hideLauncher: true }; // or data-launcher="hidden" on the script tag
</script>
<script src="https://YOUR_APP/widget.js" data-agent="AGENT_SLUG_OR_ID" async></script>
<button onclick="window.intellidesk('open')">Chat with us</button>Identify signed-in users
Tell the agent who a signed-in user is. It can greet them by name, the chat shows up on their contact in IntelliDesk, and actions like order lookups know whose order it is. Your server signs the user id with the agent's identity secret (Deploy → Chat widget → Embed); the browser passes it on.
window.intellideskUserConfig = {
user_id: "u_123",
user_hash: "HMAC_SHA256_HEX_FROM_YOUR_SERVER",
name: "Ada",
current_page: location.pathname,
};
window.intellidesk("identify", window.intellideskUserConfig);Server code for Node, Python, PHP, Ruby and Go, JWTs, and what changes when verification is required: Identity verification.
Troubleshooting
The widget explains problems in the browser console, prefixed [IntelliDesk]. Visitors only see a short message.
| Console says | Fix |
|---|---|
| isn't one of the agent's allowed domains | Add the site under Deploy → Chat widget → Embed → Allowed domains. |
| There is no agent "…" | Check data-agent: Deploy → Install shows the right value. |
| Identity verification required | This agent only chats with signed-in users: identify them (above), or turn verification off. |
| The chat couldn't load | The script can't reach IntelliDesk: check data-origin and your Content-Security-Policy. |
With a Content-Security-Policy, allow your IntelliDesk address in script-src, connect-src (with wss: for live replies), img-src and, for the iframe, frame-src. The widget's animations and hover styles also need style-src 'unsafe-inline'; the chat works without them.
Floating initial messages
Save the flags on Deploy → Chat widget → Content. The bubble uses that saved widget content when the host page has no window.intellideskConfig. A host config set before the script loads still overrides matching keys. Default delay is 2 seconds. floatingMessagesOncePerSession defaults to true and uses id_float_{agentId} in sessionStorage.
What the widget stores in the browser
| Key | What it is |
|---|---|
| id_visitor_{agentId} | The anonymous visitor id (localStorage). |
| id_convo_{source}_{agentId} | The visitor's current conversation, for the widget or the help page. |
| id_user_{agentId} | The signed-in user that conversation belongs to. |
| id_journey_{agentId} | The last pages viewed (up to 10, within a day), sent with the first message. |
| id_float_{agentId} | Floating messages already shown this session (sessionStorage). |
resetChat() clears the conversation and keeps the visitor. resetUser() clears all of it. When the browser blocks storage, the widget still works for that page view.
Public routes
What the widget and help page call. They need no key; the agent's domain lock and rate limits apply.
| Method | Route | Notes |
|---|---|---|
| GET | /api/public/agent/:agentIdOrSlug | Widget config. Allowed domain + public visibility. Id or slug. |
| GET | /api/public/conversation/:id | Restore messages when visitorId matches. |
| GET | /api/public/conversations | This visitor’s widget/help threads. |
| DELETE | /api/public/conversation/:id | Visitor may delete their own thread. |
| POST | /api/public/conversation/:id/transcript | Email the thread to the address the visitor gave in it (3 an hour). |
| POST | /api/public/chat | HTTP chat. Same domain, rate-limit, and identify rules as Socket.io. Optional attachmentIds after POST /api/public/attachment. Optional voice=true marks the visitor turn as spoken. |
| POST | /api/chat/:agentId | Help-proxy alias for POST /api/public/chat. agentId can live in the path. |
| GET | /__id/* | Help page proxy assets and /__id/api/* public routes. /__id/help/:slug is the rewritten help HTML. |
| POST | /api/public/leads | Lead form (also /api/leads). Domain lock; returns only the visitor's own details. |
| POST | /api/public/feedback | Thumbs up or down on a reply (also /api/feedback). Domain lock. |
| POST | /api/public/attachment | Visitor file upload (images, pdf, txt, docx). Domain + rate-limit + channel-off rules. |
| GET | /api/public/attachment/:id | Download a visitor file when visitorId matches. |
| GET | /api/public/demo | Demo agent payload. |
| Socket.io | chat:start | Public stream. source=widget or help. Optional handoff=true requests a human when Live chat is on. |
| Socket.io | chat:join / operator:message | Visitor joins a conversation room and receives operator replies. |
source=playground. The widget is source=widget. Help page: /help/AGENT_ID. Customer origin rewrite: /help + /__id/* (see Deploy → Help → Domain). Local path: /help-proxy/AGENT_ID. For your own backend, use the REST API with a secret key instead.