Docs

User Identification

Identifying users connects anonymous chat sessions with real user profiles.

How it works

When you call Respondo.identify(), the provided fields are attached to the conversation. All fields are optional — pass only the ones you have. For example, you can send just userId without email or name. Support agents see this data in the conversation details panel.

Parameters#

PropertyTypeRequiredDescription
emailstringoptionalUser's email address
namestringoptionalDisplay name
userIdstringoptionalYour internal user or display ID — shown as-is in dashboard
userHashstringoptionalHMAC-SHA256 signature for identity verification
metadataobjectoptionalKey-value pairs of custom fields (plan, company, etc.)
propertiesobjectoptionalCustom contact properties defined in Agent Settings. Keys must match property definitions. Values are stored with cp_ prefix and are filterable in the Inbox.
Example: Single Page Appjavascript
// After successful login
async function onLogin(user) {
  await authenticateUser(user);

  Respondo.identify({
    email: user.email,
    name: user.fullName,
    userId: user.id,
    metadata: {
      plan: user.subscription.plan,
      company: user.company.name,
      role: user.role,
      signedUp: user.createdAt
    },
    properties: {                        // custom contact properties
      account_type: user.accountType,    // must match keys from Agent Settings
      industry: user.industry,
      contract_tier: user.tier
    }
  });
}
With the standard install snippet you can call identify() at any time — calls made before widget.js finishes loading are queued by the snippet and replayed automatically once the widget initializes. Identity data is only dropped (with a console warning) if you load widget.js yourself and call identify() before ever calling init() — always call init() first in manual setups.

Anonymous Visitors#

If you don't call identify(), the widget automatically assigns a persistent visitor ID stored in localStorage (key respondoai_visitor_id). In the dashboard, the conversation appears as Guest · Web widget.

ModeDashboard displayHMAC required
No identify()Guest · Web widgetNo
identify({ email, name })Name + email shownNo — unless Identity Verification is enabled; then unsigned email/name are silently stripped and the session stays anonymous
identify({ userId })userId shown as-isNo — unless Identity Verification is enabled; then a valid userHash is required or the identity is stripped
identify({ userId, userHash })Verified user identityYes — cryptographically verified

When Identity Verification is enabled for the channel, any identify() carrying email or userId without a valid userHash is stripped server-side and the visitor remains anonymous — see Identity Verification (HMAC) below.

The visitor ID persists across sessions on the same browser. It is never sent to Respondo as an authenticated identity — it's only used for anonymous conversation continuity.

userId-only Identification (no email or name)#

If your platform doesn't have user emails or names — for example, you only have an internal display ID — you can pass just userId. No other fields are needed. The dashboard will show the userId as-is in conversation details.

userId + metadata (without email or name)javascript
// Your platform only has a display ID — that's enough
Respondo.identify({
  userId: user.displayId,      // e.g. "USR-4821" — shown in dashboard
  metadata: {                  // optional extra context
    plan: 'premium',
    region: 'eu-west'
  }
});
// No email or name needed — the widget works with just userId
To prevent spoofing of the userId, pair it with userHash (see Identity Verification below). Without HMAC, anyone can pass any userId from the browser console.

Google Tag Manager / deferred identify()#

When embedding via GTM, user data may not be available at page load. Two approaches:

Option A: localStorageKey (zero JS needed)javascript
// If your platform already writes the user ID to localStorage:
Respondo.init({
  agentId: 'YOUR_AGENT_ID',
  localStorageKey: 'myapp_user_id'  // reads localStorage.getItem('myapp_user_id') automatically
});
// No identify() call needed — the widget picks up the userId on its own
Option B: deferred identify() via dataLayerjavascript
// 1. Init widget immediately (GTM tag)
Respondo.init({ agentId: 'YOUR_AGENT_ID' });

// 2. Later, when user data appears (e.g. from dataLayer or your app):
var waitForUser = setInterval(function() {
  var uid = localStorage.getItem('myapp_user_id');
  if (uid && window.Respondo && typeof window.Respondo.identify === 'function') {
    clearInterval(waitForUser);
    Respondo.identify({ userId: uid });
  }
}, 500);
localStorageKey is only used if no userId is already set via identify(). Explicit identify() calls always take precedence.

Identity Verification (HMAC)#

Without verification, anyone could impersonate a user by passing a fake userId or email. Identity Verification uses HMAC-SHA256 to cryptographically prove the user's identity was set by your server, not by client-side code.

How it works#

  1. Enable Identity Verification in your channel settings — you'll get a secret key.
  2. On your server, compute HMAC-SHA256(secret, userId) — the secret is the key, the userId is the message — and send the result to the frontend. If you identify users by email only (no userId), sign the email instead: the signed payload is userId when set, otherwise email. If you pass both, sign the userId; it takes priority.
  3. Pass the hash as userHash in Respondo.identify().
  4. Respondo verifies the hash server-side. If invalid, identity is stripped and the user is treated as anonymous.
Never expose your secret key in frontend code. The HMAC must be computed on your backend.
Once verification is enabled, every identify() carrying userId or email must include a valid userHash — otherwise the identity fields are stripped and the visitor is treated as anonymous.

Server-side examples#

Node.jsjavascript
const crypto = require('crypto');

const SECRET = process.env.RESPONDO_IDENTITY_SECRET;

function generateUserHash(userId) {
  return crypto
    .createHmac('sha256', SECRET)
    .update(userId)
    .digest('hex');
}

// In your API endpoint:
app.get('/api/respondo-hash', (req, res) => {
  const hash = generateUserHash(req.user.id);
  res.json({ userHash: hash });
});
Pythonpython
import hmac, hashlib, os

SECRET = os.environ['RESPONDO_IDENTITY_SECRET']

def generate_user_hash(user_id: str) -> str:
    return hmac.new(
        SECRET.encode(),
        user_id.encode(),
        hashlib.sha256
    ).hexdigest()

# In your view / endpoint:
user_hash = generate_user_hash(request.user.id)
Gogo
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

func GenerateUserHash(userID, secret string) string {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(userID))
    return hex.EncodeToString(mac.Sum(nil))
}

Frontend usage#

With Identity Verificationjavascript
// Fetch the hash from YOUR server
const { userHash } = await fetch('/api/respondo-hash').then(r => r.json());

Respondo.identify({
  email: user.email,
  name: user.name,
  userId: user.id,
  userHash: userHash  // HMAC-SHA256 signature
});