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

Source: https://developer.kustomer.com/docs/chat/web

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

| Build      | Script      | Global         | Use it to                                                                                                                          |
| ---------- | ----------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Widget** | `widget.js` | `Kustomer`     | Drop in the full chat and knowledge base UI, and control it from your code. Most sites want this.                                  |
| **Core**   | `core.js`   | `KustomerCore` | Build your own chat UI on the same API the widget uses. See [Web Core API](https://developer.kustomer.com/docs/chat/web-core-api). |

This page covers the widget.

## Embed the widget

#### 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](https://developer.kustomer.com/docs/get-started/api-keys-and-roles).

#### Add the script

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

```html title="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.

#### 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`](https://developer.kustomer.com/api/core-resources/get-brands). Leave it out to use the default brand.

#### 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](#playground) below.

_Figure: The Kustomer chat widget open in the bottom-right corner of a website, showing a conversation with an agent. 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:

```tsx title="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.

#### 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`.

#### Load the widget from it

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

```js title="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.

_Interactive playground: paste a chat key and brand ID to load the real Kustomer chat widget into an example storefront, run widget methods such as open, createConversation and getOpenConversations, watch widget events in a live log, and copy the embed code for the chosen start options._

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](https://developer.kustomer.com/docs/chat/web-customization).

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

```js title="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**

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](https://developer.kustomer.com/docs/chat/web-customization).

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

| Method                                                           | What it does                                                                                                                              |
| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `start(options, cb)`                                             | Build the widget. See [Start options](#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](https://developer.kustomer.com/docs/chat/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](https://developer.kustomer.com/docs/chat/identity#describe-the-customer). |
| `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](#events).                                                                                                    |

### Start a conversation from your page

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

```js title="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**

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

## Events

```js
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)
```

| Event                              | Fires when                                                                         |
| ---------------------------------- | ---------------------------------------------------------------------------------- |
| `onOpen`                           | The chat window opens                                                              |
| `onClose`                          | The chat window closes                                                             |
| `onUnread`                         | The unread count changes. Receives `{ total, change: { conversationId, count } }`. |
| `onConversationCreate`             | The customer starts a conversation                                                 |
| `onSessionOpened`                  | A conversation is opened in the widget. Receives `{ conversation }`.               |
| `onSessionCustomAttributesUpdated` | A conversation's custom attributes change                                          |
| `onLogin`                          | `login` succeeds                                                                   |
| `onLogout`                         | `logout` finishes                                                                  |
| `onUrlPreviewReceived`             | A 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](https://developer.kustomer.com/docs/chat/web-core-api#events).

## Troubleshooting

| Symptom                                                                 | Fix                                                                                                                                                                                            |
| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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 defined`                                               | You called the SDK before `kustomerLoaded`. Wrap calls in the event listener.                                                                                                                  |
| `Kustomer.open is not a function`, or a method does nothing             | The SDK or the widget isn't ready yet. Call it from the `start` callback or later.                                                                                                             |
| `start` callback never runs                                             | The widget was already started. Call `stop()` before starting it again.                                                                                                                        |
| `getOpenConversations can only be run after Kustomer.start() completes` | Call it inside the `start` callback.                                                                                                                                                           |
| Launcher never appears                                                  | Check 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 Policy                                      | Add the CDN to your policy, or pass a `nonce`. See [Customization and security › Content Security Policy](https://developer.kustomer.com/docs/chat/web-customization#content-security-policy). |
| Widget sits under your header or cookie banner                          | Raise `zIndex`, or lower your banner's.                                                                                                                                                        |

> **For agents adding chat to a site:** Add the two script tags once, in the root layout. Read the chat key from the environment, not from source. Then confirm in the browser console that `typeof Kustomer.open === 'function'` once the `start` callback has run.
