Get the grain right first
This is the step that prevents the most wasted work. A bank row is a posted cash movement. The PSP row it should be compared against is a payout or settlement export, not an individual charge. One bank settlement often represents dozens or hundreds of PSP charges netted together. Comparing it directly against those charges produces a wall ofunmatched and amount_diff events that have nothing to do with a real reconciliation problem.
If your PSP export is charge-level and your bank export is settlement-level, aggregate the charges upstream into one row per payout before Reconify sees them, or use a
many_to_many pass with the payout ID as the group key so Reconify sums both sides for you. See Matching algorithm: split settlements for the mechanics and a worked example.
Configure both sides
1,842.30), so multiplier: 100 converts it to 184230 minor units.
The PSP export already stores minor units (184230), so its multiplier is 1.
Get this backwards on either side and every amount is off by a factor of 100, and almost every row becomes an amount_diff.
A few Stripe-style specifics that trip people up:
- Timestamps in payout exports often need a full Go time layout, not just a date: a value like
2024-01-15T14:30:00Zneedsdate_layout: "2006-01-02T15:04:05Z07:00", not2006-01-02. - These exports often carry several amount columns: gross, fee, net, and available balance. Picking the wrong one produces a wall of
amount_diffevents that look like a mapping bug but are actually a column choice.net_amountis usually the one that matches what actually lands in the bank. - Refunds and disputes legitimately reverse the sign of an amount. Don’t treat a negative PSP row as an error before checking whether it’s a genuine reversal.
Start strict
Begin withamount_tolerance_minor: 0, name_mode: none, and a date_window that reflects a known settlement delay, such as 2d.
Widen the window when you have a specific, known delay to accommodate, never to make a bad mapping look like it’s working. A wide window hides the same mapping problems it’s supposed to route around.
Validate, then run
--audit records run provenance (file hashes, timestamps, the applied config) in the output, which matters for a monthly artefact someone might need to defend later. See Read the results for what run_info contains.
Triage the exceptions
Variant: bank statement vs internal ledger
The same workflow applies when the counterpart is your own ledger instead of a PSP export, with one addition: a pre-flight checklist, because ledger exports vary more than PSP exports do. Before configuring the pair, confirm:- Both sides use the same sign convention for credits and debits.
- Reversals and refunds get the same treatment on both sides.
- Pending ledger rows are excluded, or kept in a separate source from settled ones.
- A stable payment, journal, or transfer identifier is available for
ref_col.
amount_tolerance_minor, and compare the ledger event date against the bank value date before widening date_window.