Your backend holds the tenant identity secret (dashboard → Settings → Identity). Never ship it to mobile apps or the browser — only short-lived JWTs signed with that secret.
Claims
sub(required) — your user id; stored as the end user external id.name,email(optional) — display fields.iat,exp(required) — unix seconds; expiry at most 24 hours ahead.
Exchange for session token
Widget/SDK calls POST /api/v1/widget/session with identityToken, optional anonymousId, and channel. A valid identity token wins over anonymous id. Response includes sessionToken (our HS256 JWT with tid, euid, ext, ver) used as Authorization: Bearer on all end-user routes.
Node.js
import jwt from "jsonwebtoken";
export function issueIdentityToken(userId, { name, email } = {}) {
const secret = process.env.SUPPORTAL_IDENTITY_SECRET;
const now = Math.floor(Date.now() / 1000);
return jwt.sign(
{ sub: userId, name, email, iat: now, exp: now + 3600 },
secret,
{ algorithm: "HS256" },
);
}Python
import time, jwt
def issue_identity_token(user_id: str, name: str | None = None, email: str | None = None) -> str:
secret = os.environ["SUPPORTAL_IDENTITY_SECRET"]
now = int(time.time())
payload = {"sub": user_id, "iat": now, "exp": now + 3600}
if name:
payload["name"] = name
if email:
payload["email"] = email
return jwt.encode(payload, secret, algorithm="HS256")Go
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
func IssueIdentityToken(userID string) (string, error) {
now := time.Now()
claims := jwt.MapClaims{
"sub": userID,
"iat": now.Unix(),
"exp": now.Add(time.Hour).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(os.Getenv("SUPPORTAL_IDENTITY_SECRET")))
}PHP
<?php
use Firebase\JWT\JWT;
function issue_identity_token(string $userId): string {
$secret = getenv('SUPPORTAL_IDENTITY_SECRET');
$now = time();
$payload = [
'sub' => $userId,
'iat' => $now,
'exp' => $now + 3600,
];
return JWT::encode($payload, $secret, 'HS256');
}Ruby
require "jwt"
def issue_identity_token(user_id)
secret = ENV.fetch("SUPPORTAL_IDENTITY_SECRET")
now = Time.now.to_i
payload = { sub: user_id, iat: now, exp: now + 3600 }
JWT.encode(payload, secret, "HS256")
end