SDK
SDK Reference
💬Get free consultation

SDK Reference

Who can use this feature?

  • Available to all users on any plan.
  • New to the SDK? Start with the SDK overview.

The SDK is the global object window.$chatty. This page lists everything it exposes.

Global objectwindow.$chatty
SDK version1.1.0 — read at runtime with $chatty.version
InstallShips with the Chatty widget. Nothing extra to add.

Methods

MethodReturnsDescription
push([verb, action, ...args])Run a command. Verbs: do, set, on, off.
on(event, callback)Subscribe to an event.
off(event, callback)Unsubscribe. Pass the same function reference used in on.
is(key)booleanSynchronously read a boolean state (see States).
get(key)valueSynchronously read a value (see Values).
versionstringSDK version, e.g. '1.1.0'.

on and off are also available as push verbs — push(['on', event, cb]) is equivalent to on(event, cb).

Commands pushed before the widget bundle loads are buffered and replayed in order once it is ready, so you never need to poll for readiness.

window.$chatty = window.$chatty || [];
window.$chatty.push(['do', 'chat:open'])

do actions

Run with push(['do', action, ...args]).

Widget visibility

ActionArgumentsDescription
chat:openOpen the chat window.
chat:closeClose the chat window, back to the launcher.
chat:toggleToggle open and closed.
chat:showShow the widget again after chat:hide.
chat:hideHide the whole widget — launcher and window.
launcher:showShow only the launcher button.
launcher:hideHide only the launcher button.
window.$chatty.push(['do', 'chat:open'])
window.$chatty.push(['do', 'launcher:hide'])

Messaging

ActionArgumentsDescription
message:send'text', contentSend a message as the visitor.
typing:setbooleanShow or clear the visitor's typing indicator.
window.$chatty.push(['do', 'message:send', 'text', 'I need help with my order'])
window.$chatty.push(['do', 'typing:set', true])

The first argument of message:send is the message type and must be 'text'. The SDK cannot send attachments or rich content.

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.

Identity and session

ActionArgumentsDescription
identifyobjectSet visitor identity in one call. Every field is optional, but pass at least one.
logoutClear the identity and start fresh. Use it when the shopper logs out of your store.
session:resetEnd the current conversation and start a new one. Keeps the identity.
window.$chatty.push(['do', 'identify', {
  email: 'jane@example.com',
  name: 'Jane Doe',
  phone: '+15550134',
  attributes: { plan: 'gold' }
}])

identify is a shortcut for the matching set user:* actions. Both merge into the same visitor record, so use whichever fits your code — identify for a single call at page load, set when values arrive at different times.

!

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

ActionArgumentsDescription
escalateAsk for a human agent — the AI to human handoff.
trigger:runcampaignIdRun a chat campaign by ID.
article:showarticleIdOpen an FAQ article inside the widget.
window.$chatty.push(['do', 'escalate'])
window.$chatty.push(['do', 'article:show', articleId])

Pre-chat form

Available from SDK 1.1.0.

ActionArgumentsDescription
prechat:submitSubmit the form. Required fields are validated first.
prechat:skipSkip 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'])
}

Prefill the fields with set prechat:field before calling prechat:submit.

Custom event tracking

ActionArgumentsDescription
event:trackname, data?Feed a named event into the widget context.
window.$chatty.push(['do', 'event:track', 'viewed_size_guide', { product: 'shirt-101' }])

Every tracked event is also re-dispatched on the page as a DOM CustomEvent named chatty:event, so other scripts can consume it without touching the SDK:

document.addEventListener('chatty:event', (e) => {
  console.log(e.detail.name, e.detail.data)  // 'viewed_size_guide', { product: 'shirt-101' }
})

set actions

Run with push(['set', action, value]).

Composer and visitor data

ActionValueDescription
message:textstringPrefill the input box without sending. The visitor can still edit it.
user:emailstringVisitor email. Validated.
user:namestringVisitor name.
user:phonestringVisitor phone.
user:attributesobjectStructured attributes about the visitor.
user:contextobjectFree-form context shown to your team and the AI. Merges across calls.
shop:dataobjectFree-form context about the shop or page. Merges across calls.
window.$chatty.push(['set', 'message:text', 'I want to know about shipping'])
window.$chatty.push(['set', 'user:email', 'jane@example.com'])
window.$chatty.push(['set', 'user:context', { vipLevel: 3, accountAge: '2 years' }])

user:context and shop:data merge rather than replace, so you can add keys as the page learns more. Values must be a string, number, or boolean.

Commerce context

Give the AI assistant structured context about what the visitor is looking at.

ActionValueDescription
productobjectThe product currently being viewed.
cartobjectThe current cart.
orderobjectThe 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' }])

Read the merged result back with $chatty.get('commerce:context').

Locale

ActionValueDescription
localestringLanguage hint for the conversation.
window.$chatty.push(['set', 'locale', 'de'])

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.

window.$chatty.push(['set', 'prechat:field', 'name', 'Jane Doe'])
window.$chatty.push(['set', 'prechat:field', 'email', 'jane@example.com'])
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?'])

Prefills are sticky — they apply even if the form appears later in the session.


Events

Subscribe with on(event, callback). The callback receives one data object, or nothing where the payload column shows .

Lifecycle

EventPayloadFires when
sdk:readyThe SDK has initialised. Subscribing after it fired runs your callback immediately.
chat:openedThe chat window opens, whether by the visitor or via the SDK.
chat:closedThe chat window closes.

Messages

EventPayloadFires when
message:sent{ text, type }The visitor sends a message.
message:received{ text, isAdmin, isAutomated }A reply arrives, from a human agent or the AI.
ai:replysame as message:receivedA reply from the AI assistant arrives.
human:replysame as message:receivedA reply from a human agent arrives.
message:updatedthe updated messageAn existing message is edited.
message:removed{ id }A message is removed.

Conversation

EventPayloadFires when
conversation:started{ conversationId }A conversation is created — that is, the visitor's first message was submitted.
conversation:resolved{ conversationId }The conversation is marked resolved.
conversation:removed{ conversationId }The conversation is deleted.
handoff{ conversationId }The conversation is handed from the AI to a human agent.

Pre-chat and lead capture

EventPayloadFires when
prechat:shownThe pre-chat form is displayed.
prechat:submitted{ conversationId }The visitor submits the pre-chat form.
email:captured{ email }The visitor gives their email in the pre-chat form.
survey:submitted{ rating, surveyComment }The visitor submits a satisfaction survey.

Presence and typing

EventPayloadFires when
agent:available{ online: true }Support comes online — chat hours start.
agent:unavailable{ online: false }Support goes offline.
agent:typing{ active }A human agent starts or stops typing.
ai:typing{ active }The AI assistant starts or stops typing.
unread:changed{ count }The visitor's unread count changes.

Avoiding duplicate handling

ai:reply and human:reply cover the same replies that message:received reports, split by sender. Subscribe to either message:received or the ai:reply / human:reply pair — not both — or a single reply runs your handler twice. This matters most for analytics, where it doubles your counts.

// Either this — one handler, branch on the payload
window.$chatty.on('message:received', ({ isAdmin }) => { /* ... */ })
 
// Or this — two handlers, no branching. Not both.
window.$chatty.on('ai:reply', () => { /* ... */ })
window.$chatty.on('human:reply', () => { /* ... */ })

Unsubscribing

Pass the same function reference you subscribed with. Anonymous inline functions cannot be removed.

function onOpen() { /* ... */ }
 
window.$chatty.on('chat:opened', onOpen)
window.$chatty.off('chat:opened', onOpen)

States

Read synchronously with is(key)boolean.

KeyTrue when
chat:openedThe chat window is open.
chat:closedThe chat window is closed.
chat:visibleThe widget is visible — it has not been hidden with chat:hide.
session:ongoingThere is an active conversation.
prechat:visibleThe pre-chat form is on screen.
prechat:requiredThe pre-chat form must be completed before chatting.
agent:onlineSupport is within chat hours.
if (window.$chatty.is('chat:opened')) {
  window.$chatty.push(['do', 'chat:close'])
}

Values

Read synchronously with get(key).

KeyReturns
message:textCurrent text in the composer.
session:identifierThe conversation ID, or null when no conversation is open.
chat:unread:countUnread messages for the visitor.
prechat:config{ mode, preChatFields, requiredName, requiredEmail, required }
commerce:contextThe merged product, cart, and order objects.
const unread = window.$chatty.get('chat:unread:count')

Limits and validation

The SDK enforces limits to keep the widget responsive and to prevent abuse.

InputLimit
message:send15 per minute, 1 second apart, ≤ 5,000 characters, no HTML
typing:set30 calls per minute
event:trackname ≤ 200 chars; data ≤ 20 keys, ≤ 1,000 chars per value
user:emailvalid email, ≤ 254 chars
user:name≤ 150 chars, no HTML
user:phone≤ 50 chars, no HTML
user:context / shop:data≤ 50 keys, ≤ 2,000 chars per value, string / number / boolean only
product / cart / orderplain JSON, ≤ 8 KB each
locale≤ 35 chars

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 continues

Two 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.

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.

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.


Analytics and GA4

Send Chatty activity to Google Analytics 4. Add this after your GA4 snippet, for example in theme.liquid.

<script>
  window.$chatty = window.$chatty || [];
 
  window.$chatty.push(['on', 'sdk:ready', function () {
    // Chat window opened
    window.$chatty.on('chat:opened', function () {
      gtag('event', 'chatty_chat_opened');
    });
 
    // First message submitted — a new conversation exists
    window.$chatty.on('conversation:started', function (data) {
      gtag('event', 'chatty_conversation_started', {
        conversation_id: data.conversationId
      });
    });
 
    // Every message the visitor sends
    window.$chatty.on('message:sent', function () {
      gtag('event', 'chatty_message_sent');
    });
 
    // Pre-chat form submitted
    window.$chatty.on('prechat:submitted', function (data) {
      gtag('event', 'chatty_lead_submitted', {
        conversation_id: data.conversationId
      });
    });
 
    // Email captured in the pre-chat form
    window.$chatty.on('email:captured', function () {
      gtag('event', 'chatty_email_captured');
    });
  }]);
</script>

Then mark one of them as a conversion in the GA4 admin.

!

Pick one conversion event. On a shop with a required pre-chat form, prechat:submitted and conversation:started both fire for the same visitor, so marking both counts the lead twice.

Migrating from another chat widget? The usual callbacks map like this:

Typical callbackChatty equivalent
onOpenon('chat:opened', …)
onCloseon('chat:closed', …)
onMessage outboundon('message:sent', …)
onMessage inboundon('message:received', …)
onLeadSubmittedon('prechat:submitted', …) or on('conversation:started', …)

Recipes

Custom "Chat with us" button

<button onclick="window.$chatty.push(['do', 'chat:open'])">Chat with us</button>

Open chat with a question already typed

<button onclick="
  window.$chatty.push(['do', 'chat:open']);
  window.$chatty.push(['set', 'message:text', 'What is your returns policy?']);
">Ask about returns</button>

One-click support with the message already sent

<button onclick="
  window.$chatty.push(['do', 'chat:open']);
  window.$chatty.push(['do', 'message:send', 'text', 'I need help with my recent order']);
">Get order help</button>

Hide the widget on specific pages

if (window.location.pathname.startsWith('/pages/legal')) {
  window.$chatty.push(['do', 'chat:hide'])
}

Identify logged-in customers (Shopify Liquid)

{% if customer %}
<script>
  window.$chatty = window.$chatty || [];
  window.$chatty.push(['do', 'identify', {
    email: {{ customer.email | json }},
    name: {{ customer.name | json }}
  }]);
</script>
{% endif %}

Hold the widget until the visitor accepts cookies

Useful where consent is required before loading third-party chat.

window.$chatty = window.$chatty || [];
window.$chatty.push(['do', 'chat:hide'])
 
// Call this from your consent banner's "accept" handler
function onConsentGiven() {
  window.$chatty.push(['do', 'chat:show'])
}

What the SDK cannot do

  • Read past conversations or message history — there is no getter for it.
  • Send attachments, images, or rich content. message:send is text only.
  • Reply as an agent, or act on behalf of your team.
  • 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 60+, Firefox 55+, Safari 12+, Edge 79+. The SDK uses ES6 and does not ship a transpiled fallback for older browsers.


Changelog

VersionChanges
1.1.0Added 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.0Initial 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', 'jane@example.com'])
  }
})

Compare the parts as numbers, not the whole string — '1.9.0' >= '1.10.0' is true in a string comparison and 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.

chat:open does nothing. The widget may still be initialising — wait for sdk:ready — or it was 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.

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.

Events are not reaching GA4. Register listeners inside or after sdk:ready, and confirm the event fires at all from the console first:

window.$chatty.on('chat:opened', () => console.log('opened'))

Need help?

If a command still is not working, confirm the widget is loading on the page — window.$chatty should be defined. Still stuck? Contact the Chatty support team from your dashboard.