Are you an LLM? You can read better optimized documentation at /docs/embedding/sdk.md for this page in Markdown format
window.Perfox SDK
Your page loads, the chat bubble appears, and your user types a message — all before your app has finished setting up their session. The queue stub below makes sure no command is ever lost, and this page walks you through every window.Perfox command so you can control the widget from your own JavaScript.
If you haven't embedded the widget yet, start with the Embed Script Reference first. This page assumes the embed snippet is already on your page.
The queue stub
Drop this snippet before the widget bundle loads. It buffers any commands you fire early and replays them in order once the bundle is ready:
html
<script>
(function(){var w=window;w.Perfox=w.Perfox||function(){(w.Perfox.q=w.Perfox.q||[]).push(arguments);};})();
</script>Without this stub, any identify, on, or boot call you make before the bundle finishes loading fails, because window.Perfox doesn't exist yet. With the stub in place, those calls queue up and the real dispatcher replays them automatically when it takes over.
Commands
| Command | What it does |
|---|---|
boot(config) | Set widget config from JavaScript (overrides data-* attributes). |
identify(user) | Replace the signed-in visitor. |
update(patch) | Change some visitor fields, keep the rest. |
shutdown() | Log the visitor out of the widget. |
show() / hide() | Show or hide the chat bubble. |
open() / close() | Expand or collapse the chat panel. |
on(event, cb) / off(event, cb) | Subscribe to / unsubscribe from widget events. |
on:ui(event, handler, opts?) | Handle one UI command the agent or your server sends to this page. |
Any other command name logs a console.warn and does nothing.
boot(config)
Sets the widget config. All fields are optional and override the corresponding data-* attributes on the embed script tag:
js
Perfox('boot', {
workflow_id: '019df323-bf0f-7649-b40e-ff87c9dfabcd',
site_id: 'sa_site_live_…',
theme: 'bank_blue',
api_url: 'https://acme-api.perfox.ai',
user: { name: 'Asha', external_id: 'cust_abc' },
});Fields: workflow_id, site_id, theme, api_url, user. Passing user is the same as calling identify straight after boot. Call boot through the queue stub (before the bundle loads) — that's when the widget reads its config.
identify(user)
Replaces the entire signed-in user — any field you don't supply is dropped:
js
Perfox('identify', {
name: 'Asha Iyer',
email: 'asha@example.com',
phone: '+919876543210',
external_id: 'cust_abc',
attributes: { plan_tier: 'gold', region: 'apac' },
user_hash: '…hmac_from_server…',
});If the widget had already opened an anonymous conversation and the visitor hasn't typed anything yet, identifying them switches the widget to that person's own conversation and replays it. If they have already chatted, the current conversation carries on under their identity.
Full field reference → User Context & Identity. HMAC signing → Identity Verification.
update(patch)
Shallow-merges a patch into the current user, with a deep-merge of attributes:
js
Perfox('update', { phone: '+919876543210' });Use update when you want to add or change one field without touching the rest. Use identify when you need to replace the user entirely.
shutdown()
Clears the current user, disconnects any active voice call, forgets the conversation (and any pre-chat form answers) this browser would otherwise resume, closes the panel, and emits a user(undefined) event followed by shutdown. The widget then starts a fresh, anonymous session. Call this on logout so the next person who opens the same browser doesn't resume the previous session:
js
Perfox('shutdown');Visibility — show / hide / open / close
| Command | Effect |
|---|---|
Perfox('show') | Makes the chat bubble visible. |
Perfox('hide') | Hides the chat bubble. |
Perfox('open') | Expands the chat panel. |
Perfox('close') | Collapses the chat panel. |
show and hide control whether the bubble appears on the page. open and close control whether the panel is expanded or collapsed, independent of bubble visibility. Each emits its matching event only when the state actually changes.
on(event, cb) / off(event, cb)
Subscribe to widget events from your own code:
js
const onReply = (m) => analytics.track('chat_reply', { text: m.text });
Perfox('on', 'message:received', onReply);
Perfox('off', 'message:received', onReply); // same reference to unsubscribePass the same callback reference to off to remove it. Full event list → Events.
on:ui(event, handler, opts?)
Registers a handler for one named UI command — for example navigate — that the agent or your server sends to this page:
js
Perfox('on:ui', 'navigate', ({ payload }) => router.push(payload.url));Commands with no registered handler are ignored (with a console warning). For navigate, cross-origin URLs are dropped unless you pass { same_origin_only: false } or list the hosts you allow in { allowed_hosts: ['docs.example.com'] }. The whole mechanism is on UI-command bus.
Worked example — identify on login
Setup: Acme Diagnostics has the widget embedded site-wide. When a customer signs in, their session is available in the page.
Action: Right after login completes, Acme's app calls:
js
Perfox('identify', {
name: 'Priya Sharma',
email: 'priya@example.com',
external_id: 'pat_00123',
user_hash: hmacFromServer, // computed server-side from external_id
});Result: The next message Priya sends carries her identity. The AI greets her by name and can look up her records by external_id.
What just happened: The widget sends the current identity with every message, so calling identify before Priya types her first message is all that's needed. The queue stub means the call is safe even if it fires a fraction of a second before the bundle finishes loading.
Calling order is forgiving
Identity is sent with each message, so a late identify or update call applies as long as it lands before Priya sends her next message. The queue stub makes pre-load calls safe too — you don't have to wait for the bundle before calling any command.
How replies are delivered (async push)
As soon as the widget starts, it opens a WebSocket to Perfox. While that socket is open, POST /api/public/widget/send returns 202 Accepted with { accepted: true, message_id, reply_id, server_time } immediately, freeing the connection, and the assistant reply is pushed to the browser over the socket. reply_id correlates the push back to the 202. The reply text may stream into the bubble while the agent is still writing; the final push replaces it.
The reply reaches the browser no matter which server handled the turn. If a push is ever missed, the widget recovers the reply from the conversation history on its own. When the socket isn't available, the widget falls back to a synchronous /send where the reply is the HTTP response body. Either way the message:received SDK event fires when the reply lands.
You can now control the widget from your JavaScript
You've seen every command the SDK exposes, how identity flows from your app into the widget, and how replies arrive. Next steps:
- Embed Script Reference — set up or review the embed snippet that loads the widget onto your page.
- User Context & Identity — understand all available identity fields and how the AI uses them.
- Events — see every event you can subscribe to with
on. - Identity Verification (HMAC) — secure the
user_hashfield so users can't impersonate each other.