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

# JavaScript API

> Reference for window.replyful: open, close and toggle the chat, identify signed-in users, start a new conversation, read its state, and listen for events such as ready, open, close and message-sent. Calls are safe before the script has loaded; once loaded, every command returns a Promise.

The [embed snippet](/installation) adds `window.replyful` to your page. It works the same for every layout: floating bubble, side panel, full page and search bar.

## Calling the API

There are two ways to call a command:

```javascript theme={null}
replyful('open');   // call form
replyful.open();    // method form
```

The **call form** works everywhere and at any time. Calls made before the chat has loaded are queued and run in order once it has.

The **method form** works once the chat has loaded. Before that it works only with the website snippet: the Google Tag Manager snippet supports just the call form until the chat has loaded. If you're unsure which snippet a page uses, use the call form.

### Return values

Once the chat has loaded, every command returns a Promise. Before that, queued calls return `undefined`, so don't rely on a return value in code that may run early. To run code as soon as the chat is ready, listen for `ready`; it fires straight away if the chat is already ready:

```javascript theme={null}
replyful('on', 'ready', async () => {
  const state = await replyful('state');
  console.log(state.layout, state.open);
});
```

Expected outcomes, such as a token that fails verification, resolve as values. A Promise rejects only when the call itself is wrong, such as an unknown command or a missing argument.

## Commands

| Command | What it does | Resolves to |
| - | - | - |
| `open()` | Opens the chat | Nothing, once it's open |
| `close()` | Closes the chat. A full-page chat is always open | Nothing, once it's closed |
| `toggle()` | Opens the chat if it's closed, and closes it if it's open | Nothing |
| `identify({ token })` | Links the visitor to a signed-in user. See [User identification](/user-identification) | `{ verified: true, transferred }` or `{ verified: false }` |
| `newConversation()` | Starts a new conversation | Nothing |
| `setTheme(theme)` | Changes the colours of a chat [placed in your layout](/embed-in-your-app#theming), for example when your app switches to dark mode | Nothing |
| `state()` | Reads the current state | `{ available, layout, open, identified }` |
| `on(event, callback)` | Calls `callback(detail)` every time `event` fires | Returns a function that removes the callback (once loaded) |
| `once(event, callback)` | Calls `callback(detail)` the next time `event` fires | Returns a function that removes the callback (once loaded) |
| `off(event, callback)` | Stops calling a callback added with `on` | |

The chat window loads on demand. With the chat bubble or side panel, that happens when the visitor first hovers over or opens the chat button. `identify` and `newConversation` resolve once it has loaded, so call them early but don't hold up your own page logic waiting for them.

In `state()`, `available` is `false` when the chat can't run on this page (see the `unavailable` event), `layout` is `floating`, `panel`, `page` or `search`, and `identified` is `true` once `identify` has succeeded.

## Events

Each callback receives one plain `detail` object. An error thrown in your callback doesn't affect the chat or other callbacks.

| Event | Detail | Fires when |
| - | - | - |
| `ready` | `{ layout }` | The chat is set up on the page |
| `open` | `{ reason }` | The chat opens |
| `close` | `{ reason }` | The chat closes |
| `identity-verified` | `{ transferred }` | A token passed to `identify` was verified |
| `identity-failed` | `{}` | A token passed to `identify` was rejected |
| `unavailable` | `{ reason }` | The chat can't run on this page |
| `message-sent` | `{}` | The visitor sent a message |
| `message-received` | `{}` | A reply arrived from the AI agent or your team |

`ready` and `unavailable` are sticky: a callback added after they fired runs straight away.

* `reason` on `open` and `close` is what caused it: `api` (your code), `launcher` (the chat button), `escape`, `backdrop`, `close-button`, or `navigation` (the visitor moved to a page where the chat is hidden).
* `reason` on `unavailable` is `domain-not-allowed` (the page's domain isn't in the chatbot's allowed domains), `channel-unavailable` (the chatbot is turned off or deleted) or `network`.
* `transferred` is present when the visitor's earlier anonymous chat moved to the signed-in user: `{ conversations, messages }`, as counts.
* Message events carry no message text.

## Examples

### Open the chat from your own button

```html theme={null}
<button type="button" onclick="replyful('open')">Chat with us</button>
```

### Single-page apps and React

Subscribe when your component mounts and remove the same callback when it unmounts. `off` also works on calls queued before the chat has loaded.

Your component can mount before the snippet has run, for example when Google Tag Manager adds the snippet later. If `window.replyful` doesn't exist yet, wait for the `replyful:load` event on `window` before subscribing. The chat can't fire events before then, so you won't miss any. Don't define `window.replyful` yourself: the snippet replaces it, and any calls you queued on it would be lost.

```jsx theme={null}
import { useEffect, useState } from 'react';

function ChatButton() {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    const onOpen = () => setOpen(true);
    const onClose = () => setOpen(false);
    const subscribe = () => {
      window.replyful('on', 'open', onOpen);
      window.replyful('on', 'close', onClose);
    };
    if (window.replyful) subscribe();
    else window.addEventListener('replyful:load', subscribe, { once: true });
    return () => {
      window.removeEventListener('replyful:load', subscribe);
      window.replyful?.('off', 'open', onOpen);
      window.replyful?.('off', 'close', onClose);
    };
  }, []);

  return (
    <button type="button" aria-expanded={open} onClick={() => window.replyful?.('toggle')}>
      Chat with us
    </button>
  );
}
```

The snippet is safe to run more than once, so it doesn't matter if your app or Google Tag Manager runs it again after a client-side navigation.

### Identify a signed-in user

```javascript theme={null}
replyful('on', 'ready', async () => {
  const { token } = await fetch('/api/replyful-token').then((r) => r.json());
  const result = await replyful('identify', { token });
  if (!result.verified) {
    console.warn('Replyful could not verify the user');
  }
});
```

See [User identification](/user-identification) for how to sign the token.

### Send chat activity to your analytics

```javascript theme={null}
replyful('on', 'open', ({ reason }) => {
  analytics.track('Chat opened', { reason });
});
replyful('on', 'message-sent', () => {
  analytics.track('Chat message sent');
});
```


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