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

# Add a voice AI agent to your website — Kataven widget

> Embed Kataven's voice + chat widget on any website. Mint a public key, paste a script tag, done — works on Webflow, WordPress, Next.js, Squarespace, plain HTML.

Two pieces:

1. Server-side: mint a public client key (`pk_live_…`) for your
   website's domain.
2. Browser-side: load the widget JS and hand it the public key.

## 1. Mint a client key

```python theme={null}
created = client.widget_keys.create(
    name="acme.com production",
    domain_allowlist=["https://acme.com", "https://www.acme.com"],
    agent_id="<agent uuid>",   # optional; pins this key to one agent
)
print("PUBLIC:", created["public_key"])  # save in your frontend env
print("SECRET:", created["secret_key"])  # save in your backend env (only if using authenticated end-users)
```

Both keys are returned **once**. Save them now.

## 2. Drop the widget on your page

```html theme={null}
<script src="https://widget.kataven.ai/v1.js"></script>
<script>
  Kataven.init({
    publicKey: "pk_live_acme_xxxxxxxxxxxxxxxxxxxxxxxxxx",
    // agentId: "..."   // omit if your client key is pinned
  });
</script>
```

The widget injects a launcher button at the position you configured
in `widget_settings`. End-users click it and start talking.

### Or: install via npm (React, Next.js, Vite, plain TS)

For React / Next.js / Vite shops who'd rather pin a version and tree-shake than load a CDN bundle, the same widget ships as [`@kataven/client`](https://www.npmjs.com/package/@kataven/client) on npm.

```bash theme={null}
npm install @kataven/client
```

```tsx theme={null}
// Next.js — anywhere client-side, e.g. app/components/SupportWidget.tsx
"use client";
import { useEffect } from "react";
import { Kataven } from "@kataven/client";

export function SupportWidget() {
  useEffect(() => {
    Kataven.init({
      publicKey: process.env.NEXT_PUBLIC_KATAVEN_KEY!,
      // agentId: "..."   // omit if your client key is pinned
    });
  }, []);
  return null;   // the widget injects its own launcher into <body>
}
```

Same `Kataven.init` shape as the script-tag flow — the only difference is *where the bundle comes from*. Use the npm path when you want pinned versions, TypeScript types in your IDE, or tree-shaking. Use the script-tag CDN when you want zero build setup (Webflow, WordPress, Squarespace, Shopify, plain HTML).

## 3. (Optional) Authenticated end-users

If your agent's `auth_mode` is `user` or `both`, you can identify
the visitor:

```js theme={null}
// From the customer's backend, mint an exchange token:
const response = await fetch("https://api.kataven.ai/hub-api/api/v1/widget/auth/user-token", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.KATAVEN_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    external_user_id: user.id,
    display_name: user.name,
    email: user.email,
  }),
});
const { user_token } = await response.json();
// Hand `user_token` to your frontend; it has 5-minute TTL.
```

```js theme={null}
// Browser-side, hand the user_token to the widget:
Kataven.init({
  publicKey: "pk_live_...",
  userToken: user_token,   // browser swaps this for a real user JWT
});
```

The widget's `Kataven.init` takes care of the exchange call.

## Troubleshooting

| Error                       | Cause                                                                                                      |
| --------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `origin_not_allowed`        | Page's `Origin` header isn't in the widget key's `domain_allowlist`. Add it via `widget_keys.update(...)`. |
| `widget_disabled_for_agent` | The agent's `widget_enabled = false`. Toggle in the dashboard or via `widget_settings.update_agent`.       |
| `guest_not_allowed`         | The agent only allows authenticated users. Use the user-token flow above.                                  |
| `client_key_disabled`       | The key was disabled. Re-enable or rotate.                                                                 |
