Signed Identity
Verify logged-in users via HMAC-SHA256 to prevent impersonation and link conversations to real accounts.
Why Signed Identity?
Without identity verification, anyone could pass arbitrary user data to the widget — an attacker could impersonate your users by setting a fake id or email. Signed identity solves this by requiring a server-generated HMAC hash that proves the identity data hasn't been tampered with.
When signed identity is configured, conversations are permanently linked to your user accounts. Agents see verified user information in the inbox, and returning users see their full conversation history across devices and sessions.
How It Works
- Your backend computes an HMAC-SHA256 hash of the user's
idusing your workspace's Identity Secret Key. - You pass the
id, optional metadata (name,email), and thehashto the widget via theidentityobject. - When the widget connects, it sends the identity object to the
VerifyIdentityendpoint. - The server recomputes the hash using the same secret key. If it matches, the identity is accepted. If not, the user is treated as anonymous.
Computing the HMAC Hash
The hash is an HMAC-SHA256 digest of the user's id (as a UTF-8 string), keyed with your workspace's Identity Secret Key. The output should be hex-encoded (lowercase).
zt_isk_ and must never be exposed to the browser. Find it in Settings → API Keys → Identity Secret.Node.js
import crypto from "node:crypto";
function computeIdentityHash(userId: string, identitySecret: string): string {
return crypto
.createHmac("sha256", identitySecret)
.update(userId, "utf8")
.digest("hex");
}
// Usage
const hash = computeIdentityHash(user.id, process.env.ZAPTICKET_IDENTITY_SECRET);
// Pass this hash to your frontend alongside user.idC# / .NET
using System.Security.Cryptography;
using System.Text;
public static string ComputeIdentityHash(string userId, string identitySecret)
{
var key = Encoding.UTF8.GetBytes(identitySecret);
var message = Encoding.UTF8.GetBytes(userId);
using var hmac = new HMACSHA256(key);
var hash = hmac.ComputeHash(message);
return Convert.ToHexString(hash).ToLowerInvariant();
}Python
import hmac
import hashlib
def compute_identity_hash(user_id: str, identity_secret: str) -> str:
return hmac.new(
identity_secret.encode("utf-8"),
user_id.encode("utf-8"),
hashlib.sha256
).hexdigest()Ruby
require 'openssl'
def compute_identity_hash(user_id, identity_secret)
OpenSSL::HMAC.hexdigest('sha256', identity_secret, user_id)
endPHP
function computeIdentityHash(string $userId, string $identitySecret): string {
return hash_hmac('sha256', $userId, $identitySecret);
}Passing Identity to the Widget
Once you've computed the hash on your server, pass it to the client-side embed configuration:
<script>
window.ZapTicket = {
siteKey: "zt_pub_abc123",
identity: {
id: "user_12345", // Your internal user ID
name: "Jane Doe", // Optional: display name
email: "[email protected]", // Optional: email for agents
hash: "a1b2c3d4e5f6..." // HMAC-SHA256 hex digest
}
};
</script>
<script async src="https://cdn.zapticket.app/widget.js"></script>The VerifyIdentity Endpoint
When the widget opens a connection with an identity object, it calls the following endpoint to validate the hash before establishing a verified session:
POST /widget/verify-identity
Content-Type: application/json
{
"siteKey": "zt_pub_abc123",
"id": "user_12345",
"name": "Jane Doe",
"email": "[email protected]",
"hash": "a1b2c3d4e5f6..."
}The server performs the following steps:
- Looks up the workspace by
siteKey. - Retrieves the workspace's Identity Secret Key.
- Computes
HMAC-SHA256(identitySecret, id)and compares it to the providedhash. - If the hashes match, returns a session token linked to the verified identity.
- If the hashes don't match, the identity is rejected.
What Happens on Invalid Hash
If the hash verification fails, the widget does not show an error to the visitor. Instead, it silently falls back to anonymous mode:
- The conversation is created without a linked user identity.
- Agents see the visitor as "Anonymous Visitor" with a generated session ID.
- The conversation is not linked to any previous history for that user.
- A warning is logged to the browser console:
ZapTicket: identity verification failed, continuing as anonymous.
id than what was used to compute the hash.Security Considerations
- The Identity Secret Key must only exist on your server. Never bundle it in client-side code, environment variables exposed to the browser, or mobile app bundles.
- The hash is computed only over the
idfield. Thenameandemailare informational and not verified cryptographically — they can be updated freely. - Rotate your Identity Secret Key from Settings → API Keys if you suspect it's compromised. Existing sessions remain valid until they expire.
- Identity verification is optional. If you don't set the
identityobject, all visitors are treated as anonymous with session-based tracking.
Testing Identity Verification
To verify your implementation is working:
- Set the identity object with a valid hash.
- Open the widget and send a message.
- In the dashboard inbox, the conversation should show the user's name and email instead of "Anonymous Visitor".
- To test failure: change one character in the hash. The widget should fall back to anonymous mode (check the console for the warning).