AutomationData EngineeringPython
Automating Financial Reconciliation with n8n and Python
How I replaced one-off scripts with a reusable pipeline layer
Sharath Dinesh · · 5 min read
Why reconciliation breaks at scale
Reconciliation is deceptively simple: match two sides, explain the difference. At one client it is a spreadsheet. At forty clients, with different file formats, cut-off times and edge cases, it becomes an engineering problem.
The failure mode is never the matching logic. It is the twenty one-off scripts nobody owns.
The architecture
- Ingest — n8n pulls source data on schedule (SFTP, API, portal via Playwright).
- Normalise — a Python layer coerces every source into one canonical schema.
- Validate — SQL assertions run before matching: row counts, control totals, date ranges, duplicate keys.
- Match — parameterised matching rules per client, stored as config not code.
- Report — results land in PostgreSQL and surface in Metabase.
-- control-total assertion that runs before every match
select
source,
count(*) as rows_in,
sum(amount) as total_in,
count(*) filter (where amount is null) as null_amounts
from staging.transactions
where batch_id = :batch_id
group by source;
What actually moved the needle
- Making validation fail loudly before matching, not after.
- Storing client rules as configuration, so a new client is a row, not a repo.
- Treating the automation layer as a product with owners, tests and versioning.
Result: 35% less manual processing time and 99%+ sustained accuracy.