Quickstart

Put the chat widget on your site and tell KauZap who your signed-in users are. Plan on about 30 minutes.

What you need

1. Add the widget (5 minutes)

Paste the loader before </body> on every page that should show the chat. It loads asynchronously and never blocks your page.

<script async id="kauzap-loader"
  src="https://widget.kauzap.com/loader.js"
  data-property-key="pk_your_property_key"></script>

That is the production widget. On staging use https://st-widget.kauzap.com/loader.js.

Reload the page. The launcher appears in the bottom-right corner, and visitors can already chat anonymously.

2. Mint an identity token on your server (15 minutes)

An identity token is a short-lived JWT signed with your identity secret. Mint it on your server only: the secret must never reach a browser. This is the sample app's code, unchanged:

import { createHmac, randomUUID } from 'node:crypto';

const b64url = (value) => Buffer.from(value).toString('base64url');

/**
 * Mint a KauZap identity token (JWT, HS256) on your server. Never do this in the browser:
 * the secret must stay on your backend.
 *
 * @param {object} opts
 * @param {string} opts.secret Identity secret for the property (Settings, Developers).
 * @param {string} opts.kid Key id of that secret, sent in the JWT header.
 * @param {string} opts.propertyKey The property's public key (pk_...), sent as `iss`.
 * @param {string} opts.sub Your stable user id.
 * @param {string} [opts.name]
 * @param {string} [opts.email]
 * @param {string} [opts.phone]
 * @param {Record<string, unknown>} [opts.attributes] Shown to agents in the customer panel.
 * @param {number} [opts.ttlSeconds] Lifetime, at most 600 seconds. Default 300.
 * @param {number} [opts.now] Current time in milliseconds. Default Date.now().
 * @returns {string}
 */
export function mintIdentityToken({
  secret,
  kid,
  propertyKey,
  sub,
  name,
  email,
  phone,
  attributes,
  ttlSeconds = 300,
  now = Date.now(),
}) {
  if (!secret || !kid || !propertyKey || !sub) {
    throw new Error('secret, kid, propertyKey and sub are required');
  }
  if (!(ttlSeconds > 0 && ttlSeconds <= 600)) {
    throw new Error('ttlSeconds must be between 1 and 600');
  }
  const iat = Math.floor(now / 1000);
  // JSON.stringify drops the optional claims you left undefined.
  const claims = {
    iss: propertyKey,
    sub,
    name,
    email,
    phone,
    attributes,
    iat,
    exp: iat + ttlSeconds,
    jti: randomUUID(),
  };
  const header = b64url(JSON.stringify({ alg: 'HS256', typ: 'JWT', kid }));
  const input = `${header}.${b64url(JSON.stringify(claims))}`;
  const signature = createHmac('sha256', secret).update(input).digest('base64url');
  return `${input}.${signature}`;
}
examples/node-integration/identity-token.mjs

Expose it on a route that only answers for a signed-in user, for example:

// GET /kauzap-token, behind your own login check
const token = mintIdentityToken({
  secret: process.env.KAUZAP_IDENTITY_SECRET,
  kid: process.env.KAUZAP_IDENTITY_KID,
  propertyKey: 'pk_your_property_key',
  sub: user.id, // your stable user id
  name: user.name,
  email: user.email,
  attributes: { plan: user.plan },
});
res.setHeader('cache-control', 'no-store');
res.end(JSON.stringify({ token }));

3. Identify the user in the page (5 minutes)

Once the loader has run, fetch a token and pass it to KauZap.identify. Calls made before the widget is ready are queued.

<script>
  document.getElementById('kauzap-loader').addEventListener('load', async () => {
    const res = await fetch('/kauzap-token', { credentials: 'same-origin' });
    if (res.ok) window.KauZap.identify((await res.json()).token);
  });
</script>

A token works once and for at most 10 minutes, so fetch a fresh one each time you call identify. If the visitor already chatted anonymously, their conversation moves to the identified contact.

4. Check it (5 minutes)

  1. Open the widget and send a message.
  2. In the agent app, open the conversation. The customer panel shows the name, email and attributes from your token.
  3. Sign out in your app and call KauZap.logout() so the next person on the device starts fresh.

If the widget stays anonymous, check that the page origin is in the allowed origins, the kid matches the secret, and your server clock is right. Tokens more than 60 seconds out are rejected.

Next