reconmatch is a local-first transaction matching engine for accountants, bookkeepers, and controllers. Two CSVs in — books vs bank, invoices vs payments — a scored, auditable match report out. No account, no upload, no network call.
Repo: github.com/SybilGambleyyu/reconmatch
The unglamorous pain
If you close books for a living, you already know the scene: two windows open, a bank CSV on the left, a general-ledger export on the right, and an afternoon disappearing into "which deposit is which invoice."
Bank feeds help until they do not. The hard cases are ordinary:
- One deposit that covers three invoices
- Two payouts that sum to one sales batch on the books
- A check number in the memo on one side and a dedicated column on the other
- "ACH ACME CORP INV 1042" vs "Invoice payment ACME Corp"
- An orphan the feed never explained
Enterprise close tools charge enterprise prices and want the data in their cloud. For a CPA firm or bookkeeper sitting on confidential client ledgers, "just upload the CSV" is often a non-starter. Spreadsheet VLOOKUP falls over on partial payments and batch deposits.
What reconmatch does
Matching runs in deterministic phases so the same inputs always produce the same proposals:
Exact / reference-strong — amount within tolerance, date in window, shared invoice/check/wire token
Amount + date — numbers line up even when memos are noise
Fuzzy description — token overlap plus sequence similarity (pure Python stdlib)
Group 1:N and N:1 — one line equals the sum of several on the other side
Every accepted match carries a score and human-readable reasons suitable for a workpaper. Unmatched lines stay unmatched — the tool does not invent a story for them.
pip install git+https://github.com/SybilGambleyyu/reconmatch.git
reconmatch books.csv bank.csv -o ./march-recon
Outputs: plain-text report, matches CSV, unmatched CSV, and full JSON. Zero required third-party dependencies. Python 3.10+.
Library use
from reconmatch import MatchConfig, match_transactions
from reconmatch.io import load_transactions_csv
from reconmatch.models import Side
books = load_transactions_csv("books.csv", side=Side.LEFT)
bank = load_transactions_csv("bank.csv", side=Side.RIGHT)
result = match_transactions(books, bank)
print(result.summary())
Honest limits
- Group matching is bounded (default max five lines, tight date window) for realistic batches
- Description scoring is lexical, not semantic — clean noisy exports when you can
- This is a proposal engine. Material unmatched items still need a human
- It is not a GL, not a bank feed, and not a categorizer
Why offline matters
Privacy is not a slogan for this audience. Firms have engagement letters, peer review, and client trust. A matching step that never opens a socket is a feature. You can run it on an air-gapped close laptop if that is how the engagement is set up.
Try it
git clone https://github.com/SybilGambleyyu/reconmatch.git
cd reconmatch
pip install .
reconmatch examples/books.csv examples/bank.csv -o ./out
If you reconcile for clients and this saves even one painful afternoon a month, that is the product working. Issues with redacted CSV pairs are welcome — the matching rules should grow from real close work.
MIT licensed. Built for people who still have to make two lists of money agree.
SOCIAL SHARE CARD GENERATOR