Skip to main content
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

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

<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 on npm.
npm install @kataven/client
// 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:
// 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.
// 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

ErrorCause
origin_not_allowedPage’s Origin header isn’t in the widget key’s domain_allowlist. Add it via widget_keys.update(...).
widget_disabled_for_agentThe agent’s widget_enabled = false. Toggle in the dashboard or via widget_settings.update_agent.
guest_not_allowedThe agent only allows authenticated users. Use the user-token flow above.
client_key_disabledThe key was disabled. Re-enable or rotate.