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

# Response Chat Widget

> Configuration, revenue capture, deployment, and hardening for the onsite chat widget.

# Response Chat Widget

The reference for the onsite chat widget. To get it running first, start with the
[Onsite Chat Quickstart](/stateset-response/response-chat-widget-quickstart).

## What it ships with

* **AI ready** — OpenAI, Anthropic, and Gemini integration with streaming, tool feedback, and
  source citations.
* **Rich UI** — Markdown with syntax highlighting, reactions, exports, forms, and programmable
  actions.
* **Mobile ready** — responsive fullscreen with safe-area handling for modern iPhone Safari.
* **Operations ready** — built-in record views for orders, products, subscriptions, returns,
  and exchanges.
* **Global ready** — locale packs, translation overrides, automatic RTL layout.
* **Developer friendly** — UMD bundle, type definitions, mock and AI dev servers, Docker image.

## Hosting options

| Approach            | When to use                                    | Notes                                                                    |
| ------------------- | ---------------------------------------------- | ------------------------------------------------------------------------ |
| **Static CDN**      | You already have APIs and just need the bundle | Serve `chat-widget.css` and `chat-widget.bundle.js` with long cache TTLs |
| **Docker + Nginx**  | Easiest full-stack demo                        | `docker build -t chat-widget . && docker run -p 8080:80 chat-widget`     |
| **Edge function**   | Injecting the snippet across multiple sites    | Host the bundle on a CDN and lazy-load via script tag                    |
| **React / Vue app** | Embedding inside an SPA                        | Use the UMD API (`window.ChatWidget`) or wrap it in a component          |

### Production CDN

The recommended production setup is Cloud Storage plus Cloud CDN with versioned assets.

One-time setup creates the bucket, CDN backend, URL map, HTTPS proxy, and global IP:

```bash theme={null}
scripts/setup-cdn-gcp.sh \
  --project stateset-network \
  --bucket stateset-chat-cdn \
  --domain stateset.chat \
  --location US
```

Then in DNS for `stateset.chat`, create an `A` record pointing at the global IP the script
prints, and wait for the managed SSL certificate to become ACTIVE — that can take 15–60 minutes.

Publish a version:

```bash theme={null}
npm run build
scripts/publish-cdn.sh --bucket stateset-chat-cdn --version 1.0.0
```

Embed the versioned assets:

```html theme={null}
<link rel="stylesheet" href="https://stateset.chat/chat-widget.v1.0.0.css" />
<script src="https://stateset.chat/chat-widget.bundle.v1.0.0.js"></script>
```

<Tip>
  Versioned filenames are what make long cache TTLs safe. Publish a new version rather than
  overwriting one — then you rarely need cache invalidation at all:

  ```bash theme={null}
  gcloud compute url-maps invalidate-cdn-cache stateset-chat-cdn-map --path "/*"
  ```
</Tip>

## Backend options

| Server                             | Suitable for                                                   |
| ---------------------------------- | -------------------------------------------------------------- |
| **Mock server** (`dev-server.js`)  | Demos and UI work. No auth, no persistence                     |
| **AI server** (`server-openai.js`) | OpenAI/Anthropic/Gemini completions                            |
| **Your API**                       | Production — expose `POST /api/chat` plus optional thread CRUD |

<Warning>
  The bundled AI server has **no authentication, logging, or database**. Add all three before
  putting it in front of customers — it exists to prove the wiring, not to run production traffic.
</Warning>

### Hardening

* Put your API behind OAuth/JWT or signed session cookies.
* Rate limit inbound chat requests per IP and session at the edge.
* Validate and sanitize user uploads before echoing them in the UI.
* Sanitize AI-generated markdown — the widget uses DOMPurify by default.
* Set Content-Security-Policy headers in `nginx.conf` or at your hosting platform.

### Secrets and runtime

Keep `.env` out of version control, hold provider keys in a secret manager (AWS Secrets
Manager, Doppler, Vault), and pin Node via `.nvmrc` or your host's runtime setting — the widget
requires **20.17+**.

## Revenue capture v2

For the ResponseCX lead-qualifier rollout, the bundle exports helpers that turn a public config
response into mountable widget props, so configuration lives server-side rather than in the
embed snippet.

```html theme={null}
<script>
  async function bootRevenueCaptureWidget() {
    const widgetToken = 'SIGNED_PUBLIC_WIDGET_TOKEN';
    const response = await fetch(
      `/api/public/widget/v2/config?token=${encodeURIComponent(widgetToken)}`
    );
    const config = await response.json();

    const props = window.ChatWidget.createRevenueCaptureMountProps({
      config,
      widgetAuthToken: widgetToken,
      overrides: {
        position: 'bottom-right',
        calendlyUrl: 'https://calendly.com/stateset/responsecx-demo',
      },
    });

    window.ChatWidget.mount({ container: document.getElementById('chat'), props });
  }

  bootRevenueCaptureWidget();
</script>
```

Helpers:

* `window.ChatWidget.buildRevenueCapturePageContext(...)`
* `window.ChatWidget.createRevenueCaptureMountProps(...)`
* `window.ChatWidget.mountRevenueCaptureWidget(...)`

The config route is `GET /api/public/widget/v2/config?token=<signed-widget-token>`. For a local
end-to-end mock of the public contract, run `npm run dev` and open `/widget/v2/demo.html`.

<Note>
  The token in the embed is a **signed public** widget token — it is visible to anyone who views
  source, and is meant to be. Do not put a private API key in the embed snippet.
</Note>

## Related

* [Onsite Chat Quickstart](/stateset-response/response-chat-widget-quickstart)
* [Response Public API](/stateset-response/response-public-api)
* [Response MCP Servers](/stateset-response/response-mcp)
