Customer 360
A blueprint for cross-system customer intelligence: accounts, contacts, contracts, tickets, opportunities, invoices, and the conversations that connect them. Built for the case where a sales engineer or AE asks "what's open with Acme right now?" and the answer has to span the CRM, the ticketing system, and the billing system.
The graph
| Node | Key | Source |
|---|---|---|
Account |
Id |
CRM (Salesforce, HubSpot, …) |
Contact |
Email |
CRM |
Opportunity |
Id |
CRM |
Ticket |
Id |
Support (Zendesk, Jira Service, Curiosity-internal) |
Contract |
Id |
CLM (DocuSign CLM, Ironclad, …) |
Invoice |
Number |
Billing (Stripe, NetSuite, …) |
Team |
Name |
SSO-mapped |
What this demonstrates
- Multi-source ingestion — five different sources unified by
Account.Idas the canonical key. - Account-centric navigation — every other entity has a one-hop path back to its
Account. - Graph-scoped chat — the AI assistant can be told to "stay on Acme" and the tool calls automatically scope to that account's subgraph.
- Cross-system aggregations — count open tickets, ARR by contract, days-since-last-meeting.
Retrieval
- Text search on
Account.Name,Contact.Email,Opportunity.Name,Ticket.Subject— sales reps search by name. - Hybrid search on
Ticket.Bodyand meeting notes — for "what was the last conversation about renewal?" - Graph-scoped:
Account → Out(...)produces the target set for any account-scoped search.
AI
An Account assistant scoped to a single account per chat session. Tools:
GetAccountSummary(accountId)— high-level: open tickets, active opportunities, ARR, days-since-last-touch.SearchInAccount(accountId, query)— hybrid search within the account's subgraph.RecentActivity(accountId, days)— events across all systems sorted by time.
Prompt template: "You are the Account assistant for . Answer using only the cited results from tools. If the user asks about a different account, ask them to switch context."
Permissions
Sales teams typically have read access to their assigned accounts only. Implement this by:
- Mapping CRM account ownership to a Workspace
_AccessGroupper region or per AE. - Restricting
Accountnodes withRestrictAccessToTeamat ingestion time. - All children inherit ACL through
OwnedByownership edges from theirAccount.
Customer Success and Support teams get broader read access via additional _MemberOf edges.
Connector
Run as one connector per source:
- Salesforce connector —
Account,Contact,Opportunity. Cursor onLastModifiedDate. - Ticketing connector —
Ticket. Cursor onupdated_at. - CLM connector —
Contract. Often slow-moving; daily full refresh is fine. - Billing connector —
Invoice. Daily full refresh.
Each connector is independent, runs on its own schedule, and identifies the canonical Account by a known cross-system mapping (commonly the CRM ID stored as a foreign key in the other systems). Reconcile orphans nightly.
Deployment
- Mid-size B2B has hundreds of accounts, thousands of contacts, tens of thousands of tickets — comfortably handled by a single 16 GB workspace.
- Connector failures should alert the account team, not just the platform team — surface them in Slack via a custom endpoint that posts a daily digest.
- Cross-system identity drift is the #1 operational pain. Schedule a reconciliation task and surface mismatches.
Common pitfalls in this domain
- Duplicate accounts from cross-system imports — the most common ingestion bug. Pin the canonical source (usually CRM) and resolve foreign IDs against it.
- Stale data feeling fresh — display a "last synced" timestamp per source so users know when the data was current.
- Over-broad permissions — sales reps shouldn't see all accounts by default. Audit
_MemberOfregularly.
Related
- Connectors and Ingestion pipelines for the multi-source ingestion patterns.
- Access Control Model for ReBAC against multi-team ownership.
- Technical support — narrower scope, similar shape.