Web quickstart

Embed Kustomer Chat on a website with widget.js, start it with Kustomer.start, control it from your code, and listen for widget events. Includes every start option and widget method.

The web SDK ships as a script you load from Kustomer's CDN. It comes in two builds:

BuildScriptGlobalUse it to
Widgetwidget.jsKustomerDrop in the full chat and knowledge base UI, and control it from your code. Most sites want this.
Corecore.jsKustomerCoreBuild your own chat UI on the same API the widget uses. See Web Core API.

This page covers the widget.

Embed the widget

  1. Get a chat key

    In Kustomer, go to Settings › Security › API Keys and create a key with only the org.tracking role. Chat keys are safe to put in a web page because that role can do nothing but chat as an anonymous or logged-in customer. See API keys and roles.

  2. Add the script

    Put this before </body> on every page that should have chat:

    index.html
    <script
      src="https://cdn.kustomerapp.com/chat-web/widget.js"
      data-kustomer-api-key="YOUR_CHAT_KEY"
    ></script>
    <script>
      window.addEventListener('kustomerLoaded', function () {
        Kustomer.start({ brandId: 'YOUR_BRAND_ID' })
      })
    </script>

    The loader reads the key from data-kustomer-api-key, defines window.Kustomer with only start, requests the rest of the SDK, and fires kustomerLoaded on window straight away. The start call is queued until the SDK arrives. Call every other method from the start callback or later.

  3. Pick a brand

    brandId chooses which chat settings to use: colors, greeting, business hours and assistant. List your brands and their IDs with GET /v1/brands. Leave it out to use the default brand.

  4. Test it

    Load the page, open chat and send a message. It appears in Kustomer as a new chat conversation on an anonymous customer. Reply from Kustomer and watch it arrive in the widget. To try your key and brand before touching your site, use the playground below.

The widget runs in its own iframe, so your page's CSS and scripts can't break it, and it can't break them.

Single-page apps

Load the script once for the lifetime of the app, not on every route change, and keep Kustomer.start out of render paths:

ChatProvider.tsx
import { useEffect } from 'react'

declare global {
  interface Window { Kustomer?: any }
}

export function ChatProvider({ brandId }: { brandId: string }) {
  useEffect(() => {
    if (document.querySelector('[data-kustomer-api-key]')) return
    const onLoaded = () => window.Kustomer.start({ brandId })
    window.addEventListener('kustomerLoaded', onLoaded, { once: true })

    const script = document.createElement('script')
    script.src = 'https://cdn.kustomerapp.com/chat-web/widget.js'
    script.dataset.kustomerApiKey = import.meta.env.VITE_KUSTOMER_CHAT_KEY
    document.body.appendChild(script)
  }, [brandId])

  return null
}

To tear chat down, for example when a user switches accounts, call Kustomer.stop(). It removes the widget iframe, and a later Kustomer.start() builds it again.

Shopify

On Shopify storefronts the loader picks up window.Shopify.shop automatically. Elsewhere, pass the store domain with data-kustomer-shop="acme.myshopify.com" on the script tag.

Kustomer knowledge base

To show chat on a knowledge base hosted by Kustomer, add the loader to a custom theme. The default theme doesn't accept new files, so duplicate it first.

  1. Add a global script

    In Kustomer, open Settings › Knowledge Base › Themes, choose your custom theme, and in Theme Files select Add. Create a file with Theme Target Global, File Name chat and File Type JS.

  2. Load the widget from it

    Paste the loader into chat.js, save, and apply the theme:

    chat.js
    window.addEventListener('DOMContentLoaded', function () {
      var script = document.createElement('script')
      script.src = 'https://cdn.kustomerapp.com/chat-web/widget.js'
      script.setAttribute('data-kustomer-api-key', 'YOUR_CHAT_KEY')
      document.body.appendChild(script)
    })
    
    window.addEventListener('kustomerLoaded', function () {
      Kustomer.start({ brandId: 'YOUR_BRAND_ID' })
    })

Use the brand that owns the knowledge base, so chat and articles share the same branding.

Playground

Run the real widget here with your own chat key. It loads into an example storefront, so you can check the key, brand, assistant and start options, call any widget method, and watch the events your code would receive. The Embed code tab gives you the snippet for the options you picked.

InteractiveWeb chat playground
Not running
›Start options
acme-outfitters.example/orders/4417

Paste a chat key and launch

Create one in Kustomer under Settings › Security › API Keys with only the org.tracking role. The real widget then loads into an example storefront here.

  • Kustomer.open()
  • Kustomer.close()
  • Kustomer.createConversation({ message: 'Hi! Testing chat from the developer docs.' })
  • Kustomer.createConversationInDraft({ message: 'I have a question about my order' })
  • Kustomer.getOpenConversations()
  • Kustomer.openConversationById({ conversationId: '' })
  • Kustomer.isChatAvailable()
  • Kustomer.getUnreadCount()
  • Kustomer.openKbArticle('')
  • Kustomer.login({ jwtToken: '' })How to sign
  • Kustomer.logout()
  • Kustomer.stop()
  • Kustomer.start()
Runs the real widget with your chat key. The key stays in this tab's memory and is only passed to the playground page on this site. Messages you send create real conversations in your org, on an anonymous customer unless you log in.

The playground refuses keys with any role besides org.tracking, since only chat keys are safe in a web page. If your brand uses page rules, add one that matches this site, or the launcher stays hidden here. See Customization and security.

Start options

Kustomer.start(options, callback) builds the widget. The callback runs once it's ready, and methods that need a started widget, such as getOpenConversations, must run after it.

Every common option
Kustomer.start(
  {
    brandId: 'YOUR_BRAND_ID',
    assistantId: 'YOUR_ASSISTANT_ID',
    scheduleId: 'YOUR_SCHEDULE_ID',
    lang: 'fr',
    preferredView: 'chat',
    chatIconPosition: { alignment: 'left', horizontalPadding: 24, verticalPadding: 24 },
    chatIconSize: 56,
    zIndex: 1000,
    hideChatIcon: false,
    hideHistory: false,
    showBrowserNotifications: true,
    showTitleNotifications: true,
  },
  function () {
    console.log('Chat is ready')
  },
)
Options
brandIdstring
assistantIdstring
scheduleIdstring
langstring
preferredView'chat' | 'kb'
chatIconPosition'right' | 'left' | object
chatIconSizenumber | string | object
zIndexnumber
hideChatIconboolean
hideHistoryboolean
hideNewConversationButtonOnHistoryboolean
hideNewConversationButtonOnEndboolean
hideArticleOpenInNewTabboolean
showBrowserNotificationsboolean
showTitleNotificationsboolean
allowZoomboolean
enableUrlUnfurlingboolean
enableHistoryStatusFilterboolean

Most of the widget's look is configured in Kustomer, not in code: colors, greeting, team name and icon, business hours, offline behavior and which sites may embed it. See Customization and security.

Control the widget

Call these from the start callback or later. Before the SDK finishes loading they don't exist yet, and before start builds the widget most of them silently do nothing. Callbacks follow the (response, error) convention.

MethodWhat it does
start(options, cb)Build the widget. See Start options. Ignored while the widget is already on the page, and cb doesn't run.
stop(cb)Remove the widget from the page. Listeners stay registered, and a later start builds the widget again.
open(cb) / close(cb)Open or close the chat window.
createConversation(params, cb)Open chat with a new conversation. See below.
createConversationInDraft({ message }, cb)Open a new conversation with message typed into the composer but not sent.
openConversationById({ conversationId }, cb)Open an existing conversation.
getOpenConversations(cb)The customer's conversations that haven't ended. Only after start finishes.
openKbArticle(articleId, cb)Open a knowledge base article in the widget.
openKbCategory(categoryId, cb)Open a knowledge base category.
isChatAvailable()Returns { availability }: online, offline, hidden (off hours, set to hide chat) or disabled.
getUnreadCount()Returns { total, conversations }, where conversations maps conversation IDs to counts.
login({ jwtToken }, cb)Identify the visitor as one of your users. See Customer identity.
isLoggedIn({ email } | { externalId }, cb)Whether that user is logged in. cb(true | false).
logout(cb)End the session, clear the visitor's chat data from the browser, and reset the widget.
describeCustomer(params, cb)Set attributes on the current customer. See Customer identity.
describeConversation({ conversationId, customAttributes }, cb)Set custom attributes on a conversation.
setHistoryStatusFilter(filter)Change the history screen's status filter.
addListener(event, cb) / removeListener(event, cb)Subscribe to widget events.

Start a conversation from your page

Open chat with a new conversation, for example from a Help with this order button:

Order page
document.querySelector('#order-help').addEventListener('click', function () {
  Kustomer.createConversation(
    {
      message: 'I need help with order #4417',
      customAttributes: { orderNumberStr: '4417' },
    },
    function (conversation, error) {
      if (error) return console.error(error)
      console.log('Created', conversation.conversationId)
    },
  )
})
createConversation parameters
messagestring
customAttributesobject
assistantIdstring
dialogIdstring
payloadstring

The callback receives the conversation: conversationId, createdAt, ended and, when it starts with an assistant, isInAssistantMode.

Events

JavaScript
function onUnread({ total, change }) {
  document.querySelector('#chat-badge').textContent = total || ''
  console.log(`${change.count} unread in ${change.conversationId}`)
}

Kustomer.addListener('onUnread', onUnread)
// later
Kustomer.removeListener('onUnread', onUnread)
EventFires when
onOpenThe chat window opens
onCloseThe chat window closes
onUnreadThe unread count changes. Receives { total, change: { conversationId, count } }.
onConversationCreateThe customer starts a conversation
onSessionOpenedA conversation is opened in the widget. Receives { conversation }.
onSessionCustomAttributesUpdatedA conversation's custom attributes change
onLoginlogin succeeds
onLogoutlogout finishes
onUrlPreviewReceivedA link preview loads for a message

Unknown event names are rejected with a console error. The Core API has a longer event list, including every message received; see Web Core API › Events.

Troubleshooting

SymptomFix
Console: Kustomer API Key … appears to be invalid.The data-kustomer-api-key value isn't a chat key. Copy the whole key, with no quotes or line breaks.
Kustomer is not definedYou called the SDK before kustomerLoaded. Wrap calls in the event listener.
Kustomer.open is not a function, or a method does nothingThe SDK or the widget isn't ready yet. Call it from the start callback or later.
start callback never runsThe widget was already started. Call stop() before starting it again.
getOpenConversations can only be run after Kustomer.start() completesCall it inside the start callback.
Launcher never appearsCheck chat is enabled for the brand, the page URL matches the brand's page rules, and isChatAvailable() isn't hidden or disabled.
Blocked by Content Security PolicyAdd the CDN to your policy, or pass a nonce. See Customization and security › Content Security Policy.
Widget sits under your header or cookie bannerRaise zIndex, or lower your banner's.