Identity Graph Overview
Audience: All teams (sales, management, QA, support, engineering) · Where in app: Admin → Identity (super-admin only); runs invisibly behind every chat/call · Plan availability: All plans (gated by an active subscription; super-admin can disable per tenant)
The Identity Graph stitches a visitor's identity together across visits so the AI agent — and the admin — sees a returning visitor as a known person, not a fresh anonymous session each time. Without it, every visit starts from zero: the AI re-asks "what's your name?" and has no idea the visitor booked a demo last week. With it, the AI greets returning customers by name, references their prior questions, knows their lifecycle stage, and skips qualification questions they already answered.
It is super-admin owned: customers don't normally see any configuration. It runs behind the scenes, assembling a context block that is injected into the AI's prompt every time it talks to someone the system recognizes.
What it does
- Recognizes returning visitors across sessions and devices, even after they clear cookies/localStorage.
- Pulls together everything a tenant knows about that person — identity, engagement history, recent pages, past conversations, CRM state, bookings, captured forms, and AI-extracted facts.
- Hands that context to the AI (chat agent, voice/call agent, outbound campaign agent) so responses are personalized and qualification isn't repeated.
- Maintains strict per-tenant isolation — Company A's view of a person never leaks to Company B, even though both recognize the same human.
- Captures website forms the visitor submits (name/email/phone/company) and uses them as a strong identity signal.
- Powers admin/Nox lookups — "what did John do this week?" answered across sessions.
Why it matters (sales/management framing)
- The AI doesn't ask a returning customer to re-introduce themselves.
- The AI knows the visitor already booked a demo and can reference it.
- The AI knows the visitor objected to pricing last time and can pre-empt it.
- Resolved identities flow to the CRM (HubSpot/GHL) as real contacts, and outcomes flow back. See CRM Integration.
How it works
The graph is built from three core records (full detail in Person vs Profile):
- Person — one global row per real human, matched across tenants by hashed email/phone (
match_keys). Tenant-agnostic; holds only basic identity. - PersonProfile — one row per
(Person × Company). This is where everything a single tenant knows about a person lives. Strictly sandboxed per company. - UserSession — a single visit. It belongs to a Person (once resolved) and a Company.
A visit flows through an ordered resolution chain (wired in backend/app/hooks.js boot()):
IdentityGraphRenderer → IdentityGraphConfig → SubscriptionGate
→ IdentityGraph → Verifier → PersonResolver → PersonWatch
- Config & subscription gate — load the effective config for the tenant; bail if the tenant has no active subscription.
- PersonResolver — match the session to an existing Person (by email/phone hash, CRM contact id, returning device, or anchor-on-session), or create one.
- Verifier — decide whether the session is verified or merely claimed (see below).
- Renderer — assemble the cross-session context block and inject it into the AI prompt (only for verified sessions).
- PersonWatch — fire any standing "notify me when X returns" directives.
The verification gate
The graph surfaces cross-session data only for verified sessions. An unverified ("claimed") session — someone who typed an email into chat but whom the system doesn't yet trust — sees only the current visit. The verification policy decides how strict the bar is:
| Policy | Score threshold | Use case |
|---|---|---|
permissive | Any signal verifies | Legacy / lowest friction |
moderate | Score ≥ 60 | Balanced |
strict | Score ≥ 80 | Default for new tenants |
regulated | Score path off — direct triggers only | Healthcare / finance |
See Verification Policy for the full mechanics and signal scoring.
What verified sessions see
The AI prompt context is assembled from toggleable sections (super-admin controls which appear). The AI is given verified names only — the identity section emits a name only when the session is verified (name_addressable); geo prefers the visitor's IP city over the enriched company city:
- Identity — name (verified only), email, phone, company, location
- Company Insights — domain, revenue band, employee count (when enrichment connected)
- Engagement — lead score, session count, pages viewed
- Current Session — current page, traffic source
- Recent Pages — last visited pages (deduped, cap default 5)
- Past Conversations — last chat/call summaries (cap default 5)
- Behavioral Signals — high-intent pages, products searched
- Bookings — meetings, tagged
[PAST]/[UPCOMING] - CRM State — lifecycle stage, deals, tags, notes (hourly-cached)
- Form Responses — captured form field answers
- Extracted Facts — AI-distilled facts (cap default 15)
- Suspected Identity — owner-facing "⚠ Suspected Identity (UNCONFIRMED)" block from shadow markers
- Company Contacts — Apollo colleague list; excluded from the default section set, opt-in via
enabled_sections
Full detail in Cross-Session Context.
What unverified ("claimed") sessions see
- Identity only if they typed it this visit
- The current session only
- Their own data captured this session
No past conversations, no CRM state, no extracted facts, no cross-session bookings.
Configuration & options
All configuration is super-admin owned under Admin → Identity. Customers have no knobs by default.
- Global defaults live in a single
identity_graph_defaultsdocument (theIdentityGraphDefaultsModelsingleton). Fields:enabled,enabled_sections[],custom_intro,max_recent_pages,max_past_conversations,max_extracted_facts,verification_policy,accept_inbound_call_verification,capture_website_forms. - Per-company override — set
companies.identity_graph_use_custom_config = trueand populatecompanies.identity_graph_configto override any field for one tenant. Otherwise the global defaults apply. - Subscription gate — independent runtime check; a trial-expired or cancelled tenant gets no graph context (cached 60s).
Full reference in Admin Controls.
For QA/support: if a returning visitor isn't recognized, check in order: (1) is the tenant's subscription active? (2) is the session verified or only claimed? (3) is the policy too strict for the signals present? (4) is the relevant section enabled globally?
Behaviors & edge cases
- Verified vs claimed is the single most important distinction. Verified = system trusts the identity and surfaces full cross-session memory. Claimed = visitor typed something but the system won't trust it across visits yet. Since the 2026-07 trust overhaul, self-identification in chat or call verifies directly (
verify_chat, reasonchat_identification); only genuine user sources (form / chat form / booking / inbound call / chat / call) can verify at all — enrichment and CRM auto-creates never can (first_profile_untrusted_source). - Self-provided outranks enrichment. Only high-trust sources (form, CRM, inbound call, booking, chat form) may overwrite canonical name/email/phone; enrichment (Apollo, then Snitcher below it) only fills empty fields. Junk/profane names are rejected by name moderation (
NameModeration.screen) and surface as "Unknown Visitor". - Suspected identity — cross-device IP/device-signature guesses land as shadow markers (
ip_shadow_person_id/device_shadow_person_id), shown as an amber "Suspected" badge and a "Suspected Identity (Unconfirmed)" panel. Never a trust tier, never greeted by name. - Identity graph resolves before enrichment. On session create the graph tries widget-id and device-signature linking first; Snitcher/Apollo only fire for still-unresolved, non-bot sessions — returning identified visitors don't burn enrichment quota.
- Same person across tenants — recognized as the same global Person, but each tenant only ever reads its own PersonProfile. No data crosses.
- Conflict guard — once a session is verified for one Person, a later claim matching a different Person is rejected (audited), not allowed to flip the session.
- Config changes apply going forward — already-assembled context isn't retroactively recomputed; the next conversation uses the new config.
- Subscription cache (60s) — a freshly cancelled tenant may briefly still get graph context.
Plan & limits
The Identity Graph is available on all plans, but always subject to:
- An active, non-expired subscription (trial-expired/cancelled → no graph context).
- Super-admin allowing it for the tenant (
identity_graph_allowed).
There is no per-seat or per-record price on the graph itself; it's part of the product. Verification strictness and which sections appear are operational settings, not plan tiers. (Plan-tier gating of specific identity features beyond the subscription check: verify with product.)
Technical implementation
- Models:
PersonModel(people),PersonProfileModel(person_profiles),PersonIpSignalModel(person_ip_signals),IdentityGraphDefaultsModel(identity_graph_defaults), plusUserSessionModel. - Services:
IdentityGraphConfigService,IdentityGraphSubscriptionGateService,IdentityVerifierService,PersonResolverService,PersonWatchService,IdentityGraphRendererService,IdentityGhlReconcileService,IdentityCleanupService. - Resolution chain is wired as an ordered subsystem in
backend/app/hooks.js. ms-ai consumes the assembled graph (via gRPC/HTTP) for prompt context and caches it ~30s, invalidated by the backend when identity changes.
For the engineering deep-dive — resolution chain internals, IP-assisted resolution, reliability history — see Identity Graph (subsystem).
What Nox can tell you
- Why a visitor is or isn't verified (signal breakdown vs policy threshold)
- What the AI saw in context on a specific session
- A cross-session timeline for a person
- How many visitors are verified vs claimed
Nox cannot change settings — super-admin edits directly.
Related
- Verification Policy — the four policies and signal scoring
- Form Intercept — how website forms feed the graph
- Cross-Session Context — what's in the AI prompt for a verified visitor
- Person vs Profile — the data-model split
- Admin Controls — super-admin configuration
- Identity Graph (engineering subsystem)
- CRM Integration