banktrail is hosted. Nothing to install.

Is this the complete bank statement population, or just the rows that happened to parse?

Every bank statement parser takes files in and gives rows out. No parser answers whether a statement is missing. The first panel below answers it by hand. The second panel is the same three files in banktrail, which answers it with a proof.

Before By hand — not banktrail Northwind Traders, Jan–Apr 2025. Three statements typed into one sheet.
recon_FY25_v3_FINAL(2).xls — last saved 11:48 PM
=F19-G19  …doesn’t tie. diff 64,500?? see note
After The same files in banktrail 21 rows, 2 chain breaks, 1 row the bank printed that did not parse
northwind
Date Description Reference Amount Balance Account
1 2025-01-03 INVOICE SETTLEMENT ACME LOGISTICS INV-2201 188,400.00 670,590.55 0132-4477810-03
2 2025-01-07 RENT UNIT 4 HARBOUR ESTATE rent SO-1180 -96,250.00 574,340.55 0132-4477810-03
3 2025-01-09 SUPPLIER PAYMENT KHALID BROS CHQ 100442 -41,875.20 532,465.35 0132-4477810-03
4 2025-01-14 SALARY BATCH JANUARY payroll PR-2025-01 -238,914.00 293,551.35 0132-4477810-03
5 2025-01-14 SALARY BATCH JANUARY payroll PR-2025-01 -238,914.00 54,637.35 0132-4477810-03
6 2025-01-21 INVOICE SETTLEMENT ACME LOGISTICS INV-2214 215,600.00 270,237.35 0132-4477810-03
7 2025-01-28 UTILITIES K-ELECTRIC DD-0093 -18,442.10 251,795.25 0132-4477810-03
8 2025-01-31 BANK CHARGES -1,250.00 250,545.25 0132-4477810-03
9 2025-02-03 INVOICE SETTLEMENT NORTHWIND RETAIL INV-2219 142,300.00 392,845.25 0132-4477810-03
10 2025-02-06 RENT UNIT 4 HARBOUR ESTATE rent SO-1180 -96,250.00 296,595.25 0132-4477810-03
Chain break · account 0132447781003 · 2025-02-06 → 2025-02-14 · expected 55,295.25, found 119,795.25 · 64,500.00 unaccounted for · 1 unread row(s) in northwind-2025-02.csv may hold this
11 2025-02-14 SALARY BATCH FEBRUARY payroll PR-2025-02 -241,300.00 119,795.25 0132-4477810-03
12 2025-02-19 INVOICE SETTLEMENT ACME LOGISTICS INV-2223 198,750.00 318,545.25 0132-4477810-03
13 2025-02-25 SUPPLIER PAYMENT KHALID BROS CHQ 100467 -52,300.00 266,245.25 0132-4477810-03
14 2025-02-28 BANK CHARGES -1,250.00 264,995.25 0132-4477810-03
Chain break · account 0132447781003 · 2025-02-28 → 2025-04-04 · expected 441,895.25, found 554,675.75 · 112,780.50 unaccounted for · no statement covers this window
15 2025-04-04 INVOICE SETTLEMENT NORTHWIND RETAIL INV-2241 176,900.00 554,675.75 0132-4477810-03
16 2025-04-08 RENT UNIT 4 HARBOUR ESTATE rent SO-1180 -96,250.00 458,425.75 0132-4477810-03
17 2025-04-14 SALARY BATCH APRIL payroll PR-2025-04 -243,050.00 215,375.75 0132-4477810-03
18 2025-04-17 SUPPLIER PAYMENT KHALID BROS CHQ 100501 -38,120.00 177,255.75 0132-4477810-03
19 2025-04-22 INVOICE SETTLEMENT ACME LOGISTICS INV-2247 205,300.00 382,555.75 0132-4477810-03
20 2025-04-29 UTILITIES K-ELECTRIC DD-0101 -19,105.40 363,450.35 0132-4477810-03
21 2025-04-30 BANK CHARGES -1,250.00 362,200.35 0132-4477810-03
21 rows Filter net -297,270.70 ⚠ 2 chain breaks all accounts

Every figure in the workbook is defensible on its own. The person who maintains it is not careless. The workbook cannot show whether a statement is missing. banktrail draws each chain break between the two rows it separates, with the date window and the unaccounted amount.

Chain break 2025-02-06 → 2025-02-14 expected 55,295.25, found 119,795.25 64,500.00 unaccounted for the “diff 64,500??” in that sheet, with a date and a cause
What the import returned.

The same rows now show a chain break.

banktrail imports those bank statements into a ledger. Every transaction is traceable to a byte range in a source file. A machine-checkable proof shows that no statement is missing from the chain. The viewer at the top of the page shows the sample company. The rows in it are what the import and the continuity check return.

  • Consecutive statements must chain: each expected closing balance must appear among the balances the bank printed. A chain break is a failure to connect by closing balance. The viewer shows the break between the two rows it separates, with the date window and the unaccounted amount. The note “diff 64,500??” is now 64,500.00 unaccounted for between 2025-02-06 and 2025-02-14.
  • A break has one of two causes. An unread row is a bank-printed row that banktrail cannot parse. The first break comes from an unread row, the cell printed as #####. The row stays in the unread list with its file, line, and the reason it did not parse. The second break comes from a missing statement. The ledger reports the cause.
  • The row struck out in the workbook is present. Two payments with the same amount and payee on the same day are two payments, not one. The statement printed both at different positions, and banktrail treats each position as a separate transaction. A parser that discards rows creates a ledger that looks complete but is short. That is the most expensive failure mode, because the output has the authority of every row that parsed.
The viewer

4 views of the same statements

The panel above is the viewer, not a screenshot. The viewer has 4 tabs. Select a tab to see the same 3 statements as transactions, accounts, documents, or the integrity report. Each view shows different information. All 4 views come from the same source data as the grid.

  • Transactions is a virtualised grid. It renders only the visible rows, so it scrolls, sorts and filters a 1,000,000-row ledger at the speed of a 1,000-row one. A chain break appears in the grid between the two rows it separates, not on a separate report page. Tags appear on their rows. Tag colour comes from a hash of the tag name, so a tag has the same colour across sessions. A selection copies as TSV into a spreadsheet.
  • Accounts shows the state of each balance chain. A balance chain is the sequence of closing balances across statements. Each account is listed with its currency, transaction count, date span, money in and money out, and a state: intact, has breaks, or unverifiable. Unverifiable means the statements print no running balance, so no continuity check is possible. It is a separate state, never reported as passed.
  • Documents lists every imported file. For each one: the format it was read as, rows produced, unread rows, whether statement arithmetic balances, and the SHA-256 of the uploaded file. An unread row is a row the reader cannot parse. Each transaction records its source document and byte range.
  • Integrity is the report in full: uid integrity, provenance, the amount mirror, statement arithmetic and balance continuity, each with pass or fail and the count behind it. The sample company fails balance continuity, because two chain breaks and one unread row mean the ledger is incomplete.

The viewer cannot alter a transaction, tag or figure. Evidence attaches to many rows at once as one attachment identified by content hash. A selection, a view or the whole ledger can be exported as TSV, CSV or SQLite.

Measured performance

Speed on a ledger of 249,996 transactions

Every figure below is from a ledger of 249,996 transactions in 1,140 statements across 6 accounts. The machine is Apple M4 Pro. The operations are the same ones the viewer performs. Each timing is the median of repeated runs after a warm-up. The slow timings are included. A page for checking figures cannot quote only the fast ones.

A page of rows anywhere in the ledger
6.2 ms
At row 249,596 of 249,996
The same viewer, sorted by amount
6.3 ms
The data store performs the sort.
An annotation after it has been written and read
0.20 ms
The cost to an agent per tag
Searching the text of every row
77.4 ms
It matches one word against every description and reference.
Summing the whole population
515 ms
It sums 249,996 amounts in Decimal, split by currency.
Checking the balance chain
834 ms
The check compares each statement with the next statement.
The full integrity report
4.89 s
The report lists hashes, provenance, footing and continuity.
Ledger file
1,046 bytes/txn
Size 262 MB. Provenance is included.
  • Scrolling speed does not depend on ledger size. A window at the end of 249,996 transactions costs 6.2 ms. The same window at the start costs 3.0 ms. The grid requests the on-screen rows, not the whole ledger.
  • Writing a tag and reading it back takes 0.20 ms. An agent can tag a few thousand rows as one task instead of a batch job.
  • The ledger occupies 262 MB, or 1,046 bytes per transaction. This size includes provenance for every row: the source document, the position in that document, and the file hash.
  • The full integrity report takes 4.89 s. It recomputes the content hash of every row, confirms provenance, verifies the arithmetic of each statement, and re-checks the balance chain across all 249,996 rows.
Statement parsers compared

Proof that the statement set is complete

Extracting rows from statement files is assumed. banktrail came from a real reconciliation across 284 bank statements and 6 banks. The initial answer was “eleven such transfers”. It was one account’s subset of a 3-account population. Every row had parsed correctly. The error was assuming a date range was contiguous. No statement parser we could find would have detected it. Accountants, auditors and controllers meet the same situation: every row parses, and the population is still short.

  • money

    Money is always handled as Decimal.

    A ledger must foot to the minor unit. It cannot be built on a data type where 0.1 + 0.2 does not equal 0.3. Every amount is transferred as a string. The web page never performs arithmetic on money.

  • dates

    Adapters declare the date order.

    An adapter is a banktrail component that reads a bank's statement format. It declares the date order for that bank's files. The date 03/01/2023 is 3 January in Karachi and 1 March in Chicago. When a date is ambiguous, the system reports a warning.

  • files

    File type is detected from the content.

    Banks send .xls files that are HTML documents and .xls files that are genuine BIFF. The extension does not distinguish the two formats. The system detects the file type from the content. This is the only safe way to identify the format.

  • footing

    A statement must balance before it imports.

    The import is refused unless opening plus net movement equals closing. This check detects a misread column and a defective adapter. Neither one is imported.

  • scans

    A scan is rejected until every token is accounted for.

    banktrail rejects an image-only PDF instead of parsing it to nothing. OCR converts the image to text. A capture report accounts for every token that looks like money before the import is allowed. A partial parse is visible in the report.

  • identity

    Identical payments on the same day are two payments.

    Occurrence-slot identity is the position of a transaction in the statement. Two payments with the same amount, same payee and same day are two transactions. Both salary payments on the 14th survive when the export format shuffles rows.

  • annotations

    Annotations record their provenance.

    Provenance is the record of where an annotation came from. Tags carry their source. Cross-references carry their method and confidence. A cross-reference matched on amount and account differs from one matched on amount alone. An audit distinguishes the two claims.

  • audit

    The ledger downloads as a standalone file.

    The snapshot is the downloaded ledger file. It is plain SQLite with views, plus a manifest that lists the content hash and source byte range of every row. An auditor can open it with existing tools. No conclusion depends on the service uptime.

Accounts whose statements have no balance column are reported as unverifiable. They are not reported as passed. Reporting an unchecked account as intact would claim a proof that was never run.

What the service reads

The service reads bank statement exports.

banktrail is a hosted service for accountants, auditors and finance teams. It turns bank statement exports into a transaction ledger (a list of transactions). It produces an integrity report (a report on whether the statements are complete). It creates a downloadable snapshot (a file of the results). There is no installation and no configuration. The first reconciliation takes about as long as opening the files would take.

  • It reads and converts XLS/XLSX, CSV, HTML or text e-statements. File type detection is by content, not file extension. An .xls file that is really an HTML document is read as HTML.
  • Adding a new bank does not require a software release. For most banks, the statement layout is described by naming the header fragments the bank prints. A layout not seen before is a configuration change. banktrail can add a new bank from one statement.
  • Agents and integrations use a keyed HTTP API with reader, annotator, importer and editor roles. It can import, tag, cross-reference and record attributed edits. Annotation round-trips take under a millisecond at 50k rows.