Identifying users
Usually you don’t want a visitor to see a certain experiences multiple times, for example a popup that the visitor closes should not be shown again later. To do this, we set a cookie to make sure that doesn’t happen. However, if your product runs behind a login, then it’s best practice to set a customer identifier before loading our snippet. This will make sure the visitor will always have the same id, across devices and even after clearing cookies.
This guide explains how to identify users with the client-side API. It supports two modes:
- Basic identification with unsecured profile data.
- Secure identification with a backend-signed JWT for trusted traits.
Set the identifier before the snippet loads (preferred)
Section titled “Set the identifier before the snippet loads (preferred)”If possible, set a stable identifier before loading the Unless snippet. The custom identifier will be used as the identifier of the user. The JWT token is optional and is described below.
<script> window.TxtOptions = { customIdentifier: identifier, //stable custom identifier jwt: token, //optional secure traits JWT data: [] //optional profile data }</script>
<!-- enter your Unless snippet code below --><script data-unless=...></script>Alternatively set the identifier after the snippet loads
Section titled “Alternatively set the identifier after the snippet loads”In cases you can’t set the identifier or JWT before the script loads. For example this can happen in single page application (SPA) that load statically but use runtime requests to log the user in. In that case, you can use our initialize function on the Txt object instead. When using this approach make sure to first set autoInitialize to false in the TxtOptions object to prevent loading of the user before the initialize call happened (see example below).
Signature:
Txt.initialize({ identifier: string, data?: [], //see signature: https://docs.unless.com/javascript-api/audiences/updating-a-profile/ jwt?: string,})Parameters:
identifier(string, required): Stable user ID. This becomes the visitor identifier.data(object, optional): Unsecured traits stored inprofile.jwt(string, optional): Signed JWT containing secure traits.
Use Txt.initialize(...) when identifying a user for the first time on page load, and Txt.setJwt(...) when updating or rotating the signed JWT later in the same session. This is important to prevent the token from expiring if you have a Single Page Application.
Example identification
Section titled “Example identification”When using the Txt object, you have to make sure the script is fully loaded before calling a function on it. To do this use the txt-loaded event.
<script> window.TxtOptions = { autoInitialize: false, //this prevents loading before initialize() happened. }</script>
<!-- enter your Unless snippet code below --><script data-unless=...></script>
<!-- ... later in your code after you got the logged in user ... -->
<script> Txt.initialize({ identifier: 'visitor_123', // replace with backend provided stable identifier data: [ {key: 'firstName', value: 'John', type: 'text'}, // optionally add traits that are not secure ], jwt: 'your token' // replace with backend provided token })</script>Secure identification (recommended)
Section titled “Secure identification (recommended)”Secure identification protects any user information in our system using a signed JWT token. This is highly recommended to protect user information and conversational data. Some functionality in the Unless system require verified identification. For example, if you want to send data to 3rd party API’s through agentic skills. In that case the data you are going to send needs to be validated and secure. To do this you need to create a JWT token on your backend to provide secure traits. If you create a skill that contains a variable with the same name as one of your secure traits, then we will automatically use the value of that secured trait for the variable.
You can also put authentication tokens inside a secured traits so we can pass that on to your receiving API, make sure this token is called authToken and is short lived and not a permanent authentication token. Don’t store any information in the JWT that should be a secret to your user (like secret API keys). By calling the property authToken we provide some extra checks on our backend to make it isn’t send to the client and/or logged.
What is a JSON web token (JWT)?
Section titled “What is a JSON web token (JWT)?”A JSON Web Token (JWT) is an industry standard way to sign data. It typically consists of three parts, separated by dots. A typical JWT looks like this: header.payload.signature.
- The header specifies the token type (JWT) and the signing algorithm (e.g., HS256).
- The payload contains claims about the user or session (e.g., user_id, email).
- Finally, the signature ensures that the token hasn’t been tampered with, using a secret or private key.
For more information see: https://www.jwt.io/introduction#what-is-json-web-token.
JWT requirements
Section titled “JWT requirements”- Algorithm: HS256
- Secret: your account API key (from the dashboard)
- Required claim:
sub(must matchidentifier) - Recommended claim:
exp(expiration timestamp, in seconds) - Optional claims:
traits. Note: any variables you define in your AI skills are automatically filled using these traits and can be considered secure.
Example payload (all of the traits are optional and customizable):
{ "sub": "visitor_123", "traits": { "plan": "enterprise", "region": "emea", "email": "user@company.com", "authToken": "your-custom-token" }, "exp": 1769509999}Refreshing the JWT token
Section titled “Refreshing the JWT token”If you have a Single Page Application, you have to make sure the JWT token does not expire. If your application refreshes or rotates secure identification tokens during a session, you can update the JWT without reloading the Unless snippet by using the Txt.setJwt() function.
window.Txt.setJwt(newJwt)This updates the JWT used for secure identification requests. If the AI component is already loaded, active chat connections will also refresh their authentication automatically.
Arguments
Section titled “Arguments”| Argument | Type | Description |
|---|---|---|
jwt | string | A backend-signed JWT using HS256. The token should contain a sub claim that matches the current identifier. |
Example
Section titled “Example”const refreshedJwt = await fetch('/api/unless/identify-jwt').then((res) => res.text())
window.Txt.setJwt(refreshedJwt)Overview
Section titled “Overview”- Your backend creates an HS256 JWT using your account API key. This API key should stay on the backend and should never be accessible by your end-users in your application.
- Your frontend calls
Txt.initialize({ identifier, data, jwt }). - Unless stores unsecured
datainprofileand the secured JWTtraitsinsecureProfile. - Make sure to refresh the JWT token before it expires.
Backend example (Node.js)
Section titled “Backend example (Node.js)”import jwt from "jsonwebtoken"
const apiKey = process.env.UNLESS_API_KEY
const token = jwt.sign( { sub: "visitor_123", traits: { plan: "enterprise", region: "emea", email: "user@company.com", }, exp: Math.floor(Date.now() / 1000) + 300, }, apiKey, { algorithm: "HS256" },)Troubleshooting
Section titled “Troubleshooting”- Invalid token: Check the signing secret and algorithm (must be HS256).
- Missing
sub: Tokens must include asubclaim that matchesidentifier. - Expired token: Ensure
expis in the future and in seconds (not milliseconds).