The signature object
A signed identity is three strings, produced on your server and handed to the widget.Payload claims
Keep every claim value ASCII. Verification decodes the payload as plain base64 with no UTF-8 step, so a non-ASCII name or email address still verifies but arrives with mangled characters.
The gotcha: sub must equal channelUserId
Verification rejects any signature whosesub claim differs from the widget’s channelUserId. This is the most common integration failure.
The widget’s channelUserId is the anonymous ID that embed.js stores in the host page’s localStorage under the key tars_anonymous_id. Read that value on the page, send it to your server, and sign it as sub. Do not sign your own internal ID: the subjects will not match, and verification fails silently for the end user.
The widget adopts that anonymous ID only for a fresh session. A widget session that already carries an end-user record or a session token keeps its own channelUserId, which is a generated value the host page cannot read. Check this first when verification returns subject_mismatch for a returning end user.
Signing example
Signing keys and rotation
Manage the key under Settings → Integrations, in the Identity Verification panel. Generate signing key creates the key, and the panel shows its key ID, masked secret, and created date. Regenerate issues a new key immediately. The old key keeps verifying for 24 hours, so rotate your server secret inside that overlap window. Signatures made with a key retired longer than 24 hours are rejected.Verified data wins
Fields attached through a valid signature are verified. Unverified browser data (plainTarsConfig values) can never overwrite them. A verified match can also merge an anonymous profile into the identified one, keeping earlier conversation history.
