> For the complete documentation index, see [llms.txt](https://docs.usepylon.com/pylon-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.usepylon.com/pylon-docs/chat-widget/chat-setup.md).

# Chat Setup

How to setup Pylon's Chat Widget

## Installation <a href="#installation" id="installation"></a>

{% hint style="info" %}
Installing the Chat Widget will involve adding code to your Frontend. It may be necessary to involve an engineer to get this installed if you don’t have access to the codebase.
{% endhint %}

Chat widget settings live in **Settings > Communications > Chat widget** ([US](https://app.usepylon.com/settings/chat-widget), [EU](https://app.eu.usepylon.com/settings/chat-widget)). Click a widget to open its settings, which are split into **Modules**, **Settings**, and **Styling** tabs.

1. **Getting Started**<br>

   Grab your `APP_ID` from the **App ID** column on the Chat Widgets page, or from **Set up > App ID** on the widget's **Settings** tab.
2. **Add Script to Body**\
   \
   Add the following `<script>` to right before the `</body>` tag in your `index.html`.\
   \
   Make sure to replace the `APP_ID` with the `APP_ID` you grabbed from Step 1.<br>

   ```html
   <script>
   (function(){var e=window;var t=document;var n=function(){n.e(arguments)};n.q=[];n.e=function(e){n.q.push(e)};e.Pylon=n;var r=function(){var e=t.createElement("script");e.setAttribute("type","text/javascript");e.setAttribute("async","true");e.setAttribute("src","https://widget.usepylon.com/widget/APP_ID");var n=t.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};if(t.readyState==="complete"){r()}else if(e.addEventListener){e.addEventListener("load",r,false)}})();
   </script>
   ```

   \
   If you are using EU data hosting, then use the following `<script>` instead:<br>

   ```html
   <script>
   (function(){var e=window;var t=document;var n=function(){n.e(arguments)};n.q=[];n.e=function(e){n.q.push(e)};e.Pylon=n;var r=function(){var e=t.createElement("script");e.setAttribute("type","text/javascript");e.setAttribute("async","true");e.setAttribute("src","https://widget.eu.usepylon.com/widget/APP_ID");var n=t.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};if(t.readyState==="complete"){r()}else if(e.addEventListener){e.addEventListener("load",r,false)}})();
   </script>
   ```

   \
   If you serve the widget from your own domain, use the snippet from the Custom Domain page instead. See [Custom Domain](/pylon-docs/chat-widget/custom-domain.md).

\ <br>

3. **Content Security Policy**<br>

   If you have a CSP, you will need to set up the following policy:

   ```
   Content-Security-Policy: 
       connect-src 'self' https://*.usepylon.com wss://*.pusher.com https://*.pusher.com;
       script-src 'self' https://widget.usepylon.com;
       font-src 'self' https://*.usepylon.com;
       style-src 'self' 'unsafe-inline' https://*.usepylon.com;
       img-src 'self' https://*.usepylon.com https://pylon-avatars.s3.us-west-1.amazonaws.com;
   ```

   \
   If you are using EU data hosting, then use the following policy:<br>

   ```
   Content-Security-Policy: 
       connect-src 'self' https://*.usepylon.com wss://*.pusher.com https://*.pusher.com;
       script-src 'self' https://widget.eu.usepylon.com;
       font-src 'self' https://*.usepylon.com;
       style-src 'self' 'unsafe-inline' https://*.usepylon.com;
       img-src 'self' https://*.usepylon.com https://pylon-eucentral1-avatars.s3.eu-central-1.amazonaws.com;
   ```

   \
   `https://*.pusher.com` covers the fallback the widget uses for real-time updates when a WebSocket connection isn't available.

   If you use a custom domain, add it to the policy as well. See [Custom Domain](/pylon-docs/chat-widget/custom-domain.md#content-security-policy).

   If your organization uses SendSafely for secure attachments in the widget, also allow `https://*.sendsafely.com` in `script-src` and `connect-src`.
4. **Enable the Chat Widget**<br>

   Anywhere in your React code you can access the `window` object and set the `pylon` config.<br>

   You should add this code wherever the user has already logged in and details like the User’s email, name, etc. become available.

   ```js
   window.pylon = {
     chat_settings: {
       app_id: "APP_ID",
       email: "USER_EMAIL",
       name: "USER_FULL_NAME",

       // (Optional) If you have the user's profile image available through something like Auth0
       avatar_url: "USER_PROFILE_IMAGE_URL",

       // (Optional) If you are using Pylon's identity verification
       email_hash: "USER_EMAIL_HASH",
       
       // (Optional) If you want to force association of an issue/contact with a certain account.
       account_id: "057a3564-fc1b-4224-9292-dcf718653452",
       
       // (Optional) If you want to force association of an issue/contact with a certain account.
       // Learn more about external IDs here: https://docs.usepylon.com/pylon-docs/developer/api/external-ids.
       account_external_id: "ab38e36b-3e28-4e2d-b976-6282735b77eb",

       // (Optional) Force the widget's language, e.g. "fr".
       locale: "USER_LOCALE"
     },
   };
   ```

   \\

   <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p><code>app_id</code>, <code>email</code>, and <code>name</code> are required. The widget won't load until all three are set, so the widget can't be used for anonymous chat.</p></div>

   | Field                                | Description                                                                                                                                                                                                                                                    |
   | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | `app_id`                             | **Required.** Your Chat Widget App ID.                                                                                                                                                                                                                         |
   | `email`                              | **Required.** The logged-in user's email.                                                                                                                                                                                                                      |
   | `name`                               | **Required.** The logged-in user's name.                                                                                                                                                                                                                       |
   | `avatar_url`                         | The user's profile image URL.                                                                                                                                                                                                                                  |
   | `email_hash`                         | HMAC hash of the email, for [identity verification](/pylon-docs/chat-widget/identity-verification.md#hmac-email-hash).                                                                                                                                         |
   | `jwt`                                | Signed token, for [JWT identity verification](/pylon-docs/chat-widget/identity-verification.md#json-web-token-jwt).                                                                                                                                            |
   | `account_id` / `account_external_id` | Associate the user's conversations with a specific account. Pylon never creates an account from these values. See [How Pylon matches widget users to contacts and accounts](#how-pylon-matches-widget-users-to-contacts-and-accounts).                         |
   | `contact_id` / `contact_external_id` | Pin the session to an existing contact, for apps where an email doesn't uniquely identify a user. Only honored with JWT identity verification, and the value must match the matching claim in the signed JWT.                                                  |
   | `locale`                             | Force the widget's language. Supported values: `en`, `es`, `et`, `fr`, `de`, `it`, `ja`, `ko`, `pt-br`. When set, it takes priority over the language the user picks in the widget, their saved language preference, and your organization's default language. |

   \
   Example usage in a React component:

   ```js
   useEffect(() => {
     if (user.loggedIn) {
       window.pylon = {
         chat_settings: {
           app_id: APP_ID,
           email: user.email,
           name: user.fullName,
           avatar_url: user.profileImgUrl,
         },
       };
     }
   }, [user]);
   ```
5. **Setup Chat Support Channel**\
   \
   Create a Slack channel in your workspace where you can respond to chats, like `#chat-support`.<br>

   Note: If you make this channel private, add the Pylon bot to the channel.<br>

   On the widget's **Settings** tab, set `#chat-support` as the **Chat Support Channel** under **Channel settings**.
6. **Chat Widget Color**<br>

   Set the colors of your chat widget on the widget's **Styling** tab. See [Styling](/pylon-docs/chat-widget/styling.md).
7. **Identity Verification**<br>

   This is optional, but **strongly recommended** before you're ready for live production chats.<br>

   [Here’s how to set it up](/pylon-docs/chat-widget/identity-verification.md).
8. **Turn it On**<br>

   Click **Enable** in the top right of the widget's settings page. If identity verification isn't set up, Pylon warns you that users could impersonate each other and won't see their past conversations. Click **Enable Anyway** to continue, or **Cancel** to set up identity verification first.
9. **All Done!**

## How Pylon matches widget users to contacts and accounts <a href="#how-pylon-matches-widget-users-to-contacts-and-accounts" id="how-pylon-matches-widget-users-to-contacts-and-accounts"></a>

Pylon identifies a widget user by `email`. Their contact and account are set when they send their first message or submit a ticket form.

### Contact

* If a contact with that email already exists, Pylon uses it.
* If you pass `account_id` or `account_external_id` and it matches an account, Pylon uses the contact with that email in that account. If that account has no such contact, Pylon creates one there. Existing contacts in other accounts aren't moved.
* If several contacts share the email and you don't pass an account, Pylon prefers the contact on a [partner account](/pylon-docs/account-intelligence/accounts/partner-accounts.md), then the one on the account that owns the email's domain, then the newest.
* If no contact has that email, Pylon creates one.

### Account

A new contact is added to the first account that matches:

1. `account_id`: the account's Pylon ID.
2. `account_external_id`: an [external ID](/pylon-docs/developer/api/external-ids.md) set on the account.
3. The account whose domains include the email's domain.
4. A new account that Pylon creates from the email's domain.

Pylon doesn't create an account for personal email domains like gmail.com, so the contact is created without one. To stop Pylon from creating accounts for unknown domains, turn on **Suppress automatic account creation** in **Settings > Customers > Account configuration**. Contacts are then created without an account, and you can assign them manually.

Each conversation is filed under `account_id`, then `account_external_id`, then the contact's account, then the account that owns the email's domain.

{% hint style="warning" %}
Pylon never creates an account from `account_id` or `account_external_id`. If the value doesn't match an existing account, Pylon ignores it and falls back to the contact's account or the email's domain, which can auto-create an account. While the value doesn't match, the widget only lists past conversations that have no account. Create the account and set its external ID before you pass it to the widget.
{% endhint %}

### Which conversations a user sees

* The widget lists conversations where the user's contact is the requester and that started in this widget, including ticket forms submitted in it. Conversations from email, Slack, and other channels aren't listed.
* Conversations aren't shared across contacts. Two users at the same account each see only their own.
* When you pass `account_id` or `account_external_id`, the widget only lists conversations on that account or with no account.
* Users only see past conversations when [identity verification](/pylon-docs/chat-widget/identity-verification.md) is set up.

## Single-page apps: change the user after the widget loads <a href="#single-page-apps" id="single-page-apps"></a>

You don't need to reload the page. The widget checks `window.pylon.chat_settings` about once a second, so update the object when the signed-in user or their account changes:

```js
window.pylon.chat_settings.account_external_id = "NEW_ACCOUNT_EXTERNAL_ID";
```

* When `app_id`, `email`, `account_id`, `account_external_id`, `contact_id`, or `contact_external_id` changes, the widget closes any open conversation, returns to its home screen, and loads conversations for the new identity.
* If you use identity verification, update `email_hash` whenever `email` changes. With JWT, set a new `jwt` whose claims match the new values at the same time. Requests are rejected while the values and the token don't match.
* Changing `name` updates the contact's name the next time the user sends a message, unless a teammate has edited the contact's name in Pylon.

When a user logs out, remove their settings. Within about a second, the widget and its chat bubble disappear and the widget clears the previous user's conversations:

```js
delete window.pylon.chat_settings;
```

Set `chat_settings` again when the next user logs in.

## When the chat bubble appears <a href="#when-the-chat-bubble-appears" id="when-the-chat-bubble-appears"></a>

The chat bubble only renders when all of these are true:

* `app_id`, `email`, and `name` are set. Without them nothing renders, including the bubble.
* The widget is enabled.
* Identity verification passes. If the `email_hash` or `jwt` is missing or invalid while identity verification is on, the widget doesn't load and no bubble appears.
* **Show Chat Bubble** is on in the widget's **Settings** tab. When it's off, open the chat with [`Pylon("show")`](/pylon-docs/chat-widget/javascript-api.md#window-pylon-show), or turn on **Show Chat Bubble With Unread Messages** to show the bubble only when there are unread replies.
* Your page hasn't called [`Pylon("hideChatBubble")`](/pylon-docs/chat-widget/javascript-api.md#window-pylon-hidechatbubble).

The bubble still renders when `account_id` or `account_external_id` doesn't match an account.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.usepylon.com/pylon-docs/chat-widget/chat-setup.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
