# Assembling a Customer 360, end to end

A Customer 360 is a read, not a report: it resolves one Person, gathers every identity that points at them, and pulls their conversations, calls, deals, demos, cases, onboarding, follow-ups and memory into one view. No provider is called and nothing is written. The engineering question is not whether it works but how many database statements it costs.

- **Status:** Available
- **Audience:** both
- **In the app:** #/relationships, #/timeline
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/workflows/customer-360-assembly/

## The stage chain

1. Trigger — somebody opens a person, or asks the Assistant for `customer_360` on one.
2. User event — the request names exactly one person; this view is never a list.
3. Authentication and workspace resolution — the workspace is fixed before a single row is read, and the same service answers for the operator and for a customer through two thin routers.
4. Ingest — the person identifier, and nothing else. There is no free text to interpret.
5. Canonical record — the Person, plus every Identity that resolves to them. Identities are the join key: they are what makes a WhatsApp thread and an email thread the same person's history rather than two strangers'.
6. Reasoning — none. Assembling a 360 is a query problem, and treating it as a model problem would make it slower, more expensive and less accurate at once.
7. Knowledge, memory and rules — what Connect remembers about this person is included to a budget of roughly 700 characters. Knowledge is about the business rather than the person, so it is not part of this view.
8. Autonomy and approval — not consulted. A read changes nothing, so there is nothing to approve.
9. Action and provider — no provider is contacted. Every source is the workspace's own store, which is why the view is as fast as its queries and no faster.
10. Result — one view: identities, company, lifecycle stage, conversations, calls, deals and demos, cases, onboarding, open follow-ups, memory.
11. Relationship and timeline — the timeline is assembled separately, in the order the customer actually experienced things, which is the part that turns a set of lists into a history.
12. Audit and usage — no decision-log entry, because no decision was taken. Where a model was involved in answering a question about the person, that model call is metered against the workspace's budget.

## Every source it reads

| Source | What it contributes | Why it cannot be skipped |
|---|---|---|
| Person and identities | Who this is, on every channel | Without identities, channel history fragments into separate strangers |
| Company | The organisation and the colleagues on it | A case from a colleague is context, not noise |
| Lifecycle stage | Where the relationship stands | It decides how everything else should be read |
| Conversations and calls | What has actually been said, on which channel | The single most common reason an answer looks ignorant |
| Deals and demos | What was sold, tried, won or lost | Commercial history changes the tone of a support answer |
| Cases and onboarding | What has gone wrong, and what is still being set up | A recurring problem only reads as recurring here |
| Follow-ups | What is still owed to this person | A promise made last week is part of today's answer |
| Memory | What Connect knows about them, to a budget | Preferences and corrections that no single message carries |

## The cost, measured properly

Query cost here is measured in **statements**, not in wall-clock seconds, because a threshold in seconds measures the machine the test ran on. Statements are a property of the code, and they are what grows when a workspace does.

The failure this discipline catches is the query that costs one round trip per row. The operator's customer list fetched the user, the workspace and the entitlement separately for every membership: 1,857 accounts cost **5,574 statements**, and three inner joins said the same thing in **3**. Home ran three extra statements per thread across its newest 200 open threads — 40 conversations cost **133**, and three set-based statements made it **15**, flat regardless of how many there are.

> **Careful** A per-row query is invisible in development, where a test workspace has eleven people in it, and arrives as a production incident the month a customer's list gets long. Counting statements is how a defect that grows with the business is caught before the business grows.

## What can go wrong with the view

**Half the history is missing** — The person has a second record. Identities are the join key, so an unrecognised address builds a second history rather than extending this one. Merge them.
**A headline number disagrees with the list** — Counts derived from a page of rows rather than from the whole set stop at the page size. `prospect_summary` once reported 500 organisations on a 540-contact workspace, and zero already contacted when the real figure was 40.
**The company section is empty** — The person is not attached to a company. That is a relationship gap rather than a rendering fault.
**It feels slow on a large account** — That is a statement-count question, not a hardware one. The fix is set-based reads, and it has been the fix every time here.

## How it differs from the timeline and the summary

- **Customer 360** is the structured view: every category of record about one person, in sections you can scan.
- **The timeline** is the same material in one chronological sequence, which is what you want when the question is *what happened, in what order*.
- **The relationship summary** is prose written from both, with names scrubbed on the way out, for when somebody needs the shape of an account rather than its rows.
- All three read the same store. None of them is a cached copy, which is why none of them can disagree with the others.

## Questions

### Does building a Customer 360 call a model?

No. The view is assembled from the workspace's own stores by queries. A model is involved only when somebody asks a question about the person, and that call is metered separately.

### Why measure database statements instead of load time?

Because seconds measure the machine the test ran on, and statements measure the code. A query that costs one round trip per row looks instant on a small workspace and becomes an incident on a large one.

### Why does the 360 not show a conversation I remember?

Most often because that conversation is attached to a second record for the same human — the address it arrived from resolved to a different identity. Merging brings both histories onto one person.

## Related

- [Customer 360](https://connectbyjbrh.com/docs/relationships/customer-360/)
- [The customer timeline](https://connectbyjbrh.com/docs/relationships/timeline/)
- [The relationship summary](https://connectbyjbrh.com/docs/relationships/relationship-story/)
- [Building the timeline, end to end](https://connectbyjbrh.com/docs/workflows/timeline-assembly/)
- [Deciding whether two records are one person](https://connectbyjbrh.com/docs/workflows/identity-merge-decision/)
- [The query cost that grows with the business](https://connectbyjbrh.com/research/n-plus-one-that-grows/)

## What this page is based on

- `docs-source/sources/CHANNELS.md` §5 — `relationship_console`, `customer_360` and the measured query-cost defects
- `docs-source/sources/GENERAL.md` §11 — statement counts as the unit of measurement
- `docs-source/sources/GENERAL.md` §6 — the memory budget included in a person's context
- Connect capability registry (docs-source/facts.py) — `customer_360`, `timeline`, `cross_channel_identity`
