Authentication

Signed JWT

The recommended way to identify visitors — a short-lived HS256 token signed with your shared secret

A host-signed JWT is the recommended way to identify visitors. You mint a short-lived token on your server, hand it to the widget, and the backend verifies it against your integration's shared secret.

Token format

FieldRequirementValue
AlgorithmrequiredHS256
SecretrequiredYour integration's shared secret — server-side only
subrequiredA stable, opaque visitor id (your user's id)
iatrequiredIssued-at, Unix seconds
exprequiredExpiry, Unix seconds — recommend 1–24 hours
emailoptionalVisitor email
nameoptionalDisplay name
phoneoptionalPhone
fieldsoptionalA trusted profile map written to the contact (see below)

Sign it on your server

import jwt from "jsonwebtoken";

const token = jwt.sign(
  {
    sub: user.id,
    email: user.email,
    name: user.name,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
  },
  process.env.WEXIO_INTEGRATION_SECRET,
  { algorithm: "HS256" },
);

Hand it to the widget

// React
<WexioWidget
  publicKey="pk_live_..."
  user={{ jwt: serverSignedJwt, name: "Ada Lovelace", email: "ada@example.com" }}
/>
// Web Component
document.querySelector("wexio-widget").identify({ jwt: serverSignedJwt, name: "Ada" });

// Script loader
window.Wexio("identify", { jwt: serverSignedJwt, name: "Ada" });

The backend verifies HS256 and exp, extracts sub as the user id, and establishes a verified session. A leaked token self-revokes at exp.

Trusted profile fields

Because the whole token is signature-verified, a fields claim writes the contact's profile server-side — no prechat form needed:

const token = jwt.sign(
  {
    sub: user.id,
    email: user.email,
    name: user.name,
    fields: {
      company: "Acme",            // system field
      lead_status: "QUALIFIED",   // system select — must be a valid option
      plan_tier: "enterprise",    // custom field by key
      mrr: 4200,
    },
    iat: now,
    exp: now + 3600,
  },
  process.env.WEXIO_INTEGRATION_SECRET,
  { algorithm: "HS256" },
);

Rules:

  • name splits into first_name + last_name at the first space; email / phone map to the email/phone fields. Explicit fields.first_name etc. override the split.
  • Other keys write people-field values by key — but only fields that exist for your org and are writable.
  • Unknown keys, read-only/derived fields (*_at, total_*, flow_*, …), and protected keys (trust flags, channel ids) are skipped silently. Invalid values are skipped, never thrown.

Protected keys — web_verified, is_blocked, chat_status, priority, channel ids like telegram_id, and wexio_id — can never be set through the JWT. web_verified in particular is set by the backend only on a real verification, so flows and the AI assistant can trust it.

Rotating identity

Pass a new token to log in as a different user; clear it to log out:

<WexioWidget user={{ jwt: newJwt, name: "Grace" }} />   // switch user
<WexioWidget user={undefined} />                         // log out

See Sessions for renewal and shutdown semantics.

On this page