Skip to main content

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
Identifying users is the second-most-important thing you can do after installing the widget.

identify()

Call identify() to associate the current session with a user:
string
required
Your system’s unique identifier for this user.
UserTraits
Key-value attributes. See User Traits for recommended fields.
{ userToken?: string }
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.
Behavior:
  • 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 identity is provided, the new userToken replaces the one from init() for all subsequent requests

identifyCompany()

Call identifyCompany() to associate the user with a company:
string
required
Your system’s unique identifier for this company.
CompanyTraits
Key-value attributes. See Company Traits.
Behavior:
  • 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

Call identify() as soon as you know the user’s identity — typically right after login:
You can call 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 only identify() 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 outreach campaigns until you sync them. That gap is expected, not a Halo bug. Fix it by identifying from your signup server handler in addition to the widget on login. Recommendation: In the same code path that persists the new user and team (where you already fire analytics webhooks), call: Use your publishable widget key (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.
Keep widget 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 outreach audiences filter on Role or Signed Up, see Outreach 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:

JWT payload

The JWT must include user_id and may include any additional claims:
  • user_idrequired, must match the userId passed to Halo
  • exp — 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 as userToken when initializing. Always include userTraits with at least name and email so users show up correctly in your inbox:
Passing userId and userToken alone is not enough to show user names in your inbox. You must also include userTraits with name and email, or call identify() separately.

Token refresh (SDK 0.9.0+)

Prefer getUserToken to fetch fresh JWTs from your backend when the init token expires:
You can also rotate manually with identify(userId, traits, { userToken: newToken }). See Identity Verification for the full guide.

Best practices

  1. Always pass name and email in either userTraits (during init) or via identify(). Without them, users show as “Anonymous” in tickets and conversations.
  2. Set exp to match your session length (e.g. 24 hours). Shorter-lived tokens are more secure.
  3. Refresh the JWT with getUserToken or identify(..., { userToken }) when your session refreshes.
  4. Wire onIdentityExpired if you use enforce mode, so stale tokens refresh before users lose chat access.
  5. Include useful claims — the more claims (email, role, plan, company_id), the more personalized the AI without extra database lookups.
The JWT must be signed on your server using the Identity Secret. Never expose the secret in client-side code — anyone could forge tokens and impersonate users.
For a deeper dive on identity verification with full server-side examples, see Identity Verification.

Anonymous users

If you don’t call identify() 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)
You can still use 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.

Outreach Troubleshooting

Missing contacts before first login, 0 matched audience filters, and trait gaps.

Company Traits

Recommended fields for companies.