SDK

Control the widget from your page with window.KauZap, or draw your own chat with the headless client.

Widget

The loader snippet creates window.KauZap. Calls made before the widget is ready are queued and run in order.

If you bundle your front end, you can mount the widget from the package instead: import { init } from '@kauzap/sdk', then const kauzap = init({ propertyKey: 'pk_…', widgetUrl: 'https://widget.kauzap.com/' }). It returns the same methods.

MethodWhat it does
identify(token)Signs the visitor in with an identity token from your server
setAttributes(attributes)Adds page context, such as the current plan or cart value. Agents see these labelled "Set by the page, not verified", so never rely on them for anything that matters. Use the identity token or a signed URL for trusted data.
open(), close()Opens or closes the chat window
sendMessage(text)Sends a message as the visitor, for example from your own "Ask about this order" button
on(event, listener)Subscribes to an event and returns a function that unsubscribes
logout()Ends the visitor session on this device. Call it when the user signs out of your app.

Events

EventListener gets
readyNothing. The widget has loaded.
open, closeNothing
unreadThe unread message count
messageIds and sender of a new message, without its text, so you can show your own notification
const off = window.KauZap.on('unread', (count) => {
  document.title = count ? `(${count}) My shop` : 'My shop';
});

Headless client

For apps that draw their own chat interface. It uses the same session, identity and realtime connection as the widget, with no UI. After a reconnect it replays anything missed, so your view stays complete.

import { createClient } from '@kauzap/sdk';

const client = createClient({
  propertyKey: 'pk_your_property_key',
  apiUrl: 'https://YOUR_PROJECT.supabase.co/functions/v1/api',
  supabaseUrl: 'https://YOUR_PROJECT.supabase.co',
  supabaseKey: 'your publishable key',
});
await client.identify(await getTokenFromYourServer());
client.on('message', (message) => render(message));
await client.sendMessage('Hi, where is my order?');
MemberWhat it does
start({ challengeToken? })Starts an anonymous session for this device, unless one is active. If it fails with challenge_required (the error's details carry provider, site_key and retry_after_seconds), show your challenge and call start again with the solved token. Each token works once.
identify(token)Signs the visitor in; resolves with the session
setAttributes(attributes)Same as the widget: page-set and not verified. A null value removes a key.
conversations()The visitor's conversations, newest first
openConversation(id), startConversation()Loads or starts a conversation and subscribes to it
sendMessage(text)Sends in the open conversation, or starts one
session, conversationThe current session and the open conversation
on(event, listener)Typed events: session, message, change, error. Returns an unsubscribe function.
logout()Closes the connection and forgets this device's visitor

The headless client runs in your page, so it keeps the anonymous device id in your page's localStorage (key kz.device), where your own scripts can read it. Pass a storage option to keep it elsewhere. logout() removes it.

Challenge on bursts

When a property has Challenge bursts of new chats on and many anonymous chats start from one network, start() rejects with code challenge_required. The widget handles this itself. With the headless client, show the challenge named in the error's details (provider is turnstile, Cloudflare Turnstile) and start again with the token. Your page's hostname must be on the KauZap Turnstile widget's hostname list (ask the KauZap team), and your Content-Security-Policy must allow https://challenges.cloudflare.com in script-src and frame-src. If provider is missing, wait retry_after_seconds and try again.

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>

try {
  await client.start();
} catch (error) {
  if (error.code !== 'challenge_required' || error.details.provider !== 'turnstile') throw error;
  turnstile.render('#challenge', {
    sitekey: error.details.site_key,
    // Each token works once; a rejected one gives another challenge_required.
    callback: (token) => client.start({ challengeToken: token }),
  });
}