SDK Reference
Who can use this feature?
- Available to all users on any plan.
- New to the SDK? Start with the SDK overview. For events, see SDK Events.
The SDK is the global object window.$chatty. This page lists every command it accepts.
| Global object | window.$chatty |
| SDK version | 1.1.0, read at runtime with $chatty.version |
| Install | Ships with the Chatty widget. Nothing extra to add. |
| Commands | 18 do actions, 13 set actions |
| Events | 22, documented on SDK Events |
Methods
| Method | Returns | Description |
|---|---|---|
push([verb, action, ...args]) | undefined | Run a command. Verbs: do, set, on, off. |
on(event, callback) | undefined | Subscribe to an event. |
off(event, callback) | undefined | Unsubscribe. Pass the same function reference used in on. |
is(key) | boolean | Synchronously read a boolean state (see States). |
get(key) | value | Synchronously read a value (see Values). |
version | string | SDK version, e.g. '1.1.0'. |
No method returns a result and no method throws. A command that fails validation is dropped with a console warning. See Error handling.
on and off are also push verbs, so push(['on', event, cb]) is equivalent to on(event, cb). Before the widget bundle loads, push is the only method that exists, so the push form is the safe one to use at the top of a page.
Command format
A command is an array: the verb, the action name, then the arguments.
window.$chatty.push(['do', 'message:send', 'text', 'I need help'])The arguments can also be wrapped in a single array. Both forms below are identical:
window.$chatty.push(['set', 'prechat:field', 'name', 'Jane Doe'])
window.$chatty.push(['set', 'prechat:field', ['name', 'Jane Doe']])An array with fewer than two entries, an unknown verb, or an unknown action name is dropped with a console warning.
do actions
Run with push(['do', action, ...args]). Eighteen actions in six groups.
Widget visibility
| Action | Arguments | Required | Description |
|---|---|---|---|
chat:open | None | — | Open the chat window. |
chat:close | None | — | Close the chat window, back to the launcher. |
chat:toggle | None | — | Open if closed, close if open. |
chat:show | None | — | Show the widget again after chat:hide. |
chat:hide | None | — | Hide the whole widget, both the launcher and the window. |
launcher:show | None | — | Show only the launcher button. |
launcher:hide | None | — | Hide only the launcher button. |
window.$chatty.push(['do', 'chat:open'])
window.$chatty.push(['do', 'launcher:hide'])chat:toggle decides from the state the widget last reported, the same value is('chat:opened') returns.
launcher:show and launcher:hide do not change is('chat:visible'), which tracks only chat:show and chat:hide. There is no state key for launcher visibility on its own.
Messaging
| Action | Arguments | Type | Required | Description |
|---|---|---|---|---|
message:send | type | string | Yes | Must be 'text'. Any other value is rejected. |
content | string | Yes | The message body. 1 to 5,000 characters, no HTML. | |
typing:set | active | boolean | Yes | Show or clear the visitor's typing indicator. A non-boolean is rejected. |
window.$chatty.push(['do', 'message:send', 'text', 'I need help with my order'])
window.$chatty.push(['do', 'typing:set', true])The SDK cannot send attachments or rich content. 'text' is the only message type.
If a required pre-chat form is on screen, message:send is held and delivered as the first message as soon as the form is submitted. Only the most recent held message survives, so a second call replaces the first.
message:send is rate limited at 15 per minute and one per second. typing:set is limited to 30 calls per minute, and only calls with active set to true count against it.
Identity and session
| Action | Arguments | Type | Required | Description |
|---|---|---|---|---|
identify | fields | object | Yes | Set visitor identity in one call. |
logout | None | — | — | Clear the identity, the pre-chat prefill, the commerce context, and the stored session, then start a new conversation. |
session:reset | None | — | — | End the current conversation and start a new one. Also clears the identity and the pre-chat prefill. |
identify accepts these fields, all optional on their own, but at least one has to be valid or the whole call is dropped:
| Field | Type | Description |
|---|---|---|
email | string | Must look like an email address. Trimmed and lowercased before use. |
name | string | Non-empty, no HTML. |
phone | string | Non-empty, no HTML. |
attributes | object | Structured attributes about the visitor. |
window.$chatty.push(['do', 'identify', {
email: '[email protected]',
name: 'Jane Doe',
phone: '+15550134',
attributes: { plan: 'gold' }
}])Invalid fields are dropped one by one, not as a group. An identify call with a malformed email and a valid name still applies the name.
identify and the matching set user:* actions write to the same visitor record and merge, so use whichever fits your code. Use identify for a single call at page load, and set when values arrive at different times. The one difference: the per-field limits in Limits and validation are enforced by the set actions, while identify checks format only.
The difference between logout and session:reset is what happens to the visitor's stored session. logout clears it, so the next conversation cannot be linked back to the previous one. session:reset leaves it in place. Call logout when the shopper signs out of your store, and session:reset when you only want a clean conversation.
Identity passed from the browser is supplied by the page, not verified by Chatty. Treat it as a convenience for your support team, not as proof of who the visitor is. Do not use it to gate anything sensitive. See Security and trust boundary.
Conversation control
| Action | Arguments | Type | Required | Description |
|---|---|---|---|---|
escalate | None | — | — | Ask for a human agent. This is the AI to human handoff. |
trigger:run | campaignId | string | Yes | Run a chat campaign by ID. 1 to 200 characters. |
article:show | articleId | string | Yes | Open an FAQ article inside the widget. 1 to 200 characters. |
window.$chatty.push(['do', 'escalate'])
window.$chatty.push(['do', 'article:show', 'ARTICLE_ID'])Both IDs must be strings. A number is rejected before it reaches the widget, so wrap it: String(articleId).
escalate does nothing when the conversation is already with a human agent, or when no conversation exists yet. When it does run, it posts a visible message into the conversation on the visitor's behalf and takes the conversation out of AI mode. It does not pick an agent: the conversation still has to be assigned, by your assignment rules or by hand.
trigger:run and article:show are matched against the campaigns and articles the widget has already loaded. An ID it does not recognise is logged as a [ChattySDK] warning and nothing opens.
Pre-chat form
Available from SDK 1.1.0.
| Action | Arguments | Required | Description |
|---|---|---|---|
prechat:submit | None | — | Submit the form using the values you prefilled. |
prechat:skip | None | — | Skip the form and chat anonymously, when the shop allows skipping. |
if (window.$chatty.is('prechat:visible') && !window.$chatty.is('prechat:required')) {
window.$chatty.push(['do', 'prechat:skip'])
}prechat:submit submits the values you set with set prechat:field, not whatever the visitor typed into the form. Prefill the fields first.
It checks only the fields the shop marked required, which can be name and email. Phone is never required. A missing required field cancels the submission and logs a warning.
Skipping is not submitting: prechat:skip emits no prechat:submitted and no email:captured event.
Custom event tracking
| Action | Arguments | Type | Required | Description |
|---|---|---|---|---|
event:track | name | string | Yes | Event name, 1 to 200 characters. |
data | object | No | Up to 20 keys. String, number, and boolean values only. |
window.$chatty.push(['do', 'event:track', 'viewed_size_guide', { product: 'shirt-101' }])data must be a plain object. An array is discarded entirely, and inside the object any value that is not a string, number, or boolean is dropped key by key. Keys past the twentieth are dropped, and string values longer than 1,000 characters are truncated rather than rejected.
Every tracked event is also re-dispatched on the page as a DOM CustomEvent named chatty:event. See Custom events you raise yourself.
set actions
Run with push(['set', action, value]). Thirteen actions.
Composer and visitor data
| Action | Value | Type | Description |
|---|---|---|---|
message:text | text | string | Prefill the input box without sending. The visitor can still edit it. |
user:email | string | Visitor email. Must look like an email address. | |
user:name | name | string | Visitor name. Non-empty, no HTML. |
user:phone | phone | string | Visitor phone. Non-empty, no HTML. |
user:attributes | attributes | object | Structured attributes about the visitor. Merges across calls. |
user:context | context | object | Free-form context shown to your team and the AI. Merges across calls. |
shop:data | data | object | Free-form context about the shop or page. Merges across calls. |
conversation:attributes | list | array | Named pairs stored on the conversation, as [{name, value}]. |
window.$chatty.push(['set', 'message:text', 'I want to know about shipping'])
window.$chatty.push(['set', 'user:email', '[email protected]'])
window.$chatty.push(['set', 'user:context', { vipLevel: 3, accountAge: '2 years' }])user:context, shop:data, and user:attributes merge rather than replace, so you can add keys as the page learns more. Values must be a string, number, or boolean; anything else is dropped silently.
conversation:attributes replaces the previous list rather than merging. Each entry needs a non-empty name and a non-empty value; both are converted to text, and an entry missing either one is dropped.
set user:email has a side effect worth knowing: it also prefills the pre-chat form's email field, so a visitor who reaches the form sees the address already filled in.
Commerce context
Give the AI assistant structured context about what the visitor is looking at.
| Action | Value | Type | Description |
|---|---|---|---|
product | product | object | The product currently being viewed. |
cart | cart | object | The current cart. |
order | order | object | The order being discussed. |
window.$chatty.push(['set', 'product', { id: 123, title: 'Steel Door', price: '499.00' }])
window.$chatty.push(['set', 'cart', { items: 2, total: '648.00' }])
window.$chatty.push(['set', 'order', { name: '#1042', status: 'shipped' }])Each object must survive a round trip through JSON and stay under 8,000 characters once serialized. An oversized object is rejected whole, not truncated, so nothing is set at all. The three keys are independent: setting cart leaves product and order untouched.
Read the merged result back with $chatty.get('commerce:context'). do logout clears all three.
Locale
| Action | Value | Type | Description |
|---|---|---|---|
locale | locale | string | Language hint for the conversation. 1 to 35 characters. |
window.$chatty.push(['set', 'locale', 'de'])This is a hint that applies to the current conversation, not a switch. It takes effect only once a conversation exists, and it does not force the language the AI replies in. The assistant answers in the language the visitor writes in.
Pre-chat prefill
Available from SDK 1.1.0. Takes a field name and a value, so this is the one set action with two arguments.
| Field | Type | Limit |
|---|---|---|
name | string | 1 to 150 characters, no HTML |
email | string | Must look like an email address, up to 254 characters |
phone | string | 1 to 50 characters, no HTML |
message | string | 1 to 5,000 characters, no HTML |
window.$chatty.push(['set', 'prechat:field', 'name', 'Jane Doe'])
window.$chatty.push(['set', 'prechat:field', 'email', '[email protected]'])
window.$chatty.push(['set', 'prechat:field', 'phone', '+15550134'])
// 'message' is queued and sent automatically once the form is submitted
window.$chatty.push(['set', 'prechat:field', 'message', 'Where is my order #1042?'])Any other field name is rejected with a warning.
message behaves differently from the other three. It is not a form field: it is held as the first message and sent once the form is submitted. It shares that slot with a held do message:send, so the last of the two wins.
Prefills are sticky, so they apply even if the form appears later in the session. do logout and do session:reset clear them.
States
Read synchronously with is(key) → boolean. An unknown key returns false with no warning, so check your spelling.
| Key | True when |
|---|---|
chat:opened | The chat window is open. |
chat:closed | The chat window is closed. The exact opposite of chat:opened. |
chat:visible | The widget has not been hidden with chat:hide. Starts true. |
session:ongoing | A conversation exists, which is true from the visitor's first message onward. |
prechat:visible | The pre-chat form is on screen. |
prechat:required | The pre-chat form must be completed before chatting. |
agent:online | Support is within chat hours. |
if (window.$chatty.is('chat:opened')) {
window.$chatty.push(['do', 'chat:close'])
}These values mirror what the widget last reported. Read them from an event handler rather than at the top of the page, where the widget may not have reported anything yet.
Values
Read synchronously with get(key). An unknown key returns null.
| Key | Returns | Before the widget reports |
|---|---|---|
message:text | Current text in the composer, as a string. | '' |
session:identifier | The conversation ID. | null |
chat:unread:count | Unread messages for the visitor, as a number. | 0 |
prechat:config | { mode, preChatFields, requiredName, requiredEmail, required } | null |
commerce:context | The merged product, cart, and order objects. | {} |
const unread = window.$chatty.get('chat:unread:count')prechat:config fields: mode is the shop's pre-chat setting, preChatFields is the list of fields the form shows, requiredName and requiredEmail say which of them the visitor must fill in, and required is true when the form cannot be skipped.
Limits and validation
The SDK enforces limits to keep the widget responsive and to prevent abuse. Values marked truncated are shortened and still applied; everything else is rejected outright.
| Input | Limit | Over the limit |
|---|---|---|
message:send | 15 per minute, 1 second apart, 1 to 5,000 characters, no HTML | Rejected |
typing:set | 30 calls per minute, counting only true | Rejected |
event:track name | 1 to 200 characters | Rejected |
event:track data | 20 keys, 1,000 characters per value, string / number / boolean only | Extra keys dropped, values truncated |
trigger:run / article:show ID | 1 to 200 characters, string only | Rejected |
user:email | Valid email, up to 254 characters | Rejected |
user:name | 1 to 150 characters, no HTML | Rejected |
user:phone | 1 to 50 characters, no HTML | Rejected |
user:context / shop:data / user:attributes | 50 keys, 2,000 characters per value, string / number / boolean only | Extra keys dropped, values truncated |
product / cart / order | Plain JSON, 8,000 characters each once serialized | Rejected whole |
conversation:attributes | 50 entries, name up to 255 characters, value up to 1,000 characters | Extra entries dropped, values truncated |
locale | 1 to 35 characters | Rejected |
prechat:field name / phone / message | 150 / 50 / 5,000 characters, no HTML | Rejected |
"No HTML" means the value is rejected if it contains anything that looks like a tag.
Error handling
Commands never throw. A command that fails validation or hits a rate limit is dropped, and the SDK logs a warning prefixed with [ChattySDK] to the browser console.
window.$chatty.push(['set', 'user:email', 'not-an-email'])
// [ChattySDK] ... the command is ignored, execution continuesThree consequences worth designing around:
- There is no error callback and no return value. Your code cannot detect a rejected command at runtime, so validate input on your side before pushing it, especially anything coming from a form or a URL parameter.
- A dropped command is silent to the visitor. If a rate limit swallows a
message:send, nothing appears in the chat. Throttle on your side rather than relying on the SDK to queue. - Not every no-op logs a warning. A command that passes validation but arrives before the part of the widget that handles it has rendered is simply not acted on, with nothing in the console. See When your commands run.
Security and trust boundary
The SDK runs entirely in the visitor's browser. Everything below follows from that.
Identity is not verified. identify and the set user:* actions take whatever the page gives them. Anyone can open the browser console and pass another person's email. Chatty uses these values to label the conversation for your team, so:
- Do not use SDK identity to unlock account data, order details, or anything else you would gate behind a login.
- Do not treat a matching email in your inbox as proof of identity.
A signed variant of identify, where your server vouches for the email, exists in the product but is not open to stores yet. Until it is, treat every identity the page supplies as unverified.
Input is sanitised. All text is sanitised before it reaches the widget. HTML and script content is rejected, and the fields marked "no HTML" above reject markup outright.
Only documented keys are exposed. is and get return the keys listed on this page. The SDK object does not expose the widget's internal state, and its methods are frozen so ordinary scripts on the page cannot replace them by accident. This is a guard against collisions with other scripts, not a defence against a hostile script. Any script on your storefront runs with the same privileges as your own code.
Anything you push is visible to the visitor. user:context, shop:data, and the commerce objects sit in page memory. Pass what your support team needs to help, and nothing more: no internal costs, margins, risk scores, or notes you would not show the customer.
What the SDK cannot do
- Read past conversations or message history. There is no getter for it.
- Send attachments, images, or rich content.
message:sendis text only. - Reply as an agent, or act on behalf of your team.
- Change widget settings, colours, or position. Those live in your dashboard.
- Embed the widget. The SDK controls a widget that Chatty already loaded; it is not an npm package.
For server-side access to conversations, see the Chat Conversations API.
Browser support
Chrome 80+, Firefox 72+, Safari 13.1+, Edge 80+. The SDK ships as modern JavaScript and does not include a transpiled fallback for older browsers.
Changelog
| Version | Changes |
|---|---|
| 1.1.0 | Added pre-chat form control: set prechat:field, do prechat:submit, do prechat:skip, and the prechat:visible / prechat:required states with get prechat:config. |
| 1.0.0 | Initial release. |
Check the version at runtime before calling a newer API:
window.$chatty.on('sdk:ready', () => {
const [major, minor] = window.$chatty.version.split('.').map(Number)
if (major > 1 || (major === 1 && minor >= 1)) {
window.$chatty.push(['set', 'prechat:field', 'email', '[email protected]'])
}
})Compare the parts as numbers, not the whole string. In a string comparison '1.9.0' >= '1.10.0' is true, which is wrong.
Troubleshooting
window.$chatty is undefined. The widget script has not loaded on that page. Check that the Chatty app embed is enabled in your theme and that the page is not excluded. Starting your code with window.$chatty = window.$chatty || [] makes it safe regardless of load order.
window.$chatty.on is not a function. The widget bundle has not loaded yet, so window.$chatty is still the plain array you created. Only push exists at that point. Use push(['on', event, cb]) instead.
chat:open does nothing. The widget may still be initialising, so wait for sdk:ready. It may also have been hidden earlier by chat:hide, in which case call chat:show first.
message:send seems ignored. Check the console for a [ChattySDK] rate-limit or validation warning. If a required pre-chat form is open, the message is held and sent once the form is submitted. If nothing at all appears in the console, the chat window had probably not rendered yet.
article:show or trigger:run does nothing. Check the console. [ChattySDK] article:show — expects an article id means you passed a number or an empty value. unknown article id means the widget has no article with that ID loaded.
A handler runs twice. You are probably subscribed to message:received and to ai:reply or human:reply at the same time. See Avoiding duplicate handling.
Need help?
If a command still is not working, confirm the widget is loading on the page. window.$chatty should be defined and window.$chatty.version should return '1.1.0'. Still stuck? Contact the Chatty support team from your dashboard, and include the [ChattySDK] console warnings you see. For server-side access, see the Chat Conversations API.
Chatty Help Center