Integration

CSV Ledger Reconciliation: Matching a Bank Export to a General Ledger Export

CSV ledger reconciliation works even when your bank and your ledger only give you files, and you can see exactly how a match is made. This page runs a small synthetic bank export and a synthetic ledger export through four ordered rules, shows every matched and unmatched row, and states what the demonstration does not prove.

In short

CSV ledger reconciliation matches the rows of a bank export to the rows of a general ledger export using rules. This demonstration, for example, tries an exact match first, then a date window, then an amount tolerance, then one-to-many groups. Each row is used once, and whatever stays unmatched becomes an exception for review.

On this page
  1. What goes in: the two CSV exports
  2. The match rules, in order
  3. Tolerances: what widening a window or an amount changes
  4. The result on the synthetic fixture
  5. The human review step
  6. What the output looks like: two files
  7. Limitations and when not to use this approach
  8. Frequently asked questions
  9. Related reading
  10. Sources

At a glance

  • Two CSV exports, one from the bank and one from the general ledger, can be matched by rules applied in a fixed order, with every row used at most once.
  • Tolerances widen what counts as a match; a wider date window absorbs timing, but a larger amount tolerance does not fix a real discrepancy.
  • In this demonstration, for example, a match that depended on an amount tolerance is never cleared automatically; it goes to a person.
  • The output is the contents of two CSV files: every match, and every exception for a person to work.
  • The demonstration is synthetic and shows the method, not a connector or real-volume performance.

What goes in: the two CSV exports

Each export needs a unique id, a date, a signed amount and a reference shared with the other export. A CSV file is plain text, one record per line. RFC 4180 notes that "there is no formal specification in existence, which allows for a wide variety of interpretations of CSV files."[1] The demonstration reads optional double-quoted fields; RFC 4180 says a double-quote inside one "must be escaped by preceding it with another double quote."[1] It needs a header line, because it finds columns by name.

Columns each export needs
ExportColumns
Bank exportbank_txn_id, posted_date, description, amount, reference
General ledger exportgl_entry_id, gl_date, memo, amount, reference

Dates are YYYY-MM-DD. Amounts are signed decimals with up to two places, payments negative, held as integer cents so rounding never decides a match. The reference links the two sides and is normalized by removing case, spaces and punctuation, so inv1042 and INV-1042 compare equal. A date not in YYYY-MM-DD form, a bad amount or a duplicate id stops the run with the row number.

Illustrative example (synthetic data, not client data or real transactions): the two tables below are the complete input of the demonstration.

Illustrative example: synthetic bank export (12 rows)
bank_txn_idposted_datedescriptionamountreference
B0012026-03-02ACH PAYMENT VENDOR A-1250.00INV-1042
B0022026-03-03ACH PAYMENT VENDOR B-480.25INV-1043
B0032026-03-05CARD SETTLEMENT2310.40DEP-0305
B0042026-03-06WIRE IN CUSTOMER C3000.00DEP-0306
B0052026-03-09ACH PAYMENT VENDOR D-75.00INV-1051
B0062026-03-10BANK SERVICE FEE-15.00FEE-0310
B0072026-03-11ACH PAYMENT VENDOR E-620.00INV-1060
B0082026-03-12CHECK 4471-900.00CHK-4471
B0092026-03-13ACH PAYMENT VENDOR F-310.00INV-1070
B0102026-03-16ACH PAYMENT VENDOR A-1250.00INV-1042
B0112026-03-17ACH RECEIPT CUSTOMER G1500.00DEP-0317
B0122026-03-18ACH RECEIPT CUSTOMER H820.00DEP-0318
Illustrative example: synthetic general ledger export (12 rows)
gl_entry_idgl_datememoamountreference
G0012026-03-02Vendor A invoice 1042 paid-1250.00inv1042
G0022026-03-01Vendor B invoice 1043 paid-480.25INV-1043
G0032026-03-05Card settlement 5 Mar2310.42DEP 0305
G0042026-03-05Customer C receipt part 11800.00DEP-0306
G0052026-03-06Customer C receipt part 2950.00DEP-0306
G0062026-03-06Customer C receipt part 3250.00DEP-0306
G0072026-03-20Vendor E invoice 1060 paid-620.00INV-1060
G0082026-03-12Check 4471 Vendor K-900.00CHK-4471
G0092026-03-13Vendor F invoice 1070 paid-301.00INV-1070
G0102026-03-17Customer G receipt1500.00DEP-0317
G0112026-03-18Customer H receipt820.00DEP-0318
G0122026-03-19Vendor Z invoice 1088 accrued-2200.00INV-1088

The match rules, in order

A match rule states what must agree between a bank row and a ledger row before they are paired. The demonstration applies four rules in order, strictest first. A row paired by an earlier rule is not offered to a later one. For the strategy behind ordering rules this way, see reconciliation match rules and exceptions; this page is the runnable example.

The four rules, with the demonstration's default parameters (date window 3 days, amount tolerance 0.05)
RuleWhat must agreeOutcome
R1 exactAmount, date and normalized referencematched
R2 date windowSame amount and same reference; dates within the date windowmatched
R3 amount toleranceSame reference; date within the window; amount within the toleranceneeds_review, never auto-cleared
R4 one-to-manyOne bank row against ledger rows sharing its reference, within the window, whose amounts sum exactly to the bank amountmatched
  • Amounts are compared with their sign: -620.00 and 620.00 are different amounts, so R1 and R2 never pair a payment with a receipt.
  • In R1 to R3, each bank row in file order takes the free ledger row with the smallest date gap; ties go to file order.
  • A row with an empty reference never matches, and R4 needs at least two ledger rows in the group.

One-to-many matching is the pairing of one bank line with several ledger transactions. One ERP and accounting-software vendor's documentation defines it as "one bank statement line to many system side transactions."[3] An example is a single deposit that the ledger records as several receipts.

Tolerances: what widening a window or an amount changes

A date tolerance is a number of days within which two dates still count as the same event; an amount tolerance is a small difference within which two amounts count as the same payment. One vendor's documentation describes date tolerances as validating that the source transaction date or dates are "within a certain number of days before and after the bank statement line date or dates."[2] The same documentation states that "If no date or amount tolerance is defined within a rule, it requires an exact match."[2]

That documentation also limits amount tolerances: "Reconciliation amount tolerances can only be used in one to one matching scenarios," and for one-to-many scenarios "the amount of the bank statement line or lines must be equal to the amount of the transaction or transactions."[2] The demonstration follows the same shape: its amount tolerance applies only in R3; R4 requires an exact sum.

Illustrative example (synthetic data): widening the settings on the fixture
Setting changedRowsResult
Date window 3 days to 10 daysB007 and G007 (same reference INV-1060, 9 days apart)Matched by R2 date window with a 9-day gap. At 3 days they stay unmatched.
Amount tolerance 0.05 to 1.00B009 and G009 (same reference INV-1070, amounts differ by 9.00)B009 stays unmatched; a 1.00 tolerance does not cover a 9.00 difference.

A wider window absorbs a timing difference; a larger tolerance does not absorb a real discrepancy.

The result on the synthetic fixture

Illustrative example (synthetic data): with the default parameters, the 12 bank and 12 ledger rows produce 7 matches, 4 of them R1 exact, 1 by date window, 1 by amount tolerance flagged for review and 1 one-to-many, leaving 5 bank rows and 3 ledger rows unmatched. These counts are not a typical rate.

  1. Read and check both files

    Stop with the row number on a date not in YYYY-MM-DD form, a bad amount or a duplicate id.

  2. Normalize references

    Remove case, spaces and punctuation from each reference.

  3. Apply R1, R2 and R3 in order

    Pair one bank row with one ledger row; mark every R3 pair needs_review.

  4. Apply R4

    Group free ledger rows sharing a bank row's reference within the window; pair them only on an exact sum.

  5. Label what is left and produce the two files' contents

    Give each unmatched row a reason hint; produce the contents of the matches and exceptions CSV files.

Illustrative example: the 7 matches on the synthetic fixture
RuleBankGLGL minus bankMax day gap
R1 exactB001G0010.000
R1 exactB008G0080.000
R1 exactB011G0100.000
R1 exactB012G0110.000
R2 date windowB002G0020.002
R3 amount tolerance (needs_review)B003G0030.020
R4 one-to-manyB004G004+G005+G0060.001

In the synthetic fixture, B001 and G001 match under R1 because normalization makes INV-1042 and inv1042 the same key. B003 and G003 differ by 0.02, inside the 0.05 tolerance, so they are paired but held for review.

Illustrative example: the 8 unmatched rows on the synthetic fixture
SideRowReason hintWhat it means in this fixture
bankB005no_counterpart_with_this_referenceNo ledger row has INV-1051.
bankB006no_counterpart_with_this_referenceNo ledger row has FEE-0310.
bankB007same_reference_outside_toleranceG007 is 9 days away, outside the 3-day window.
bankB009same_reference_outside_toleranceG009 differs by 9.00.
bankB010reference_already_matchedINV-1042 already matched G001: a possible duplicate payment.
glG007same_reference_outside_toleranceThe other half of B007.
glG009same_reference_outside_toleranceThe other half of B009.
glG012no_counterpart_with_this_referenceAn accrual with no bank counterpart yet.

The human review step

Software pairs rows; a person decides what leftovers mean. In the synthetic fixture, the R3 pair needs a decision on a 0.02 difference, B007 and G007 a decision on a timing difference, B009 and G009 an investigation, and B010 a check for a duplicate payment. B005, B006 and G012 each have an entry on one side only, so someone must find or post the missing entry. To route and work such a queue, see payment reconciliation exception automation.

  • Each exception row carries a reason hint, so the reviewer starts from a likely cause.
  • An R3 pair is listed even though it was paired, because an amount tolerance can pair rows that are not the same transaction.
  • The demonstration marks the R4 group matched; a team may prefer to send grouped matches to review until grouping is validated on its own data.
  • A correcting entry or a bank query happens in the ledger or with the bank, outside this matcher.

What the output looks like: two files

A run produces the text of two CSV files; the matcher does not read or write files itself. Matches file columns: rule, status, bank_ids, gl_ids, diff, max_day_gap (one line per match). Exceptions file columns: side, id, reason. In the synthetic fixture, the exceptions file holds nine lines under the header: the R3 pair as one review line (review, B003+G003, R3_amount_tolerance), then the eight unmatched rows with the reason hints shown above.

Limitations and when not to use this approach

This demonstration shows how a match is made and reviewed, not how it performs on your data.

  • All data is synthetic and small. Nothing here was tested on real transactions or at real volumes.
  • It is a method over two CSV files, not a connector to any product.
  • Matching depends on a shared reference. Without one on both sides, these rules leave rows unmatched and you need a different key first.
  • Many-to-one and many-to-many matches are not covered; only one bank row against several ledger rows is.
  • Do not automate when a person already clears the volume without a backlog.
  • Do not automate when the team has not agreed its rules.
  • Do not automate when an existing reconciliation tool already does the job.

Frequently asked questions

How do you reconcile two CSV files, a bank statement and a general ledger export?

Parse and check both files, normalize the shared reference, then apply match rules from strictest to loosest, using each row once. Pairs that relied on a tolerance go to review, and rows left over become exceptions that a person works.

What columns does a CSV ledger reconciliation need?

Each side needs a unique id, a date, a signed amount and a reference that links the two sides. Descriptions or memos help a reviewer but are not matched on. The demonstration names these columns bank_txn_id, gl_entry_id, posted_date and gl_date.

How do date and amount tolerances work in matching?

A date tolerance accepts dates within a set number of days; an amount tolerance accepts a small difference. In the demonstration, a pair that needs the amount tolerance is always held for review. Widening a window can absorb a timing difference, but a larger tolerance does not erase a genuine discrepancy.

How does one-to-many matching work?

One bank line is compared with a group of ledger rows that share its reference and fall inside the date window. If the group's amounts sum exactly to the bank amount, all of them are paired with that line, as with a deposit booked as several receipts.

What happens to rows that do not match?

They are never forced into a match. Each unmatched row receives a reason hint, such as no counterpart with its reference, and goes to the exceptions file for review. A person investigates it and corrects the ledger or queries the bank.

When should you not automate a CSV reconciliation?

Skip it when volume is low enough that manual review already keeps up, when the matching rules are not yet agreed, when the two files share no common reference, or when an existing reconciliation tool already covers the job. A match does not decide accounting treatment.