Docs
Identify your users
Out of the box, every chat reaches your inbox as “Visitor 42”. One global object turns that into a name, an email, and anything else your app already knows — their plan, their last order, how long they have been a customer.
Concept
How it works
The widget gives every browser a visitor id the first time it loads and keeps it in localStorage, so the same person is recognised across pages, tabs and return visits. That is enough to thread a conversation together — but not to know who they are.
You supply the “who”. Your site sets window.helpcabUser with whatever it knows about the signed-in user. The widget reads it when it boots, again a few seconds later, on every client-side route change, whenever the chat is opened, and on its background heartbeat. It only sends something when the values actually changed — so there is no ordering rule to get right, and no traffic when nothing moved.
- Your page sets
window.helpcabUser— before or after the widget script, it makes no difference. - The widget attaches those details to this browser's visitor record.
- Your inbox shows the person's name instead of a number — immediately, even mid-conversation.
Recommended
The one-line way
Drop this anywhere your app renders for a logged-in user. It works on every stack, and it is the only method with no load-order rules at all.
<script>
// Set this wherever your app knows who is signed in.
// Leave it unset for logged-out visitors.
window.helpcabUser = {
userId: "usr_123", // your own user id
email: "[email protected]",
name: "Jane Doe",
plan: "pro" // any extra key shows up in your inbox
};
</script>userId, email, name and phone — plan, company, order count, signup date — is stored as a custom attribute and shown to whoever answers the chat.Do not set it at all for logged-out visitors. They stay anonymous, which is the correct behaviour.
Framework
React & Next.js
Set it in an effect keyed on your session so it follows sign-in, sign-out and account switches. Render the component once, high in your tree.
"use client"
import { useEffect } from "react"
// Runs again whenever the session changes — sign-in, sign-out, account switch.
export function HelpcabIdentity({ user }) {
useEffect(() => {
if (!user) {
// Logged out: forget the previous person so the next one on this
// browser doesn't open the widget onto someone else's conversation.
window.ChatWidget?.reset()
window.helpcabUser = undefined
return
}
window.helpcabUser = {
userId: user.id,
email: user.email,
name: user.name,
plan: user.plan,
}
}, [user])
return null
}This is safe with strategy="afterInteractive", where your effect usually runs before the widget script has even downloaded. The widget reads the global when it arrives.
Framework
Server-rendered sites
Rails, Django, Laravel, WordPress, Shopify — print the object straight into the page. Always JSON-encode the values; a name containing an apostrophe will otherwise break the page.
{% if customer %}
<script>
window.helpcabUser = {
userId: {{ customer.id | json }},
email: {{ customer.email | json }},
name: {{ customer.name | json }},
orders: {{ customer.orders_count | json }}
};
</script>
{% endif %}@auth
<script>
window.helpcabUser = @json([
'userId' => auth()->id(),
'email' => auth()->user()->email,
'name' => auth()->user()->name,
'plan' => auth()->user()->plan,
]);
</script>
@endauthNo JavaScript
Script tag attributes
If your platform only lets you paste one tag and interpolate values into it, put the identity on the tag itself.
<script
src="https://helpcab.com/chat-widget.js"
data-widget-key="YOUR_WIDGET_KEY"
data-user-id="usr_123"
data-user-email="[email protected]"
data-user-name="Jane Doe"
data-user-attributes='{"plan":"pro","mrr":49}'
defer></script>These are read once, when the widget loads. For anything that can change while the page is open, prefer window.helpcabUser.
Reference
JavaScript API
// Safe to call at any time — before or after the widget finishes loading.
// Calls made early are queued and replayed, never dropped.
window.ChatWidget.identify({
userId: "usr_123",
email: "[email protected]",
name: "Jane Doe",
plan: "pro",
})
// On logout — starts a fresh, anonymous visitor.
window.ChatWidget.reset()| Method | What it does |
|---|---|
| ChatWidget.identify(user) | Attach a name, email, phone, userId and any extra attributes to this visitor. |
| ChatWidget.reset() | Forget the current visitor and start a fresh, anonymous one. Call this on logout. |
| ChatWidget.open() | Open the chat panel — wire it to your own “Contact support” button. |
| ChatWidget.close() | Close the chat panel. |
| ChatWidget.toggle() | Open if closed, close if open. |
| ChatWidget.sendMessage(text) | Send a message as the visitor. |
| ChatWidget.track(event, data) | Record a custom event against this visitor. |
| ChatWidget.getVisitor() | Read back the current visitor id, conversation id and identity. |
Important
Logout & account switching
A visitor record lives in the browser, not in your session. On a shared computer — a family laptop, a support desk, a kiosk — that means the next person can open the widget onto the previous person's conversation unless you say otherwise.
window.ChatWidget.reset()If a different email or user id turns up without a reset — a straight account switch — the widget notices and resets itself. Calling reset() on logout is still the right thing to do, because a logged-out browser has no new identity to compare against.
Result
What your team sees
Once identity is flowing, every conversation carries:
- The person's name and email in the conversation list, the chat header and the profile panel — no more “Visitor 42”.
- Their phone number, if you passed one.
- Every extra key under “Details from your app” in the profile panel, next to their location, device and page history.
- All of their devices as one conversation — see One person, many devices.
All of it is available over the API too, at GET /api/v1/visitors.
Result
One person, many devices
A visitor record is a browser, not a person. The same customer on a laptop, a phone and an incognito window is three records — which is why, in every live chat tool, one person turns into three unrelated conversations and nobody can follow the story.
Identity fixes it. Once two browsers report the same email (or the same userId), Helpcab links them and your inbox shows one row for the person, marked 2 chats. Opening it gives you every message they have ever sent you, in order, with a divider where a new session began. Your reply goes to the device they used last, so it reaches them where they actually are.
You do not have to do anything for this beyond passing the email. It applies to conversations you already have, too.
Shortcut
Let AI do it
Paste this into whatever coding assistant you already use — Claude, Cursor, Lovable, ChatGPT, Bolt, Replit. It wires the identity into your existing auth code rather than inventing a new user object.
My site already has the Helpcab chat widget installed:
<script src="https://helpcab.com/chat-widget.js" data-widget-key="YOUR_WIDGET_KEY" defer></script>
Right now every chat reaches my support inbox as an anonymous "Visitor 42". Change that: make the site tell Helpcab who the logged-in user is.
What to do:
Set a global object called window.helpcabUser wherever my app knows the current user — the root layout, the auth provider, or the server-rendered page template. Load order does not matter; the widget reads it before and after it loads, and picks up changes.
window.helpcabUser = {
userId: "<my app's user id>",
email: "<the user's email>",
name: "<the user's full name>",
plan: "<optional: any extra detail a support agent would want>",
}
In React or Next.js, use an effect keyed on the session so it updates on sign-in and sign-out:
useEffect(() => {
if (!user) {
window.ChatWidget?.reset() // logout: don't leak the last user's chat
window.helpcabUser = undefined
return
}
window.helpcabUser = { userId: user.id, email: user.email, name: user.name }
}, [user])
Rules:
- Wire it into my EXISTING auth/session code — do not invent a new user object or hardcode an example email
- Do not set window.helpcabUser for logged-out visitors
- Call window.ChatWidget?.reset() on logout
- Add 2 or 3 extra keys that would genuinely help a support agent (plan, company, signup date) — every extra key shows up in the inbox
- Never pass passwords, tokens, API keys or payment details
Then tell me which files you changed and how I can verify it worked.Replace YOUR_WIDGET_KEY with the key from your dashboard — it is in the install snippet you already pasted:
<script src="https://helpcab.com/chat-widget.js" data-widget-key="YOUR_WIDGET_KEY" defer></script>Reference
Limits & security
- Up to 40 custom attributes per visitor, 500 characters per value. Objects and arrays are stored as JSON.
- Emails are lowercased and validated; an address that is not one is ignored rather than stored.
- Identity sticks to the visitor record, so a returning customer is still recognised on their next visit.
Help
Troubleshooting
Still seeing “Visitor 42”?
- Open your site and type
window.helpcabUserin the browser console. If it isundefined, your app is not setting it on that page — check that you are logged in and that the code runs on the client, not only on the server. - Check the object has a real
emailorname. An object of only optional extras has nothing to identify. - Run
window.ChatWidget.getVisitor()— it returns what the widget currently believes about this person. - Conversations that started before you added identity keep the name they had. Start a new chat to see the change.
Still stuck? Open the chat in the corner of this page — we answer it ourselves.