Why identify
When you identify users and companies, the AI can:- Greet users by name and reference their plan, role, or any custom attribute
- Access company-specific context (integrations, events, plan details)
- Apply the right escalation rules based on user/company segments
- Show real names in your inbox instead of “Anonymous”
- Link conversations and tickets to the right contact record
identify()
Callidentify() to associate the current session with a user:
Your system’s unique identifier for this user.
Key-value attributes. See User Traits for recommended fields.
Optional. Pass a fresh signed identity proof (typically a JWT) at the same time as identify, e.g. when your app refreshes the user’s JWT. The new token is forwarded on every subsequent SDK request, so user-scoped endpoints can re-verify ownership without you reinitializing the widget. See Identity Verification.
- Traits are merged with any previously set traits (new values overwrite old)
- The user record is created or updated in the database
- All subsequent chat messages include these traits
- If
identityis provided, the newuserTokenreplaces the one frominit()for all subsequent requests
identifyCompany()
CallidentifyCompany() to associate the user with a company:
Your system’s unique identifier for this company.
Key-value attributes. See Company Traits.
- The company record is created or updated
- The current user is linked to this company
- Company traits are included in the AI prompt alongside user traits
- Traits are merged with existing data (new values overwrite)
When to call
Callidentify() as soon as you know the user’s identity — typically right after login:
identify() and identifyCompany() multiple times. Traits merge additively.
Authenticated apps with delayed first login
If signup and first session are separate (email verification, magic links, invite flows, OAuth onboarding later), calling onlyidentify() from the browser is not enough. Halo creates a contact after Stripe sync, SDK/server identify, or similar. Rows in your product database do not appear in Contacts or automations until you sync them.
| Event | Your app database | Halo (widget identify only) |
|---|---|---|
| User completes signup | User + team created | No contact yet |
| User verifies email and opens the dashboard | User active | Widget runs identify(), contact appears |
POST /api/sdk/users/identifyPOST /api/sdk/companies/identifywhen applicable
Authorization: Bearer ab_live_...). Pass signed_up_at as ISO 8601 from your product’s user created_at. Do not send created_at in traits; Halo sets that when the row is inserted.
identify() on every authenticated page load so traits stay fresh. Run a one-time backfill for accounts created during any gap before this shipped.
If automations filter on Role or Signed Up, see Automation Troubleshooting.
Identity verification (JWT)
For production, enable identity verification so users can’t impersonate each other. When enabled, every chat call must include a JWT signed with your Identity Secret on your server. The JWT can also carry trusted claims (email, role, plan, company_id) that Halo will use without a database lookup.How it works
Halo generates two separate keys when you set up your project:| Key | Prefix | Where it goes | Purpose |
|---|---|---|---|
| Widget key | ab_live_... | Frontend (browser) | Identify your workspace — publishable, safe to expose |
| Identity secret | ha_secret_... | Backend (server only) | Sign JWTs for identity verification |
JWT payload
The JWT must includeuser_id and may include any additional claims:
user_id— required, must match theuserIdpassed to Haloexp— optional but recommended (Unix timestamp)- All other claims — optional, used as trusted user data
Server-side: generate the JWT
Sign a JWT with your Identity Secret using HS256:Client-side: pass the token
Pass the JWT asuserToken when initializing. Always include userTraits with at least name and email so users show up correctly in your inbox:
Token refresh (SDK 0.9.0+)
PrefergetUserToken to fetch fresh JWTs from your backend when the init token expires:
identify(userId, traits, { userToken: newToken }). See Identity Verification for the full guide.
Best practices
- Always pass name and email in either
userTraits(during init) or viaidentify(). Without them, users show as “Anonymous” in tickets and conversations. - Set
expto match your session length (e.g. 24 hours). Shorter-lived tokens are more secure. - Refresh the JWT with
getUserTokenoridentify(..., { userToken })when your session refreshes. - Wire
onIdentityExpiredif you use enforce mode, so stale tokens refresh before users lose chat access. - Include useful claims — the more claims (email, role, plan, company_id), the more personalized the AI without extra database lookups.
Anonymous users
If you don’t callidentify() or pass userTraits, the user is treated as anonymous. The AI still works but:
- Conversations and tickets show “Anonymous” in your inbox
- The AI doesn’t have access to user-specific traits or company data
- Escalation filters and priority rules don’t match (they depend on user/company attributes)
setContext() to provide session-level context, but for the best experience, always identify users with at least name and email.
Where to go next
Send Context
Push structured state about the user beyond simple traits.
Identity Verification
Production setup with full server-side examples.
User Traits
Recommended fields and the full trait schema.
Automation Troubleshooting
Missing contacts before first login, 0 matched audience filters, and trait gaps.
Company Traits
Recommended fields for companies.