Integrate
Player identity
A player id is never accepted directly from the browser. Your server signs a short-lived token, your page hands it to the widget, and the platform verifies the signature.
#Why it matters
Once a visitor is linked to a verified player:
- agents see the verified player id and operator data in the conversation;
- the AI agent can read account data such as balance, deposits and bonus history from your operator API (for that player only);
- actions such as bonuses can only be requested for verified players.
In calls to your operator API the player id is always filled in from this token; it cannot be changed by anything the visitor types.
#Flow
Get the secret
In the admin console, on Websites & widget, click Reveal identity secret (audited) (requires
websites:write; each reveal is written to the audit log). Each website has its own secret, shown base64url-encoded. Keep it on your server only.Sign a token on your server
When the player logs in, base64url-decode the secret and use the resulting bytes to sign an HS256 JWT (see the examples below).
Hand the token to the widget
Your page calls
window.TwoPeople.identify(token). The widget sends the token to POST/api/v1/widget/identity; the platform verifies the signature and claims and links the visitor to the player.
#Token format
// header
{ "alg": "HS256", "typ": "JWT" }
// payload
{
"sub": "player-48213", // your player id
"aud": "wk_…", // the website's widget key
"iat": 1767225600, // issued at (unix seconds)
"exp": 1767225900, // expiry, at most iat + 300
"jti": "5f0c2a8e-…" // unique token id (single use)
}| Name | Type | Description |
|---|---|---|
algrequired | HS256 | HS256 is the only accepted algorithm. |
subrequired | string | The player id in your operator system. 1–128 characters: letters, digits and _ . : @ - only. |
audrequired | string | The website’s widget key (wk_…). |
iatrequired | number | Issued-at time (Unix seconds). Tokens older than 5 minutes from iat are rejected. |
exprequired | number | Expiry. Must be iat + 300 or earlier. |
jtirequired | string | Unique token id (up to 128 characters). Every token can be used only once. |
#Server examples
The examples read the widget key and the secret from environment variables: TWOPEOPLE_WIDGET_KEY and TWOPEOPLE_IDENTITY_SECRET.
// npm install jose
import { SignJWT } from 'jose';
import { randomUUID } from 'node:crypto';
// The secret is shown base64url-encoded: decode it to bytes before signing.
const secret = Buffer.from(process.env.TWOPEOPLE_IDENTITY_SECRET, 'base64url');
const widgetKey = process.env.TWOPEOPLE_WIDGET_KEY; // wk_…
export async function playerIdentityToken(playerId) {
const now = Math.floor(Date.now() / 1000);
return new SignJWT({})
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
.setSubject(String(playerId)) // your player id
.setAudience(widgetKey)
.setJti(randomUUID()) // unique per token
.setIssuedAt(now)
.setExpirationTime(now + 300) // at most 5 minutes
.sign(secret);
}#On the page
Because the token is short-lived and single-use, the safest approach is to fetch it from your own server when you need it. window.TwoPeople is defined once the widget has loaded:
// Runs on your page after the player has logged in.
// /api/support/identity-token is an endpoint on YOUR backend that returns
// { token } for the logged-in player (see the server examples above).
function whenWidgetReady(callback) {
if (window.TwoPeople) return callback(window.TwoPeople);
setTimeout(() => whenWidgetReady(callback), 200);
}
whenWidgetReady(async (widget) => {
const res = await fetch('/api/support/identity-token', {
method: 'POST',
credentials: 'include',
});
if (!res.ok) return;
const { token } = await res.json();
const linked = await widget.identify(token); // true when the player is verified
if (!linked) console.warn('2people: player identity was not accepted');
});identify() resolves to true when the player is linked and to false when the token is rejected or the request fails. An open conversation is refreshed after linking.
#Errors
When POST/api/v1/widget/identity rejects a token it returns application/problem+json:
| HTTP | code | Reason |
|---|---|---|
| 401 | INVALID_IDENTITY_TOKEN | Signature, algorithm, aud, time (exp / 5-minute age limit) or claims are invalid. |
| 401 | IDENTITY_TOKEN_REPLAYED | A token with the same jti was already used. |