Security

Identity verification.

Cryptographically prove who is on the other side of the chat.

Why it matters.

By default, the widget trusts whatever externalId or email you pass to KalTalk.init(). Anyone who knows or guesses a customer’s email can land on that customer’s record. Identity verification closes the gap: your backend signs a short-lived token with a secret only you and KalTalk know, and KalTalk rejects any identify whose signature does not match.

Available on the Pro plan and above.

Get it working in three steps.

1. Copy your signing secret

Open Settings › Security › Identity verification in your dashboard and click Reveal. Store the value as KALTALK_IV_SECRET in your backend environment. Never ship it to the browser.

2. Sign a JWT on the server

On every authenticated page render, sign a token with HS256 and your secret. The sub claim must be your customer’s stable identifier.

import jwt from "jsonwebtoken";

export function kaltalkUserJwt(user) {
  return jwt.sign(
    { sub: user.id, email: user.email, name: user.name },
    process.env.KALTALK_IV_SECRET,
    { algorithm: "HS256", expiresIn: "1h" }
  );
}

3. Pass the token to the widget

<script>
  KalTalk.init({
    organizationId: "YOUR_ORG_ID",
    userJwt: "<%= kaltalkUserJwt(user) %>"
  });
</script>

Paste a sample JWT into the Test it card on the dashboard to confirm everything is wired up before going live.

Modes.

Off
The default. Identity verification is disabled. userJwt is decoded but never verified, so the claims become self-claimed plain identity (no shield is stamped). Plain identity fields keep working too. Use this while you wire signing on the backend.
Optional
Tokens are verified when sent. A successful verification stamps the verified shield on the customer record. Failed verification (bad signature, expired, malformed) downgrades any prior verified flag, but the claim payload still populates the customer as self-claimed identity so the dashboard shows a name instead of an Unknown row. Sessions without a token still bind via plain identity fields, identical to the no-verification flow.

How agents see it.

The customer drawer in your inbox shows a shield that reflects the identity state for each conversation.

Identity verified (green)
Backed by a host signature. The JWT verified cleanly and the customer record is bound to the sub in your token. Strongest trust state.
Unverified (amber)
Claimed identity with no proof. Either no JWT was sent, or one was sent and failed verification (bad signature, expired, malformed). The claim payload still populates the customer so the row isn't blank, but treat with caution before sharing sensitive history.

A few rules to keep it tight.

  • Keep tokens short-lived.
    One hour is a sensible default. Re-sign on every authenticated page render so a leaked token has bounded value.
  • Use your immutable user ID for sub.
    Anything a user can edit (such as username) will fragment their history if they change it.
  • Rotate the secret if it leaks.
    The dashboard supports a 24 hour grace window so you can deploy without downtime, plus an emergency replace for confirmed leaks.

Need the install snippet first? See Chat widget.