Skip to main content

Authenticated Visitors (JWT Identity)

By default, web chat visitors are anonymous. If your website or mobile app has its own login, you can carry that identity into the chat: your backend signs a small JWT for the logged-in user, the widget presents it once, and the conversation is bound to that user — same history on every device, no re-verification, and your agents see who they're talking to.

Auth modes

Open Integrations → Website → your integration → Authentication:

ModeBehavior
Anonymous visitorsAnyone can chat. Visitors pass a bot check (Cloudflare Turnstile). Default.
Anonymous + authenticatedAnonymous visitors allowed; visitors carrying a valid JWT are recognized as logged-in users.
Authenticated onlyOnly visitors with a valid JWT can chat — for apps behind your own login.

Enforce identity (only meaningful in Anonymous + authenticated): when on, a visitor presenting an invalid or expired JWT is rejected instead of falling back to an anonymous session.

The signing secret

The Authentication section manages a per-integration signing secret:

  • Generate — the secret is shown once. Copy it into your backend's secret manager.
  • Rotate — a new secret is issued; the previous one keeps working for 24 hours so your servers can roll over without an outage.
  • Revoke — both secrets stop working immediately. If the integration required authentication, it falls back to anonymous mode.
Sign on your server only

The secret must never appear in website JavaScript, a mobile app binary, or any client-side code. Mint tokens on your backend and hand the finished JWT to the client.

Minting the JWT (your backend)

Sign an HS256 JWT with the integration's secret:

// Node.js — npm i jsonwebtoken
const jwt = require('jsonwebtoken');

const orkiUserJwt = jwt.sign(
{
sub: user.id, // your app's stable user id (this becomes the chat identity)
tenantId: '<your tenant id>',
integrationId: '<your integration id>',
name: user.name, // optional — pre-fills the customer profile
email: user.email, // optional
},
process.env.ORKI_WEBCHAT_SIGNING_SECRET,
{ algorithm: 'HS256', expiresIn: '1h' }, // exp is required; maximum 24h
);

Required claims: sub, iat, exp (≤ 24 h ahead), tenantId, integrationId — the last two must match the integration the token is used with.

Using the token

Website embed:

webchat('init', { tenantId, integrationId, userJwt: orkiUserJwt });
// later, e.g. shortly before expiry — no reload:
webchat('updateToken', { userJwt: freshJwt });
// on logout:
webchat('logout');

The page fires webchat:auth-required when the widget needs a (fresh) token, and webchat:user-set / webchat:auth-error after each attempt.

React (@orki/webchat-react): pass userJwt to <OrkiWebChat />, or use useOrkiWebChat().setUser / updateToken / logout.

Mobile apps: see the React Native and Flutter SDKs.

Custom chat UI: see the Customer Chat API.

How it works (and what it never does)

The JWT is presented once, when the chat session starts. Orki verifies the signature against your integration's secret and issues its own short session credential — your JWT never appears in URLs and never travels with chat messages. A verified visitor bypasses the bot check (your backend's signature is a stronger signal than a captcha). If an anonymous visitor logs in mid-conversation, their existing chat is upgraded to the verified identity — history preserved.