> ## Documentation Index
> Fetch the complete documentation index at: https://docs.impellabs.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat widget

> Put an ImpelLabs agent on your website with one script tag or the npm package, and read the public endpoints it calls.

The chat widget puts one of your agents on your website. Visitors talk to the agent in a panel on the page. The
conversation reaches your workspace like any other channel.

You create the widget in the app, under Channels, as a website chat. It gets a public key that starts with `pub_`. The
key is in your page source, so it is not a secret. The agent on the channel answers, and it must be published.

## Add it with a script tag

Paste this before `</body>` on every page. The app shows the same snippet with your key filled in.

```html theme={null}
<script src="https://cdn.impellabs.tech/sdk/VERSION/impel-chat.js"
  data-impel-key="pub_..."
  data-api-base-url="https://api.impellabs.tech/api/v1"
  data-brand-color="#1f51ff"
  defer></script>
```

* Replace `VERSION` with a version from `https://cdn.impellabs.tech/sdk/versions.json`. The `current` field names the
  latest one and `supported` lists the others. A pinned path never changes under your site.
* Without `data-api-base-url` the widget runs its built-in demo and does not reach your agent.
* Any option below can be a `data-` attribute in kebab case, for example `data-start-open="true"`. Set
  `data-auto="false"` to stop the widget from starting itself, then call `ImpelChat.init` when you are ready.

## Add it with npm

The package is `@impellabs/chat-widget`. Pin an exact version, because options can change between releases.

```bash theme={null}
npm install @impellabs/chat-widget@VERSION
```

```js theme={null}
import { ImpelChat } from "@impellabs/chat-widget";

ImpelChat.init({
  key: "pub_...",
  apiBaseUrl: "https://api.impellabs.tech/api/v1",
});
```

Importing the package does not start a widget. Only `init` does.

## Allowed sites

When you create the widget you can list the sites that may use it, for example `https://shop.example`. Write each one
as a scheme and host. With no list, the widget works on any site. With a list, a request from another site gets a
`403` with a reason. The widget then greys its launcher, shows the message, and fires the `blocked` event.

## Options

Set options in `init`, as `data-` attributes, or from the widget settings in the app. A value from the app wins over an
attribute in an old snippet. A value you pass to `init` wins over the app.

| Option | Default | Use |
| - | - | - |
| `key` | empty | The public key. Sent as `X-Impel-Key`. |
| `apiBaseUrl` | empty | The API address. With it set, the widget talks to your agent. |
| `transport` | `auto` | `auto`, `demo` or `http`. |
| `brandColor` | `#1f51ff` | Launcher, your visitor's bubbles and primary buttons. Hex only. |
| `accentColor` | `#0b36d9` | Small marks and progress. Hex only. |
| `fontFamily` | `Plus Jakarta Sans` | A font name, `System UI`, `Serif` or a CSS font stack. |
| `cornerRadius` | `18` | Panel corner in pixels. |
| `launcherStyle` | `portrait` | `portrait` or `icon`. |
| `position` | `right` | `right` or `left`. |
| `offset` | `24` | Distance from the page edge in pixels, 0 to 80. |
| `zIndex` | `2147483000` | Lower it where another element must sit on top. |
| `startOpen` | `false` | Open the panel when the page loads. |
| `inline` | `false` | Fill `container` as part of the page. |
| `container` | `document.body` | Where the widget mounts. |
| `greetOnOpen` | `true` | Show the agent's first message when the panel opens. |
| `fileUploads` | `true` | Show the attach button. |
| `sound` | `true` | Play the interface sounds. |
| `showBanner` | `true` | Show the footer line. |
| `nudgeDelay` | `4200` | Milliseconds before the unprompted nudge. `0` turns it off. |
| `agentName`, `agentStatus` | | Name and status line in the header. |
| `placeholder`, `greeting`, `teaser` | | Words in the message box, the first message and the nudge. |
| `suggestions` | empty | Questions shown before the visitor writes. The widget keeps the first four. |
| `identify` | empty | Details you already know, such as `name`, `email` or `phone`. The agent does not ask for them again. |

The settings you save in the app are sent to the widget by `GET /api/v1/chat/status`. That list excludes `key`,
`apiBaseUrl`, `transport`, `container`, `identify`, `zIndex`, `soundBase`, `cards` and `inline`. Only your code sets those.

## Control it from your page

```js theme={null}
ImpelChat.open();
ImpelChat.close();
ImpelChat.toggle();
ImpelChat.isOpen();            // true or false
ImpelChat.identify({ name: "Ada", email: "ada@yourcompany.com" });
ImpelChat.update({ brandColor: "#112233" });
ImpelChat.reset();             // forget the stored thread and start again
ImpelChat.destroy();           // remove the widget
ImpelChat.version;             // the build you run

const off = ImpelChat.on("reply", (data) => { /* ... */ });
off();                         // stop listening
```

Events are `ready`, `open`, `close`, `message`, `reply`, `rating`, `fullscreen` and `blocked`. An error inside your
listener does not reach the widget.

The widget stores the visitor's thread in the browser under `impel_chat_session` and reads it back on the next page.
It writes nothing until the visitor types. If storage is blocked, the widget still works and forgets the thread.

## The public endpoints

The widget calls these routes under the `apiBaseUrl`. They need no token. Each carries `X-Impel-Key` and, after the
first answer, `X-Impel-Session`. Browsers may call them from any site: the server answers cross-origin requests and
checks the allowed sites itself.

| Route | Use |
| - | - |
| `GET /chat/status` | Can an agent answer here, and how the widget looks. Makes no contact. |
| `POST /chat/session` | The visitor's thread, the greeting and the suggestions. |
| `POST /chat/message` | One message. Waits for the agent's reply. |
| `POST /chat/upload` | One file for the next message. |
| `POST /chat/rating` | The visitor's rating, saved on the contact's timeline. |
| `POST /chat/end` | The visitor cleared the chat. The thread starts again. |
| `GET /chat/files/{token}` | A file of the conversation, by its signed link. |

The server returns a signed visitor handle in `X-Impel-Session`. The widget sends it back on each call. A handle that
the server did not sign starts a new thread.

### Send a message

```bash theme={null}
curl -X POST https://api.impellabs.tech/api/v1/chat/message \
  -H "X-Impel-Key: pub_..." \
  -H "X-Impel-Session: SESSION_HANDLE" \
  -H "Content-Type: application/json" \
  -d '{"text": "Do you ship to Dubai?", "collected": {"name": "Ada"}}'
```

```json theme={null}
{
  "reply": "Yes. Delivery takes two to four days.",
  "blocks": [],
  "chips": ["Track my order"],
  "taps": {"Track my order": "track_order"},
  "collected": {},
  "card": null,
  "collect": null
}
```

The body takes these fields.

* `text`: up to 4000 characters. Send `text`, `files`, or both.
* `files`: up to ten file links from `/chat/upload`.
* `collected`: details the visitor typed, such as `name`, `email` and `phone`. Only those three reach the contact, and
  as claims.
* `page`: an object that describes the page, for example its `url`.
* `tap`: `{"id": "..."}` when the visitor taps a suggestion or a row the agent offered.

The reply is the agent's own text, plus blocks and chips built from its messages. If no answer comes in time, `reply`
holds the channel's waiting words in the visitor's language. A blank waiting message returns an empty reply.

### Upload a file

`POST /chat/upload` takes a multipart form with one `file` field and returns `{"url": "..."}`. Put that link in the
`files` list of the next message. A file can be up to 20 MB. The server checks the file type and refuses a file that is
not what its name says. Links to files last 90 days.

### Errors

| Status | When |
| - | - |
| `400` | No text and no file, no file in an upload, or a file the server refuses. |
| `401` | The key is not valid. |
| `403` | The site is not allowed (`reason: origin_not_allowed`), or the caller looks like a crawler. |
| `429` | Too many requests. The limits are per key and visitor address each minute, and per visitor and per workspace each hour. |
| `503` | No published agent answers this chat. The body has `reason: no_agent`. |

## Related

* [Channels](https://docs.impellabs.tech/api/channels.md) lists the channel routes, including how to create the widget.
* [Conversations](https://docs.impellabs.tech/api/conversations.md) shows how to read and answer the thread from your own tools.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.