Information extracted, one lease record.
The lease, the abstract and the fact sheet all describe the same property, and they do not always agree. Send them together, get one cited record back, and see exactly which document won each contested field.
One property. Four versions of the truth.
A commercial lease is never one document. There is the signed lease, an abstract someone typed up two years ago, a broker's fact sheet, and a spreadsheet the asset manager keeps. Each one states the rent. They do not all state the same rent.
So someone opens all four, finds the disagreements, decides which to believe, and types the answer into a system. That person is usually the only record of why the number is what it is. When they leave, the reasoning leaves with them.
- The fold is ordinary code running documented rules. The same files in the same order give the same record every time.
- Values are never blended. One value is taken whole, with its citation, from one document.
- Lists are not unioned. A list of charges comes from one document. Build the union downstream, where you can see it.
- Instructions shape the extraction, not the fold. To choose which document wins, choose the order you send them in.
The reconciliation is the work. Reading the documents is just how you get there.
Extract each document. Fold them after.
The order matters, and it is the reason the result is predictable. Every document is read on its own, then the results are folded by rule.
In:Lease, abstract, fact sheet. PDF, Word, Excel, or a scan.
Parse
Read each file once, whatever it is. A scanned lease and a spreadsheet arrive as the same kind of thing downstream.
Extract
Each document is extracted on its own, against your schema, with a citation for every field it filled.
Fold
The per-document results collapse into one record by documented rules. No model decides this part, which is why it never surprises you twice.
Review
Every contested field is listed with what each document said and which one won. Your own checks run here too.
Send
One record, plus what each document said on its own, into the system that tracks the portfolio.
Out:One cited record. Every field traceable to a file, a page and a box on that page.
The fold keeps one answer. It also shows its work.
Three documents, three contested fields, three different rules deciding them. Nothing is averaged and nothing is blended. A value is taken whole, with its citation, from exactly one document.
| Kept value | Rule applied | |
|---|---|---|
| Monthly rent | $12,400, cited in lease.pdf | A cited value beats an uncited one. The lease pointed at the clause it came from; the other two just asserted a number.Other files: lease-abstract.docx $12,000, uncited; fact-sheet.xlsx $12,400, uncited. |
| Rentable square feet | 4,820, cited in lease-abstract.docx | Any real value beats an empty field. The lease never states the area, so it had nothing to contribute and was not asked to guess.Other files: lease.pdf not found; fact-sheet.xlsx 4,800, uncited. |
| Term in months | 60, cited in lease.pdf | Both cited the figure just as plainly, so the tie went to the earlier file in the request. Upload order decided this one.Other files: lease-abstract.docx 60, cited; fact-sheet.xlsx not found. |
Every value points at its source.
A citation is not a footnote. It carries the file, the page and the box on that page, so a value you are unsure about takes one click to check rather than an afternoon with a PDF.
Article 2 — Term and renewal
Article 4 — Rent
Extracted fields
const run = await sdk.extract({
files: [lease, abstract, factSheet],
unit: 'across_documents',
citations: true,
judge: true, // re-score each value against the passage it cites
schema: leaseSchema,
})
// A citation names the file, the page, and the box on that page. That is
// what the panel opposite is drawing, and what makes a value checkable.
for (const [field, cited] of Object.entries(run.output.citations)) {
for (const c of cited) {
console.log(field, c.fileId, c.page, c.bbox, c.confidence)
}
}
// Confident values carry on. The rest go to a person — and the citation
// is the first thing they open.
const needsReview = Object.entries(run.output.citations)
.filter(([, cited]) => cited.some((c) => c.confidence <= REVIEW_THRESHOLD))
.map(([field]) => field)Your documents already disagree. Now you can see where.
Take two leases you already argue about, send them in one call, and read the disagreement list. Ten minutes, and you will know whether this belongs in your stack. Or clone the runnable TypeScript example, reverse the two documents, and watch the tied field change its answer.