# Customization and security

> What to configure in Kustomer versus in code, custom launchers, language and right-to-left support, domain restrictions, Content Security Policy with nonces, and privacy considerations for web chat.

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

Web chat is configured in two places. Admins set the brand's look and behavior in Kustomer, and every SDK (web, iOS, Android) picks it up. Your code controls where and how the widget sits on your page.

## Configured in Kustomer

These come from the brand's chat settings and apply to every platform at once, with no deploy:

| Setting                              | Effect                                                                                 |
| ------------------------------------ | -------------------------------------------------------------------------------------- |
| Colors, team name and icon           | The widget's theme and header                                                          |
| Greeting and wait message            | Text before and after the customer's first message                                     |
| Business hours and off-hours display | Whether chat shows as online, offline (leave a message), or is hidden outside hours    |
| Assistant                            | The chatbot that answers first                                                         |
| Knowledge base                       | Whether the widget shows chat, the knowledge base, or both                             |
| Conversation history                 | Whether customers see past conversations, and whether they can have more than one open |
| Attachments                          | Whether customers can send files                                                       |
| Ending conversations                 | Whether customers can end a conversation themselves                                    |
| Page rules                           | Which page URLs show chat                                                              |

The Core API returns all of these from [`init`](https://developer.kustomer.com/docs/chat/web-core-api#init), so a custom UI can honor them.

## Configured in code

| Need                                     | Option or method                                                                   |
| ---------------------------------------- | ---------------------------------------------------------------------------------- |
| Move the launcher                        | `chatIconPosition: 'left'`, or `{ alignment, horizontalPadding, verticalPadding }` |
| Resize the launcher                      | `chatIconSize: 64`                                                                 |
| Your own launcher                        | `hideChatIcon: true` plus `Kustomer.open()`                                        |
| Open on the knowledge base               | `preferredView: 'kb'`                                                              |
| Force a language                         | `lang: 'de'`                                                                       |
| Fix stacking                             | `zIndex`                                                                           |
| Different brand per section of your site | `brandId`                                                                          |

See [Start options](https://developer.kustomer.com/docs/chat/web#start-options) for the full list.

### Use your own launcher

Hide Kustomer's button and drive the widget from yours, with an unread badge:

```html title="Custom launcher"
<button id="help" type="button">Help <span id="help-badge" hidden></span></button>

<script>
  window.addEventListener('kustomerLoaded', function () {
    Kustomer.start({ brandId: 'YOUR_BRAND_ID', hideChatIcon: true }, function () {
      var badge = document.getElementById('help-badge')
      Kustomer.addListener('onUnread', function (res) {
        badge.hidden = !res.total
        badge.textContent = res.total
      })
      document.getElementById('help').addEventListener('click', function () {
        Kustomer.open()
      })
    })
  })
</script>
```

If chat can be `hidden` outside business hours, hide your button too: check `Kustomer.isChatAvailable().availability` when the page loads.

## Language

The widget follows the visitor's browser language unless you set `lang`. It ships translations for more than 50 languages, including `en_us`, `es`, `fr`, `de`, `it`, `pt`, `pt_br`, `nl`, `sv`, `da`, `no`, `fi`, `pl`, `cs`, `tr`, `ru`, `uk`, `ja`, `ko`, `zh_cn`, `zh_tw`, `th`, `vi`, `id`, `hi`, `ar` and `he`.

- Codes are matched case-insensitively, and `pt-BR` is treated as `pt_br`.
- A regional code without its own translation falls back to the base language, so `fr_ca` uses `fr`.
- `ar` and `he` switch the widget to right-to-left layout.

Text you control in Kustomer (greeting, assistant dialogs, knowledge base articles) is translated separately in Kustomer.

## Restrict where chat runs

Admins can limit chat to pages whose URL matches rules in the brand's chat settings. The widget hides itself on pages that don't match, and checks again whenever the URL changes, so it works with client-side routing: you can load the script on every page and let the rules decide.

Headless UIs apply the same rules with `KustomerCore.isUrlAllowed({ url: location.href })`, which returns `{ allowed }`.

These rules control where chat is shown. They aren't an access control: the chat key itself still works anywhere, which is safe because of what the `org.tracking` role allows.

## Content Security Policy

The widget runs in iframes created from your page, so your page's Content Security Policy applies to it.

### Use a nonce

If your policy uses nonces, put the nonce on the script tag:

```html
<script
  src="https://cdn.kustomerapp.com/chat-web/widget.js"
  data-kustomer-api-key="YOUR_CHAT_KEY"
  nonce="r4nd0m-per-request"
></script>
```

The SDK reads the nonce from the tag with `data-kustomer-api-key` and adds it to the scripts it loads, but only to inline scripts and scripts from Kustomer's CDN (`https://cdn.kustomerapp.com`). Scripts from any other origin never get your nonce. The same works for `core.js`.

### Directives

Chat needs to load scripts from the CDN, call Kustomer's API, hold a realtime connection, and show images and attachments. Start from this policy, which is known to work, in `Content-Security-Policy-Report-Only` mode, then tighten `connect-src`, `img-src` and `media-src` using the violations your browser reports:

```text
script-src 'nonce-r4nd0m-per-request';
style-src * 'unsafe-inline';
img-src * data: blob: https:;
media-src *;
connect-src *;
object-src 'none';
```

`style-src 'unsafe-inline'` is required: the widget sets inline styles for positioning and theming.

## Privacy

- **Chat keys are public.** Anyone can read them from your page. That's expected: the `org.tracking` role can only act as the chat customer on that browser. Never put any other kind of API key in a web page.
- **Anonymous by default.** Until you call [`login`](https://developer.kustomer.com/docs/chat/identity), Kustomer only knows the browser. Don't use `describeCustomer` to attach an email or phone you haven't verified; use `login` with a signed token instead.
- **Log out on shared devices.** Call `Kustomer.logout()` when your user signs out, so the next person on the device doesn't see their conversations.
- **Browser storage.** The SDK stores the session in the browser so conversations survive reloads. `logout()` clears it.
