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

# Live chat widget

> Install and configure the Woes customer chat widget.

The live chat widget lets customers contact your team from your website or application. It uses a workspace public key and a widget API route to create and continue conversations.

## Install the widget

Add the snippet to pages where chat should be available.

```html theme={null}
<script>
  window.Woes =
    window.Woes ||
    function () {
      (window.Woes.q = window.Woes.q || []).push(arguments);
    };
</script>
<script
  async
  src="https://cdn.woes.dev/widget.js"
  data-public-key="YOUR_WIDGET_PUBLIC_KEY"
  data-api="https://YOUR_APP_ORIGIN/api/widget/messages">
</script>
```

| Attribute         | Required | Purpose                                                                 |
| ----------------- | -------- | ----------------------------------------------------------------------- |
| `src`             | Yes      | Loads the hosted Woes widget script.                                    |
| `data-public-key` | Yes      | Identifies the workspace for public widget requests.                    |
| `data-api`        | Yes      | Points the widget at the Woes widget message route for your deployment. |

<Note>
  The widget public key is intentionally public. It identifies the workspace but does not grant general table, admin, or authenticated workspace access.
</Note>

## Identify signed-in customers

When your app knows the customer, pass stable identity and display context.

```html theme={null}
<script>
  window.Woes("identify", {
    externalId: "user_123",
    email: "customer@example.com",
    name: "Ada Lovelace",
    company: "Example Co",
    plan: "Pro"
  });
</script>
```

Send only fields that help support. Avoid secrets, payment details, unnecessary personal data, and unrelated account information.

## Add page context

Context helps operators understand where the question came from.

```html theme={null}
<script>
  window.Woes("setContext", {
    page: window.location.pathname,
    environment: "production",
    apiVersion: "2026-06-01"
  });
</script>
```

Good context is:

* Short.
* Operational.
* Safe for operators to see.
* Helpful for reproducing the issue.

## Conversation history

Conversation history requires the per-conversation history secret issued for that conversation. Verified customer identity does not replace that secret.

This separation protects customers from one public workspace key being enough to read arbitrary conversation history.

## Test the installation

<Steps>
  <Step title="Load the page">
    Open the page with the widget installed. Use a private browser session for a clean first-run test.
  </Step>

  <Step title="Send a message">
    Open the widget and send a short test message.
  </Step>

  <Step title="Check the inbox">
    Confirm the conversation appears in Woes with the expected channel, identity, and page context.
  </Step>

  <Step title="Reply as an operator">
    Send a reply and confirm the browser receives it.
  </Step>

  <Step title="Refresh and continue">
    Refresh the browser and confirm conversation continuity works as expected.
  </Step>
</Steps>

## Security model

The public widget route is narrower than authenticated workspace routes.

It should:

* Accept requests for the workspace identified by the public key.
* Validate payloads and route controls.
* Avoid broad database access.
* Avoid returning internal notes, secrets, or unrelated workspace data.
* Keep operator-only diagnostics out of customer responses.

## Troubleshooting

<AccordionGroup>
  <Accordion title="The widget does not appear">
    Confirm the script URL is reachable, `data-public-key` is present, `data-api` is correct, and your content security policy allows the script.
  </Accordion>

  <Accordion title="Messages do not arrive in Woes">
    Confirm the workspace public key is correct, the widget API route is deployed, the origin is allowed, and the payload is within route limits.
  </Accordion>

  <Accordion title="Customer identity is missing">
    Confirm `window.Woes("identify", ...)` runs after the shim is loaded and before or near the first customer message.
  </Accordion>

  <Accordion title="History does not continue after refresh">
    Confirm the browser has the conversation state and that the per-conversation history secret is present and valid.
  </Accordion>
</AccordionGroup>
