Two-layer data model
Halo stores everything you know about your users in two related layers:| Layer | What it is | Lives in | Set via |
|---|---|---|---|
| Traits | Simple key-value identity attributes | end_users.custom_fields and companies.custom_fields | identify() / identifyCompany() or REST |
| Context | Rich, structured state data for the AI | end_users.context and companies.context | setContext() or chat request body |
Where you see them in the dashboard
- Contacts (
/dashboard/users) — every identified user with profile, traits, custom fields, conversation history, and activity feed. - Companies (
/dashboard/companies) — every identified company with profile, plan, integrations, health score, renewal info, and the linked users. - Settings > Data — the schema discovery view: every trait and context key Halo has ever seen, with optional descriptions you can set so the AI understands what each field means.
Data flow end-to-end
Your app sends data
From the browser via the Web Widget, from your backend via REST, or from connected integrations like HubSpot.
Data is stored
Traits land in dedicated columns (name, email, plan) and the JSON
custom_fields blob. Context lands in the JSON context blob.A chat message triggers a merge
When the user sends a message, the chat API loads stored data and merges it with any per-request context the SDK sent.
The AI sees structured context
The engine renders all of it into structured, human-readable text in the system prompt —
[User Profile], [Company: Name], [Connected Integrations] (integration), etc.Merge strategy
When the AI sees the user’s data on a chat call, it’s merged from up to four places in this order (later wins):- Stored user traits (
end_users.custom_fields) — base layer - Per-request
custom_fields— overrides matching keys - Stored user context (
end_users.context) — base context - Per-request
context— overrides matching context keys
- Stored company traits → 2. Request company traits (rare) → 3. Stored company context → 4. Request company context
What the AI sees
For a fully populated user, the AI’s system prompt includes something like:Companies and the user-company link
When you callidentifyCompany(), the current user is automatically linked to that company (foreign key end_users.company_id).
This means:
- The AI sees company traits and context alongside user traits on every message.
- You only need to call
identifyCompany()once per user — the link persists. - Multiple users at the same company share company-scoped data.
- Plan, MRR, lifetime value from Stripe
- Renewal date, contract terms from Stripe and PandaDoc
- Health score from your health scoring config
team_sizecomputed from the linked end users
Discovered attributes
Halo records every trait and context key you ever send. Settings > Data has tabs for User traits, Company traits, and Context that show:- The key name
- The data type
- A sample value
- An optional description you can write (used by the AI to understand what the field means)
acquisition_channel with the description “How the user found us — paid, organic, referral, or partner” tells the AI to interpret values in that exact set.
Where to go next
User Traits
Recommended and custom user attributes.
Company Traits
Recommended and custom company attributes.
Context Entries
Structured context types reference.
Examples
Real-world integration patterns.
Health Scores
How customer health is computed and used.
Renewals
Track contract terms and renewal dates.