Chat
Chat adds an AI chatbot and live human chat to your website, with native SDKs for your mobile apps. The AI answers from your knowledge base around the clock, and your agents take over in the dashboard when a visitor needs a person.
How it workslink
- A visitor opens the widget on your site or app and talks to the AI assistant first; it answers from the knowledge you've configured.
- When the AI can't help, or the visitor asks, the conversation hands off to a human agent in Dashboard → Chat, honouring your routing rules and agent availability.
- Conversations are real-time both ways (server-sent events with WebSocket fallback), support file attachments, typing indicators and unread counts, and survive page reloads.
- Prompts are screened by the platform's AI-injection guard; manipulation attempts are rejected before they reach the model.
Integration surfaceslink
| Surface | How | Auth |
|---|---|---|
| Web | embed.js script tag or the NInstanceChat JavaScript API (below) | A customer token minted by your server (recommended), or the full key on trusted internal pages |
| Android / iOS / React Native / Flutter | Native chat SDKs (sub-pages below) with a ready-made chat UI | The same two modes |
Two exclusive auth modes (SDK 0.4.0)link
Every chat client is configured with exactly one credential. Passing both, or neither, is a configuration error (E_CONFIG).
- Customer token (recommended). Your backend mints a short-lived token with
POST /api/v1/chat/sessionsusing the fullkeyId:secret, and hands only the token to the client. The token is the identity: the customer's details never have to be passed into the SDK, a tampered client cannot claim to be somebody else, and the secret never leaves your server. Use this for every app or website you distribute to the public. - Key mode (trusted internal apps only). The client is given the full
keyId:secretand names the customer withsetUser. This ships your secret inside the client, so use it only for internal tools and apps you control, never for public apps.
The older bare key ID form (sk_live_abc123 with no secret) is deprecated. Deployed web embeds and SDKs below 0.4.0 keep working; the 0.4.0 mobile SDKs reject it. See Authentication and identity for both modes, the token API, the migration notes and the diagnostic error codes; see Theming for how dashboard, local and built-in themes combine.
To let the assistant call your own tools for a signed-in customer, see MCP servers.
Configure your chat widgetlink
- Log in to the merchant dashboard
- Navigate to Dashboard → Org → Chat (
/dashboard/org/[orgId]/chat) - Configure your widget: appearance, AI chatbot, routing rules, agent availability
- Create a chat API key under Dashboard → Org → API Keys and store the full
keyId:secreton your server. Your backend uses it to mint customer tokens; it never goes into public client code.
Embed the widget via script tag (auto-init)link
Add this to your HTML <head> or just before </body>, with a token your server minted for the signed-in customer:
<script
src="https://widget.newinstance.cloud/embed.js"
data-customer-token="TOKEN_FROM_YOUR_BACKEND"
data-theme="dark"
data-position="bottom-right"
></script>data attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
data-customer-token | string | none | Customer token minted by your server. Selects customer token mode (recommended). Pass this or data-api-key, not both |
data-api-key | string | none | The full keyId:secret selects key mode, for trusted internal pages only. A bare key ID is the deprecated legacy form |
data-theme | dark or light | light | Widget colour scheme (only these two values) |
data-position | bottom-right or bottom-left | bottom-right | Button position |
data-transport | sse or ws | inherited | Realtime transport override (testing) |
data-customer-name / -email / -id | string | none | Deprecated bare key ID embeds only. In token mode the identity comes from the token; in key mode call setUser |
data-auto-open | boolean | false | Open the panel on load |
data-gql-endpoint / data-widget-url | URL | derived | Self-host overrides for API endpoint and iframe origin |
Embed via JavaScript API (full control)link
<script src="https://widget.newinstance.cloud/embed.js"></script>
<script>
const widget = NInstanceChat.init({
customerToken: tokenFromYourBackend,
theme: 'dark',
position: 'bottom-right',
autoOpen: false,
callbacks: {
onAuth: (info) => console.log('Chat auth:', info.valid, info.status),
onOpen: () => console.log('Chat opened'),
onClose: () => console.log('Chat closed'),
},
});
// Programmatic control
widget.open();
widget.close();
widget.toggle();
widget.isOpen();
widget.setUserToken(freshTokenFromYourBackend);
widget.updateConfig({ theme: 'light' });
widget.destroy();
// File attachments (before opening composer)
widget.attachFile(file);
widget.attachFiles([fileA, fileB]);
widget.removeAttachment(id);
widget.clearAttachments();
widget.getAttachments();
widget.openComposer();
</script>NInstanceChat.init(config) options
| Option | Type | Description |
|---|---|---|
customerToken | string | Customer token minted by your server. Selects customer token mode (recommended) |
apiKey | string | The full keyId:secret. Selects key mode, for trusted internal pages only. Pass customerToken or apiKey, not both. A bare key ID is the deprecated legacy form |
theme | dark or light | Widget theme |
transport | sse or ws | Realtime transport; inherited from platform settings (SSE when nothing is advertised), override for testing |
gqlEndpoint / widgetUrl | URL | Self-host overrides for the API endpoint and iframe origin |
customerName / customerEmail / customerId | string | Deprecated bare key ID embeds only. Use the token, or setUser in key mode |
position | bottom-right or bottom-left | Button position |
autoOpen | boolean | Open the chat on load |
buttonSize | number | Launcher button size in px |
panelWidth | number | Chat panel width in px |
panelHeight | number | Chat panel height in px |
zIndex | number | CSS z-index for the widget |
callbacks | object | onReady, onAuth, onOpen, onClose, onDestroy |
The instance init returns also has setUserToken(token), which swaps the customer token without reloading the iframe, and setUser({ customerName, customerEmail, customerId }), which names the customer in key mode. setUserToken is a configuration error in key mode.
Defaults: buttonSize 60, panelWidth 400, panelHeight 620, zIndex 2147483647, autoOpen false, position bottom-right. Calls made before the auth handshake are queued and flushed after validation; every bridged call times out after 15 seconds. attachFile accepts a File, Blob, data URL or bare base64 plus an optional name; attachment snapshots carry id, name, type, size, status, progress and an error reason or URL. openComposer() also opens the panel locally, so isOpen() flips synchronously. updateConfig reloads the iframe and resets an in-progress conversation; only theme, customerName and customerEmail are re-applied.
Widget behaviour beyond these options (welcome and offline messages, input placeholder, file-upload, emoji and typing toggles, email-required, sound, online status, branding) is configured in the dashboard, not from the embed.
Conversation modellink
Flow states run idle → collecting_info → bot_conversation → handoff_pending → live_chat → ended | error; conversation statuses are BOT_ACTIVE, WAITING, ACTIVE, RESOLVED and CLOSED; channels are ai, live and ticket. The pre-chat info form is skipped when the customer is already known: in customer token mode, or when a non-blank customerName was supplied. The transport heartbeats roughly every 10 seconds; a missed beat within about 16 seconds tears down, reconnects and replays missed messages from the server event log; connectivity is re-checked on online, visibilitychange and pageshow.
Widget lifecyclelink
- Auto-init runs on window load (immediately if the document is already complete) and only when
data-customer-tokenordata-api-keyis present. - A second
init()returns null and warns that the widget is already initialized. - If the iframe never answers, a 10 second safety timeout removes the widget silently; on auth failure the root is removed,
onDestroyfires, queued calls reject, and the console explains that chat is unavailable, disabled or not configured. destroy()notifies the iframe, then rejects in-flight bridged calls with "widget destroyed".- The host page CSP must allow the widget origin in both
frame-srcandscript-src.
How the iframe bridge workslink
The widget loads in a sandboxed <iframe>. All communication between the host page and the widget uses postMessage with source-tag and origin validation:
- Host → widget messages are tagged with
ninstance-chat-host - Widget → host messages are tagged with
ninstance-chat
The widget fires onAuth after credential validation. The button stays hidden until onAuth confirms valid: true.
Permission scopes for Chat API keyslink
| Scope code | Description |
|---|---|
full-access | All chat permissions (recommended) |
ai-chatbot-access | Interact with AI chatbot |
live-chat-operations | Manage live chat sessions |
chat-configuration | Configure widget settings |