Back to Case Studies
Published:
Last Updated:
Recently Updated
Case StudyOne platform, many families, no shared data
Financial Services / SaaSMulti-Agent System
high priority

How a Multi-Family Office Made Its Own Documents Answerable

  • Invested, committed, funded and unfunded visible together, so a capital call stops being a surprise
  • Reconciliation between what the documents say and what the accounting says, rather than trusting one of them
  • Investment documents read into structured records automatically, so terms buried in PDFs became facts the system can answer with
  • Entities, companies, investments and documents linked in a graph, making multi-hop ownership questions answerable
  • One platform serving many families, rather than one deployment forked per client
  • Tenant isolation enforced structurally across 70+ endpoints and 60+ permission scopes, not by convention
14 weeks
3 engineers
11 min read
774 words
Muhammad Mudassir

Muhammad Mudassir

Founder & CEO, Cognilium AI

Outcome metrics

70+endpoints
Endpoints behind the tenant boundary
was isolation by convention before
60+scopes
Permission scopes across five roles
7specialist agents
Specialists behind one router, so the user picks a question not a tool
manyrather than one fork per client
Client families served from one deployment

The situation

A multi-family office answers a narrow set of questions for wealthy families: what do we own, what have we committed to but not yet funded, what falls due, and what did we actually agree in that document three years ago.

The second of those is the dangerous one. A commitment that has been signed but not yet called is a liability that does not appear in a bank balance, and it arrives on someone else's timetable. An office that cannot see its unfunded commitments at a glance is one capital call away from an avoidable liquidity problem.

The answers existed. They were spread across accounting software, a shared drive of private placement memoranda, subscription agreements and cap tables, a calendar, and an inbox. Assembling any one of them meant a person opening files and reading — which made the answers slow, and made them only as accurate as whoever last read the paperwork.

The harder constraint was structural. A platform serving several families cannot let one family's data touch another's, ever, for any reason. The usual industry answer is to run a separate deployment per client. That is safe and it does not scale: every improvement has to be shipped as many times as you have clients.

What we did about it

The platform answers questions in plain language, and the isolation is enforced by structure rather than by discipline.

Investment documents — private placement memoranda, subscription agreements, SAFEs, cap tables — are read automatically and turned into structured records, so the terms buried in a PDF become facts the system can answer with. Those facts are linked into a graph connecting companies, investments, entities and documents, which is what makes "what do we own through this entity" a question rather than an afternoon.

Seven specialists sit behind one router, so a person asks a question rather than choosing a tool. And each client organisation is bound only to the integrations it has actually connected — the system does not know the others exist for that tenant. Every one of the platform's 70+ endpoints sits behind that boundary, with 60-plus permission scopes across five roles. Cross-tenant access is not policed after the fact; there is no path that reaches it.

How it was built

The technical detail, for the person who will be asked whether this is sound.

Supervisor-router with tools bound per organisation

Every request hits TenantContextMiddleware which reads the immutable Firebase custom claim, fetches organizations/{orgId}/permissions, and attaches the merged context to request.context. The supervisor factory takes that context and assembles a fresh Agent: which specialists to register, which tools to bind to each, which system-prompt fragments to splice in. The output is cached by {orgId, integrations_hash} — cold path ~150ms (Firestore reads + tool wiring), warm. Pub/Sub invalidates the cache <1s after any integration change.

RBAC at two layers

Layer one (tool registration): the factory only binds tools the org is allowed to use. The LLM literally does not know the others exist — system prompt is shorter, hallucination cannot reach into a non-connected Salesforce. Layer two (per-tool permission check inside the handler): every tool starts with assert_permission(request.context, "salesforce:read"). Defense in depth — the factory layer can be bypassed accidentally; the tool layer is enforced last. Together they cover both gaps.

Document Intelligence pipeline

Parser (PDF / DOCX / XLSX text extraction) → Classifier (document type via Gemini 2.0 Flash) → Evidence Extractor (supporting text per field) → Extractor (structured field extraction via Gemini 2.5 Pro) → Validator (cross-field consistency: dates, party names, amounts) → Scorer (confidence per field) → Graph Writer (Neo4j upsert with cross-document entity linking). The pipeline handles PPMs, SPAs, SAFEs, and cap tables. Confidence below threshold triggers a human-review queue rather than silent low-quality writes.

Zero-trust multi-tenant Firestore

Every collection lives under organizations/{orgId}/. There is no top-level documents collection — only organizations/{orgId}/documents. A query that omits the orgId path segment fails at dispatch. The system has no way to "accidentally" query across tenants because the path itself enforces scope. 60+ permissions follow the format "{resource}:{action}:{scope}" — examples: "documents:read:org", "documents:*:org", "billing:read:platform". Five roles bundle them; wildcards expand at check-time, not at storage.

Real-time sync via webhooks + Pub/Sub

Gmail push notifications land in a Cloud Pub/Sub topic the backend subscribes to; new threads index into the per-org Vertex AI Search engine within ~1 second. Google Drive uses watch-channel webhooks with polling fallback (Drive's webhook reliability is good-but-not-perfect; the poll catches missed deliveries). QuickBooks uses scheduled syncs on Cloud Scheduler — 8 jobs cover financials, transactions, and entity reconciliation.

TL;DR

A family office's records lived across accounting, a document drive, a calendar and an inbox. How one platform made them answerable — for many families, from one deployment.

A family office's job is to know what a family owns and what it owes. That answer lived across QuickBooks, a document drive, a calendar and an inbox — and could only be assembled by a person opening files. Here is how the platform that serves those families made the question answerable without giving anyone access to a second family's data.
family office softwareunfunded commitments trackingcapital call managementinvestment document extractionmulti-family office platformcap table reconciliationportfolio reporting for family offices

The question a family office exists to answer

Strip away the sophistication and a family office does something simple: it knows what a family owns, what it has committed to, and what falls due. Every other service rests on that being reliably true.

In practice the answer was scattered. Accounting lived in one system, the paperwork that defines the commitments lived in a document drive, dates lived in a calendar, and the context for all of it lived in an inbox. Nothing was missing. It was simply that no one could see it at once.

So the answer to "what do we own through this entity" was not a lookup. It was an afternoon, and its accuracy depended on whoever last read the subscription agreement.

The number that causes the phone call

Of the questions a family office answers, the one that does damage is unfunded commitments — capital that has been legally committed to a fund or a deal but not yet called. It is not in the bank balance. It does not appear in a profit-and-loss statement. And it is drawn down on someone else's schedule.

An office that can see invested, committed, funded and unfunded in one place is managing liquidity. An office that has to assemble that view from subscription agreements when a call arrives is reacting to it. The same platform view that answers "what do we own" has to answer "what could be asked of us next quarter", or it has answered the easy half of the question.

Adjacent to it sits reconciliation: what the documents say a position is, against what the accounting says it is. Those two drift, quietly, and the drift is only ever found by someone comparing them. Making both readable by the same system is what turns that from an audit exercise into a view.

Why the paperwork is the hard part

The terms that actually govern a position are written in prose: a private placement memorandum, a subscription agreement, a SAFE, a cap table. They are the authoritative record, and they are unreadable at scale. A person extracting those terms by hand is slow and occasionally wrong, and neither failure announces itself.

So the documents are read automatically and turned into structured records. The effect is not that reading gets faster — it is that a term buried on page forty of a PDF becomes a fact the system can answer with, and can show its source for.

Those facts are then linked: this entity holds this position in this company, evidenced by this document. Ownership questions are rarely one hop deep, and a graph is what makes the second and third hop answerable instead of manual.

The constraint that shaped everything else

A platform serving several families has one absolute rule: one family's data must never reach another's, under any failure, ever. The comfortable way to guarantee that is to run a separate deployment per client.

It works, and it quietly caps the business. Every improvement ships as many times as you have clients, every client drifts onto a slightly different version, and the cost of serving the tenth family is the same as the first. The economics of software stop applying.

The alternative is to make isolation structural. Each organisation is bound only to the integrations it has actually connected — for that tenant, the others do not exist. All 70-plus endpoints sit behind that boundary, with more than sixty permission scopes across five roles. Cross-tenant access is not something the system detects and blocks; it is something the system has no route to.

Seven specialists, one question

Underneath, different questions need different competence — financial, legal, document, calendar, correspondence. Exposing that as five tools would push the work back onto the user, who now has to know which one to ask. A router in front of the specialists means a person asks their question and the system decides who answers it.

What transfers

If you are building software for an industry where each client's data is confidential from every other client's, you will be offered the per-client deployment early, and it will look like the responsible choice. It is worth understanding what it costs before you accept it — not in infrastructure, but in the rate at which you can improve the product for anyone.

The second lesson is about documents. In finance, law and insurance the authoritative record is prose, and most platforms treat it as an attachment — something to store and link to. Everything interesting starts when the contents of that prose become facts the system can reason over, with the document still attached as evidence.

Technologies used

Google ADK 1.15Gemini 2.0 FlashGemini 2.5 ProFastAPIPython 3.11Next.js 16React 19Neo4j AuraFirestoreVertex AI SearchCloud RunFirebase AuthTerraformCloud Pub/Sub

Share this case study

Frequently Asked Questions

Find answers to common questions about the topics covered in this article.

Still have questions?

Get in touch with our team for personalized assistance.

Contact Us

Want a result like this for your team?

Talk to an engineer about your AI system. We scope the engagement against the outcome you need, not the hours we want to bill.