Case study · Project Falcon

The model proposes. The engine computes. A person approves.

We built an agentic accounting platform on QuickBooks Online for a private equity portfolio — the close, the flux review, the cash forecast, the board pack. It ran in production against live client books. This is the architecture, the five constraints that had to exist before any of it was useful, and what we deliberately did not build.

We refer to the engagement as Project Falcon. The client is not named.

10 → 7 daysThe close cycle after implementation
≈24 hoursClose time returned each month
One write pathEvery write routed through one approval queue

The problem

A private equity finance team closes the same month, over and over, across entities that do not share a chart of accounts, a calendar, or a bookkeeper. No single instance of the work is hard. It is hard because it is the same work, repeated, under a deadline, by people who cannot all hold the whole picture at once.

That is a description of a private equity portfolio. It is also a description of a CAS practice with thirty clients on it. The shape of the problem does not change when you swap the word "entity" for the word "client".

What we built

One platform on QuickBooks Online, covering the month rather than a slice of it.

The close

Close cockpit · Flux review · Anomaly detection · Morning briefing

The money

13-week cash forecast · Two-lens covenant tracking · AR, AP and collections

The reporting

Financials and board reporting · FP&A · Cap table

The technical accounting

ASC 842 leases · ASC 606 revenue · ASC 326 CECL · ASC 205-40 going concern · Tax package and PBC binder assembly

Eleven scheduled routines run this on a timer. Everything that writes routes through one approval queue. There is no second path.

The five things that had to be true

Software that reads your books is a reporting tool. Software that writes to them is a different thing, and the difference is not a feature you add — it is a set of constraints you build before you build anything useful. These five came first.

01

The engine owns every number

Every figure that reaches the books is computed by a deterministic engine, in Decimal, quantized ROUND_HALF_UP. Before any row is emitted it is checked against a sum invariant — allocated amounts must equal the transaction price; the final row plugs the liability to the cent. If the invariant breaks, the code raises.

The model is not in that path. It can ask the engine for a figure and it can explain one. It cannot produce one.

02

It consumes human judgment. It never invents it.

Standalone selling price, measure-of-progress percentage, the locked lease rate, the ASC 606 and 842 criteria — every one of these is a required input, validated on the server. There is a flag in the codebase named ENGINE_SELF_EVALUATES_CRITERIA_FROM_TEXT. It is false for the life of the engine, and a test fails if anyone changes it.

The engine will not read a contract and decide whether revenue is recognised over time. A person answers that. The engine does the arithmetic that follows from the answer.

This is where most of the category gets it wrong. Reading a contract and forming a judgment is the part your licence covers. Computing a schedule from a judgment already made is the part that should have been automated a decade ago.

03

It drafts. Someone else approves.

No tool the model can call executes anything. A mutating call produces a proposal marked PENDING and stops. Authority to change a client's books lives in the backend, never in model output.

When a proposal is approved, it is approved by someone other than whoever prepared it — enforced in code, on all three approval lanes, not as a setting that gets switched off in a busy week. The executor claims an idempotency key before it posts, checks the period is open, and has a reconcile path for the case where a write times out and nobody knows whether it landed.

The failure mode of a bad inference is a bad draft, which somebody reads. Not a bad journal entry, which somebody finds in March.

04

An approval binds to one action, not to a conversation

When a person confirms an action, the confirmation is bound to that action by a SHA-256 fingerprint over the trusted scope and the payload, recomputed at the moment of execution and compared with a constant-time check. If anything about the action changed after it was approved, it does not run.

The scope keys — which company, which realm — are stripped out of anything the model touched and re-injected from trusted context. The model never supplies them.

This matters more than it sounds. The sentence "the user confirmed" is a thing a language model can produce. A fingerprint is not.

05

It has to be able to refuse

There are hooks in front of every path that writes, and a test suite whose only job is to confirm the system declines when it should. Absent or unreadable input returns nothing, or an explicit not-available, or a typed error. It never returns a fabricated zero.

The same rule governs what reaches a person. The tax package and the PBC binder assemble documents; they compute no tax, mark no requirement satisfied, and carry no confidence score — because a handoff that reads like an assurance opinion is worse than no handoff at all.

Most testing asks whether software does the thing. This asks whether it declines to, under the conditions where declining is correct. That is the harder question, and it is the one that matters when the input is a client's live books.

Where your clients' data goes

Firms ask this second or third, and most vendors answer it with a badge.

Inference runs on API-tier access, where both providers' terms state that customer data is not used to train their models. That is a contractual fact about somebody else's business, so it is worth re-checking at renewal rather than taking on faith.

What is ours, and checkable in the code:

  • The usage ledger records counts and costs. It never stores prompt or completion content — the platform's own database is not a shadow archive of what was sent.
  • Pipelines send computed metrics and structured sections, not whole ledgers. The board-commentary pass sees section JSON, not the books.
  • Financial payloads are redacted out of application logs, error logs included.
  • Keys are per tenant. A firm that wants its own provider relationship can bring one, and its data rides its own contract.

And what is deliberately absent: no fine-tuning on customer data, no reuse of prompts or responses across tenants, no cross-tenant retrieval anywhere in agent context. Nothing one client's books taught the system is available to another's.

Who decided this

Every approved action carries an evidence pack: what was proposed, on what basis, who approved it, and when. Tenant isolation is enforced in the application layer on every query, with row-level security across 97 tables underneath it as a backstop — so a scope someone forgot to apply returns nothing rather than somebody else's ledger.

None of that is there because a regulator asked. It is there because the first question anyone sensible asks about an automated journal entry is who decided it — and if the honest answer is "the software did", the system is not usable in a firm that signs its name to the number.

What we deliberately did not build

Two things this platform does not do, listed because the omissions describe it more accurately than the feature list does.

It does not learn across clients. Nothing observed in one entity's books influences the treatment of another's. There is real accuracy left on the table by that decision, and it is not close to worth the alternative.

It has no tax engine, no inventory engine and no FX engine. Those are specialist domains with their own failure modes and their own liability. It integrates with that work. It does not pretend to do it.

The honest version

The internal architecture document for this system grades its own layers. Some are marked mature. Some are marked recent — real, tested, and new enough that we say so. It carries an open backlog with named items on it.

That is not modesty. It is the same discipline as everything above: a system that will not emit a number it cannot stand behind gets built by people who write down what does not work yet.

If you are deciding whether to let somebody build something that touches your clients' books, that is the thing worth checking. Not the demo.

Results

The close went from ten days to seven — about 24 hours of close time returned every month.

It caught things people had missed: depreciation had been miscalculated, and leases were not being accounted for under ASC 842. Both had been through prior closes without being caught.

And one thing went wrong. The anomaly detection flagged ordinary-course transactions as anomalous — noise a person had to review and wave through — and the fix was unglamorous: it needed more training on what ordinary looks like, and got it. No guardrail failed. Every false flag was a draft somebody read, not an entry somebody found, which is the failure mode this system was built to have.

What this took

About 2,000 hours. We count the build in hours because hours are the honest unit — a calendar span says more about what else was happening than about the work. The order is the part worth repeating: the five constraints came first, and nothing useful was built until they held.

The bridge

We built this for a private equity finance team running a close across a portfolio of entities. A CAS team running a close across a portfolio of clients is doing structurally the same job: the same repetition, the same deadline, the same problem of nobody holding the whole picture. The constraints do not change. Neither does the argument for building rather than buying — a platform sold to you is a platform designed around somebody else's portfolio.

Want to know what this looks like against your book?

Start with a close teardown. Forty-five minutes on your own close — the checklist, the calendar, the entity you argue with most — and one written page about it afterwards. Free, five a month, and there is no obligation at the end of it.

Book a close teardown

We are looking for experts in other fields to build with →