> ## 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.

# Deploy and Qualify the Onsite Widget

> Ship matching widget assets, configure Redis and reporting, and qualify renewing sessions and native support on every replica.

Deploy the browser artifact and API as a compatible pair. This guide reflects the reviewed
1.2.13 workspace plus local work on October 20, 2026; it does not establish what a hosted
storefront or published image currently runs.

## Ship one complete browser artifact

`npm run build` produces the entry bundle and stylesheet, content-hashed copies, lazy chunks,
licenses, vendored React files, `embed.js` and `asset-manifest.json`. Keep the chunks next to
the bundle and preserve `vendor/`. The built loader contains that build's filenames and SRI
values; `data-rw-asset-base` changes their base URL, not which build they reference.

| Asset | Cache policy |
| - | - |
| `embed.js`, `asset-manifest.json` | `no-store` |
| Stable entry, stylesheet and vendor names | Short TTL; never immutable |
| Content-hashed entry, stylesheet and chunks | `public, max-age=31536000, immutable` |

Upload immutable assets before switching the loader/manifest, and retain previous hashed
assets for rollback. Cross-origin SRI requires CORS-readable asset responses. Byte-rewriting
CDN optimizations break integrity checks; normal gzip/brotli encoding is compatible.
The static `Dockerfile`/Nginx image serves browser assets; `Dockerfile.server` packages the API.
Reset the source loader with `npm run embed:reset` after a local build, while retaining the
complete built artifact for publication.

## Configure the API

For normal production startup set the selected provider key, `ALLOWED_ORIGINS`, and
`STATESET_WIDGET_AUTH_SECRET`. `AI_PROVIDER` accepts `openai`, `anthropic` or `gemini`.
An explicit `STATESET_WIDGET_AUTH_REQUIRED=false` permits a tokenless compatibility deployment;
it does not establish verified conversation ownership for the native inbox or analytics.
Use verified tokens and enforce them after qualifying existing storefronts.

| Setting | Operational effect |
| - | - |
| `REDIS_URL` | Shared thread, rate-limit, execution and response-outbox state; required for multi-replica correctness |
| `REDIS_COMMAND_TIMEOUT_MS` | Bounds store calls during outages; default 2000 ms |
| `THREAD_TTL_MS` | Conversation lifetime in the store; default 24 hours |
| `STATESET_THREAD_TOKEN_REQUIRED=true` | Reject capability-less legacy threads as well as invalid access to protected ones |
| `LOG_LEVEL`, `LOG_FORMAT` | Structured logs and verbosity; request IDs propagate via `x-request-id` |
| `METRICS_TOKEN` | `x-metrics-token` for `/api/metrics`; otherwise private-network/loopback access only |
| `TRUST_PROXY` | Trusted proxy configuration used to derive client IPs |

Without Redis, stores are process-local and disappear on restart. Run one replica for that
mode. Production multi-replica operation needs shared Redis plus a persistence policy suitable
for the conversations you retain.

Use `GET /api/health` for liveness and `/api/ready` for readiness. Readiness stays 503 until
thread, rate-limit, response-outbox and execution stores initialize. Thread reads are
rate-limited too: allow for visitor polling when sizing budgets rather than bypassing access
checks or the limiter.

### Response reporting

When Hasura response reporting is configured, the server queues stable response IDs and
retries background delivery. By default enqueue failure logs/counts the loss and allows chat
to continue. `RESPONSE_OUTBOX_STRICT=true` makes enqueue success a reply precondition and
requires a durable Redis outbox in production when reporting is configured. Queue failure can
then return 503 or an SSE error without `[DONE]`.

Monitor pending records, enqueue failures and confirmed deliveries separately from HTTP
success. The strict queue acknowledgement is not a transaction spanning provider actions,
commerce mutations, helpdesk writes and reporting.

## Enable sessions and native support in stages

Prepare the [admin database and credentials](https://docs.stateset.com/stateset-response/response-chat-widget-admin.md),
then bind the public session issuer to exact website origins, org/agent and optional site.
Store secrets in the deployment's secret manager. Match `WIDGET_SESSION_ALLOWED_ORIGINS` with
CORS `ALLOWED_ORIGINS` and keep session TTL between 60 and 900 seconds.

| Phase | Required behavior |
| - | - |
| Prepared | New image and additive admin schema; issuance and native live chat disabled |
| Pilot | Issuance and native handlers enabled on every replica; tokenless compatibility retained while signed conversations are qualified |
| Enforced | Require widget identity after every installed embed renews successfully |

The repository's `scripts/prepare-live-chat-rollout.js` produces a strategic merge patch for
known deployment bindings and an immutable GHCR digest. It does not apply it. Review the
selected deployment's origins and bindings before using the generated patch. Do not infer
legacy conversation ownership from visitor metadata.

Inside each candidate API pod, with its runtime environment and operator credential:

```bash theme={null}
node scripts/live-chat-preflight.js   --url http://127.0.0.1:3000 --runtime --phase prepared
```

Use the matching `pilot` or `enforced` phase later. Preflight checks liveness/readiness,
authenticated inbox access, a ready Redis thread store, issuer bindings and Hasura schema.
Exit 0 means ready, 1 failed and 2 incomplete; omitting `--runtime` leaves checks pending.
An unauthenticated 401 by itself does not prove readiness.

Qualify a signed visitor turn and operator reply across different replicas. Check wrong-tenant
denial, notes absent from visitor/model transcripts, stable-ID duplicate protection, and token
renewal without a remount or lost draft. An old replica can still generate AI replies during
takeover, so every replica needs the native handler before enabling support.

## Release evidence

The current CI/release workflow records asset SHA-256 values in `release-evidence.json`,
serves that artifact for browser checks, verifies both images against it, and uses the same
bytes for tag publication. Source mismatches, uncommitted source and stale extra chunks fail
the artifact gate. `WIDGET_ASSET_DIR` selects an isolated artifact directory for checks.

Tag publication also requires a configured `WIDGET_CANARY_URL`. JSON and real SSE replay
must both pass; a JSON fallback does not pass the streaming gate. Canary reports record
attempt/check counts without transcript text or capabilities and reference the verified
asset-evidence digest. These gates establish the cases exercised. Physical iPhone use,
screen-reader review and real-provider answer quality still need separate evidence.

If a rollout fails qualification, restore the prior image/loader and feature flags, preserving
additive tables and credentials. Release controlled operator claims before disabling live chat.
See [native live chat](https://docs.stateset.com/stateset-response/response-chat-widget-live-chat.md) for the operator flow.


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