Finance → Reconciliation → /reconciliation/overview Beta
Reconciliation answers one question: does the money the bank says arrived match the payments the system recorded?
Everything else follows from that. A statement line with no payment behind it is money you have received but not credited to anyone. A payment with no statement line behind it is money you think you have but the bank has never seen.
statement imported → lines matched against payments → what the engine could not decide goes to a human → exceptions resolved → the period is signed off.
Before you start: two switches
Reconciliation is off until both of these are on. Neither reports an error when it is off — a run simply completes having examined nothing, which looks identical to "everything is already reconciled".
Turn the module on for the organization
The feature flag
payments_reconciliationmust be enabled for your organization or branch. Ask an administrator, or see Feature flags.Enrol each bank account
Open the account under Bank accounts, press Edit, and switch on Reconcile this account's statements in the Integrations section. Accounts are enrolled one at a time on purpose: you reconcile the accounts you have statements for, not every account you have ever created.
This is a different switch from Bank feed connected. That one says money lands on the account without anyone keying it in; this one says the account's statements are imported and matched. Reconciling a manually-captured account is the normal case, so an account can have either, both or neither.
If either gate is closed, every tab in the module carries a Reconciliation is not switched on yet banner naming the one that is missing — you do not have to work it out from a screen of zeros. A run with 0 lines when you know you imported a statement is the same symptom.
The tabs, and the order to use them
The tab strip is numbered for this flow, so where you are in it is never a guess:
| Tab | What it is for |
|---|---|
| Overview | Where you stand, and the one thing to do next. Not a step. |
| 1. Imports | File a statement, and see what every past file staged. |
| 2. Matching | How a line is decided, and the history of every sweep. |
| 3. Worklist | The queue of lines that need a person. |
| 4. Exceptions | The typed findings. |
| 5. Periods | Close the month. |
Reconcile now is pinned to the right of the tab strip, so a match run can be started from any tab.
Step 1 — Import a statement
Statements come in through the Bulk Upload wizard, not through a separate uploader on this page. The 1. Imports tab is the history of what has been filed.
Open the wizard
Bulk Upload → Bank Statements (for reconciliation).
Choose the account and the format
Both are required and neither can be guessed from the file — several banks export the same column layout, and filing a statement against the wrong account would attribute one organization's money to another.
Format Use it for M-Pesa paybill statement The paybill export from the M-Pesa org portal. Has separate Paid In / Withdrawn columns and a Receipt No. NCBA statement (CSV) NCBA's CSV export. Generic statement (CSV, signed amount) A plain CSV with one signed Amount column — positive in, negative out. A separate withdrawn column is ignored by this format. Map the columns (usually nothing to do)
The parser recognises the standard headers for each format. Map a column only when your export uses a name it does not recognise.
The single most valuable column is the Receipt / Reference No. — the M-Pesa receipt or bank reference. An exact match on it reconciles a line automatically with no human step. Without it, every line needs a person.
Rows the parser cannot read are reported, never silently dropped. A statement routinely carries header blurb and subtotal rows, so one unreadable row does not cost you the other four hundred. The run summary lists each rejected row with its number and the reason — most often No readable transaction date or No readable amount, which means that column did not map.
If the import staged 0 lines, nothing was filed. The run is marked Failed, not Succeeded.
Reading the Imports tab
Each row is one file. Alongside the account it was filed against and the dates it covers, the row shows where its lines got to — how many auto-matched, how many are still waiting on a person, how many raised an exception. A row count on its own tells you the file landed; it does not tell you whether any of it reconciled.
Select a row to open it. The import expands into the lines it actually staged — date, amount, receipt or reference, payer, narrative and current state — and each line links through to its workbench. This is how you check what a file contained without going hunting in the worklist.
Step 2 — Matching
A match run starts automatically after an import, and you can start one any time with Reconcile now, pinned to the right of the tab strip. Each unresolved line is scored against the payments on that account within the date window. The 2. Matching tab states these three tiers on screen and lists every sweep that has run beneath them.
| Tier | What it means | What happens |
|---|---|---|
| Tier 0 — exact receipt | The line's receipt number equals the payment's confirmation number. | Reconciled automatically. This is identity, not a guess. |
| Scored | Amount, reference, payer phone and recency agree to a degree. | Offered as a suggestion for a human to approve. Never auto-applied unless auto-approve is switched on and the score clears the threshold. |
| No candidate | Nothing plausible on that account. | Raises an exception. |
Confidence bands are 95 / 80 / 60. Auto-approve is off by default: the engine proposes, a person disposes.
Step 3 — Work the worklist
3. Worklist is the queue of lines that need a person. Open a line to reach the workbench, which puts the statement evidence beside the candidate payments and shows the per-component score for each one, so you can see why something was suggested.
From the workbench:
| Action | Use when |
|---|---|
| Approve | The suggested payment is the right one. |
| Reject | This candidate is wrong; the others stay on offer. |
| Match manually | You know the payment and the engine did not find it. |
| Post payment | The bank received money the system never recorded. Creates the missing payment through the normal payment path, with a receipt derived from the line so a retry cannot double-post. |
| Ignore | The line is not a payment — a bank charge, a transfer, a reversal. Requires a reason, which is kept on the audit trail. |
Every one of these is recorded on an append-only trail with who did it and when.
Step 4 — Exceptions
An exception is a typed finding, not a generic error:
| Type | Meaning |
|---|---|
| Missing in platform | Money on the statement with no payment recorded. Usually resolved with Post payment. |
| Missing in statement | A payment with no statement evidence. Either the statement is incomplete or the payment is not real. |
| Duplicate | Two statement lines claim the same payment. |
| Amount mismatch | The reference agrees but the amount does not. |
| Already matched | A re-imported line conflicts with one already matched. |
| Stale | Unresolved past the staleness threshold — nobody has worked it. |
Step 5 — Sign the period off
5. Periods closes the loop. A period covers one bank account for one calendar month, and it cannot be signed off while anything in it is unresolved — that refusal is the control.
Where the dates come from
You never create a period. Importing a statement opens the calendar month containing the statement's earliest transaction, for that bank account, and files every line on that import into it. That is the whole rule, and it is why a month you did not ask for can appear: it appeared because a statement line was filed into it.
Each card names the account, the month it covers, the span of transactions actually filed into it, and how many imports opened it. Where those two spans disagree — a month covering 1–31 July holding transactions only up to the 12th — the statement is partial, and the rest of the month has no evidence behind it yet.
The Sign off button states what is blocking it rather than making you press it to find out: an unresolved line count, an open exception count, or no lines filed at all.
A signed period can be reopened by an administrator, but the reason is mandatory and is kept.
Re-importing the same statement
Safe. There are two guards:
- Line level — each line is hashed on its identity fields, so the same transaction cannot be staged twice even if the file is re-exported with different column order or spacing.
- File level — a file already staged for that account is recognised and skipped.
A row that was rejected is not fingerprinted, so fixing the file or the mapping and re-uploading imports it normally.
Troubleshooting
| What you see | What it means |
|---|---|
| Run completed, 0 lines, no error | The account is not enrolled, or the feature flag is off for its branch. See Before you start. |
| Import says 0 line(s) staged, N unreadable | The parser could not read those rows. The run summary lists each one and why — usually the date or amount column did not map. |
| Run shows Failed with a reason | The run stopped on that error. Nothing was silently skipped; fix the cause and press Reconcile now. |
| Worklist is empty but a statement was imported | Every line matched automatically, or the lines belong to a branch you cannot see. Open the import on 1. Imports to see where its lines went. |
| A period you did not expect | Periods are opened by imports, never by hand. Its card names the imports and the transaction dates that opened it. |
| Sign off is greyed out | The button says why beside it: unresolved lines, open exceptions, or no lines filed against that month. |
Related
- Bank accounts — enrolling an account
- Payments — how payments are recorded in the first place
- Bulk upload — the import wizard and its run log
- Allocations — what happens to a payment after it is reconciled