Two document types, one call
A KYB or onboarding flow that asks a business to submit financial evidence is, in practice, asking for one of two things: a bank statement, or a set of financial statements. Both are genuinely different documents with genuinely different structures — one is a transaction ledger, the other is a set of line-item sections summarizing a period. This feature reads both, automatically classified, and returns each as its own typed schema.
Neither is a registry filing, and neither requires the jurisdiction-specific legal parsing that a certificate of incorporation or an articles of association would need — these are the financial documents a business already has on hand and can upload directly.
Why these two specifically
A bank statement is the most common piece of financial evidence a KYB flow asks for, because it answers a question no registry lookup can: does this business actually operate an account with real activity, under the name it claims. A financial report — a P&L, a balance sheet, an annual statement — comes into play for higher-risk tiers or larger transaction limits, where a platform wants evidence of financial standing beyond just an operating bank account.
Between the two, they cover what a document can actually tell a KYB flow about a business's real financial activity — which is a genuinely different question from whether the business is legally registered, and one a registry API was never built to answer.
What this doesn't decide
Doesn't read registry filings or incorporation documents
Those are separate legal documents with jurisdiction-specific structures this feature doesn't parse — a registry API covers that data instead.
Doesn't judge whether the financials are accurate or audited
The totals check confirms internal consistency — the document's own numbers add up. It doesn't verify the figures against any external source.
Doesn't decide your onboarding risk threshold
It returns confidence-scored, structured data. Where that data pushes your risk score, and what triggers manual review, is your platform's own logic.
The bank statement JSON shape
{
"type": "bank_statement",
"data": {
"bank_name": "Example Bank",
"account_holder": "Acme Trading Ltd",
"account_number_masked": "****4471",
"iban": "GB10EXPL00997083438617",
"statement_period": "2026-06-01 to 2026-06-30",
"transactions": [
{ "date": "2026-06-03", "description": "Client payment — INV-2201",
"amount": 4200.00, "balance": 18940.55 },
{ "date": "2026-06-05", "description": "Supplier payment",
"amount": -1150.00, "balance": 17790.55 }
]
}
}The account holder name is the single most useful field for a name-match check — it's read exactly as printed on the statement, ready to compare against whatever legal entity name your registry check already returned.
The financial report JSON shape
{
"type": "financial_report",
"data": {
"title": "Annual Financial Statements",
"entity": "Acme Trading Ltd",
"period": "Year ended 31 December 2025",
"currency": "GBP",
"sections": [
{ "name": "Revenue", "rows": [ { "label": "Turnover", "amount": 842000 } ] },
{ "name": "Expenses", "rows": [ { "label": "Cost of sales", "amount": 511000 } ] }
],
"report_check": { "ties_out": true }
}
}The entity field gives you a second, independent name-match point beyond the bank statement — useful when a business submits both documents during a higher-risk onboarding tier.
How the right schema gets chosen
You don't tell the API in advance which document type is coming — classification runs automatically as part of the same /extractcall, based on the document's own structure. A business can upload either document type through the same upload flow, and your pipeline gets back the schema that actually matches what was submitted.
Why typed fields matter more than they sound
"Typed" sounds like a small implementation detail until it's the thing standing between your risk logic and a bug. An amount returned as 4,200.00as a string needs locale-aware parsing before your code can safely compare it to a threshold — and that parsing has to guess correctly whether the comma is a thousands separator or a decimal point, which varies by the document's country of origin. Returned as a real number, that ambiguity doesn't exist.
The same applies to dates. A statement printed with dates as "03/06/2026" is genuinely ambiguous between 3 June and 6 March without knowing the document's locale — a mistake here doesn't just misformat a date, it can silently shift a transaction outside the period your platform is checking against. Every date comes back as ISO 8601, already resolved, so that ambiguity is handled once, centrally, instead of by each engineer who touches the response.
For a KYB flow specifically, this matters beyond convenience — a mis-parsed amount or date feeding a risk decision is exactly the kind of quiet, hard-to-catch bug that a compliance-sensitive pipeline can't afford.
Multi-page statements and long reports
A business bank statement covering a full month often runs several pages, and an annual financial report can run considerably longer. Both are read as a single document — the transaction array or the section list spans every page automatically, so your integration receives one complete, correctly-ordered result rather than needing to stitch together per-page responses itself.
This matters specifically for the totals check on a financial report — confirming that printed totals tie to line items only works correctly if every section across every page has actually been read and included, not just the first page a naive page-by-page approach might stop at.
The financial report totals check
Every financial report comes back with a report_checkverdict — whether the sums of the line items read actually tie to the document's own printed totals. A report that doesn't tie out is flagged, which is worth knowing before that data feeds a risk decision, regardless of whether the mismatch turns out to be an extraction issue or a genuine inconsistency in the submitted document.
This check doesn't verify the financials are correct in any absolute sense — a business could submit internally consistent numbers that still misrepresent its actual financial position. It verifies the narrower, concrete thing a document-reading tool can actually confirm: the totals as printed match the totals as summed.
How it works
Send the document to /extract
One call, either document type, classified automatically.
The right schema is applied
Bank statement or financial report, based on the document's own structure.
Fields and rows are typed and checked
Transactions or line items extracted with a confidence score; totals cross-checked for a financial report.
The typed result comes back
JSON ready for your risk logic, or exported to Excel/CSV via /export.
A mixed submission, extracted
A business applying for a higher transaction limit submits both a recent bank statement and its latest annual financial statements in the same onboarding session — two separate calls to /extract, each classified and returned as its own schema.
| Document | Result |
|---|---|
| Bank statement | Classified correctly, 52 transactions, account holder matches registry name |
| Financial report | Classified correctly, entity name matches, totals tie out |
| Cross-check | Both documents name the same legal entity, independently |
Neither document alone would have given the platform two independent name-match points — reading both consistently is what makes that cross-check possible without a person manually comparing two PDFs.
Documents this is built to handle
A statement from a bank never processed before
Reading works from the document's own layout, not a fixed template per bank — an unfamiliar bank is read the same way as a familiar one.
A financial report in an unfamiliar section layout
Line-item sections are read from the document's own structure, so an uncommon report format doesn't require a new template.
A scanned or photographed submission
Goes through the same pipeline, with confidence scoring that flags the specific fields worth a second look.
A statement with a slightly different legal name than expected
Read exactly as printed — normalizing a legal suffix or comparing name variants is a matching decision your platform makes with the raw field.
Why the schema doesn't change under you
Once a KYB pipeline has been built against a specific field shape, a change to that shape is a breaking change — a renamed field, a restructured section list, or a shifted data type can quietly break a name-match rule or a totals check that was working fine the day before. For a compliance-adjacent flow, an integration that behaves differently without warning is a real operational risk, not just an inconvenience.
The bank statement and financial report schemas shown above are the stable, documented contract — new optional fields can be added over time as extraction improves, but existing fields keep their name, type and meaning, so an integration built today keeps working as accuracy improves under the hood.
Raw OCR text vs. typed document data
| Raw OCR text | Typed document data |
|---|---|
| A block of unstructured text per page | A schema matched to the document type, ready to consume |
| Your team writes a parser to find the account holder name | account_holder is already its own typed field |
| No signal on which fields are uncertain | Confidence score on every individual field |
| No check that a report's numbers add up | report_check verdict included automatically |
A note on scanned and photographed documents
Not every applicant submits a clean digital export — a scanned statement or a phone photo of a printed report is common, particularly from smaller businesses or older banking relationships. Both go through the same classification and extraction pipeline as a digital document, with confidence scoring calibrated to reflect the added uncertainty a scan or photo genuinely introduces, rather than silently treating it as equivalent to a clean digital source.
Who uses this
Fintech onboarding engineering teams
Structured, name-matchable data feeding directly into an existing risk pipeline.
KYB and business verification platforms
The document layer of a broader flow that also includes registry and screening checks.
Embedded finance and BaaS platforms
Financial document verification for business accounts opened through your own product.
Risk and compliance engineering
Auditable, confidence-scored extraction output that traces every field back to source.
Why a general OCR service isn't a shortcut here
A general-purpose document AI service can extract text and table coordinates from a page — a real capability, but not the same thing as this feature. It has no concept of a bank statement's transaction structure specifically, no built-in totals check for a financial report's line items, and no confidence scoring calibrated to which fields actually matter for a name-match check.
Getting from raw OCR output to the schemas shown above is itself a substantial build — reconstructing a transaction table across page breaks, deciding how to represent a signed amount consistently, validating that a report's totals actually tie out. That layer is exactly what this feature already is, available as one typed response instead of a multi-month internal project on top of a general-purpose service.
Why financial documents are harder than they look
A bank statement looks simple at a glance — dates, amounts, a running balance — but the actual variation across banks, countries and statement generations is significant: different column orders, different ways of representing debits and credits, different handling of multi-line transaction descriptions. A financial report varies even more, since there's no single standard layout for how a company presents its own numbers.
This is exactly why a general OCR service, on its own, isn't enough — it reads the text, but turning that text into a reliable account_holder field or a correctly-summed line-item section is a substantial parsing and validation layer that has to be built and maintained on top of it. That layer is what this feature already is.
How this behaves at volume
Nothing about the schema or the classification step changes as call volume grows — the same typed response comes back whether it's the first document your integration has ever sent or the hundred-thousandth. There's no batching requirement and no separate high-volume tier to negotiate; pricing and throughput scale with usage automatically.
Get your API key
Run a real bank statement or financial report through /extract and see the typed schema yourself. See the full API overview for how this fits into the rest of a KYB flow.
Security and privacy
Uploads are encrypted with TLS from end to end.
Processing runs on infrastructure with SOC 2-aligned controls.
Original documents are deleted shortly after processing.
Nothing you upload is ever used to train AI models.
Full details are on the security page.
