- Home
- Case studies
- A loan-agreement breach detector that keeps data inside the law firm
A loan-agreement breach detector that keeps data inside the law firm
The law firm can process 200 agreements in one afternoon without clients’ personal data leaving its own server. A deterministic rules engine makes the finding, not the model.
4 min read
- Client
- Law firm
- Industry
- Law / LegalTech
- Scope
- Invoices, reports and documents
- Delivery time
- 4 business days
Who the client was and what was getting in the way
A law firm. After a high-profile ruling concerning loan agreements, a wave of claims followed and agreements from new clients began arriving at the firm. Each is dozens of pages long, but only a handful of figures and clauses really matter. Manually reviewing one agreement takes a lawyer a considerable amount of time. With this volume, the choice was simple: find a way to screen cases quickly or start turning clients away.
The obvious idea is to put an agreement into a language model and ask whether it contains a breach. At a law firm, however, that idea runs into four problems.
First and most important: the firm’s clients’ personal data must not leave its server. A loan agreement contains a person’s name, national ID number (PESEL), address, bank-account number, and full history of liabilities. Sending that information to an external service is unacceptable in this context, and no assurances from a provider change that. This was a hard boundary for the whole project, not a preference to consider.
Second, the finding has to be reproducible. A lawyer cannot base a court filing on the model deciding that an agreement contains a breach this time. The same agreement must produce the same result every time, and the result must be explainable.
Third, agreements arrive in every format: clean PDFs, scans from a printer, and photos taken on a phone. The system has to handle them all and distinguish a loan agreement from an unrelated file someone uploaded by mistake.
Fourth, the law changes. After new rulings, the detection logic needs to be updateable within hours, not only as part of the next major rebuild.
What we built
We began with where the system would run. We host everything on the law firm’s own server in containers managed with Docker and Coolify, so nothing needs to leave the premises. The firm can connect its own model subscription, which means it is tied neither to one provider nor to our account. The agent layer supports four different model engines, including Claude and Codex; switching between them is a settings change, not a rewrite.
Before anything reaches a model, the agreement is processed locally. Scans and photos go through OCR so they can be read. Personal data is then anonymized on the server. The agent sees only amounts, dates, and clause excerpts. It cannot see whose agreement it is because that information is irrelevant to assessing a breach. This is the core of the implementation: personal data stays on the law firm’s server, and the model works with material that cannot be used to identify the client.
We deliberately kept the final finding outside the model. A separate deterministic rules engine detects breaches and calculates the claim using the figures the agent extracted from the document. The model reads the agreement; it does not act as a judge. This makes the result reproducible. When case law changes, we update the rules rather than the prompts. The calculator gives the claim value in three scenarios, from the most cautious to the most favorable, so the lawyer sees a range rather than one unexplained figure.
A task queue in Supabase coordinates batch processing. We also added document generators and case-law monitoring, with rule updates after new rulings.
How it works today
It took four business days to build a working version. The law firm uploads agreements through a browser, individually or in a batch. Each follows the same path: the system checks whether it is a loan agreement, runs OCR if needed, anonymizes it, extracts the figures, evaluates the rules, and calculates the claim in three scenarios. The lawyer receives a list of cases with findings and ranges instead of a pile of files to read.
Everything runs on the law firm’s server. We built this as a product from the start, not a one-off implementation, so deploying the same system at another law firm is repeatable.
The outcome
The system processes 200 agreements in one afternoon. Zero personal data leaves the law firm’s server. The calculator estimates claim values in three scenarios. Users can upload PDFs and scans because OCR is built in.
What we would do differently
We underestimated input quality most of all. A phone photo taken at an angle, with a shadow across half the page, can throw off OCR; then even the best rules have nothing reliable to work with. Next time, from day one we will make it clear when a document cannot be read and needs to be uploaded again at better quality, rather than letting the system guess.
The second lesson concerns anonymization. Separating it into its own step before the model was the right decision, but we added tests for that step later than we should have. In a system whose central promise is that data never leaves the server, this step deserves its own test suite from the very beginning. Finally, legal rules should be stored from the outset in a format a lawyer can review and challenge without our help.