Payments reconciliation

Match bank and M-Pesa statement lines against recorded payments — file the statement, review what the engine could not decide, chase payments no bank has confirmed, and sign the period off.

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 filed → lines matched against payments → what the engine could not decide goes to a human → payments the bank never confirmed chased down → 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".

  1. Turn the module on for the organization

    The feature flag payments_reconciliation must be enabled for your organization or branch. Ask an administrator, or see Feature flags.

  2. 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:

TabWhat it is for
OverviewWhere you stand, and the one thing to do next. Not a step.
1. StatementsFile a statement, and see what every past file staged.
2. MatchingHow a line is decided, and the history of every sweep.
3. Statement linesEvery line the bank sent — the queue that needs a person.
4. Unmatched paymentsPayments we recorded that no bank statement has shown.
5. PeriodsClose the month.

Steps 3 and 4 are the two sides of the same question, and that is why they are separate tabs rather than one list. Step 3 holds what the bank sent us; step 4 holds what we hold that no bank has confirmed. A payment-side finding has no statement line, so no filter on step 3 can ever reach it.

Reconcile now sits on the Overview tab, at the top right. It opens a summary of what the run will cover and what it will do to each line before anything starts, stays up while the run is away, and finishes on what — if anything — is now waiting for you.

Step 1 — Import a statement

Statements come in through the Bulk Upload wizard, not through a separate uploader on this page. The 1. Statements tab is the history of what has been filed.

  1. Open the wizard

    Bulk Upload → Bank Statements (for reconciliation).

  2. 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.

    FormatUse it for
    M-Pesa paybill statementThe 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.
  3. 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 upload staged 0 lines, nothing was filed. The run is marked Failed, not Succeeded.

Reading the Statements 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.

Rows read and Staged are deliberately separate columns. A row whose transaction was already staged by an earlier statement is skipped rather than duplicated, so the two disagree whenever a file overlaps one you have already filed.

Opening a single statement

Select a row to open that file's own page. It accounts for every row in the file across three populations — staged, skipped as a duplicate, rejected as unreadable — and then shows what became of each staged line:

On the statement's pageWhat it tells you
Rows read / Staged / Duplicates / UnreadableWhere every row in the file went. These reconcile against each other.
Rows the parser could not readThe exact rows that failed and why, kept from import time. Fix the file or the mapping and re-upload — a rejected row is not fingerprinted, so it imports normally next time.
Statement linesEvery staged line with its current status, filterable to Needs review, Exceptions or Reconciled. Each opens its own page.
Match runs over this statementEvery sweep that covered this file, and the reason if one failed.

A file whose lines are all still Not checked yet has never been through a match run, and the page says so rather than leaving you to infer it from six identical rows. The Exceptions filter on that page lists the lines from this file carrying an open finding.

Statements filed before this page existed show for Unreadable: the reject detail was not being kept then, and showing a zero would be a claim rather than a fact.

Step 2 — Matching

A match run starts automatically when a statement is filed, and you can start one any time. 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.

There are two ways to start one, and the difference is scope:

Start it fromWhat it covers
Reconcile this statement, on that statement's pageOnly the lines that came out of that file. Use this when you are working one statement — the counters describe what is on screen.
Reconcile now, on the Overview tabEvery enrolled account in your organization. Use this to bring everything up to date at once.

Either one opens the same summary first: what will be scored, against what, and what auto-matched, suggested and exception each mean for a line. Nothing starts until you confirm it. A run moves no money and alters no payment — it only changes whether a statement line counts as matched — and lines already decided are left alone, so running it a second time is safe.

TierWhat it meansWhat happens
Tier 0 — exact receiptThe line's receipt number equals the payment's confirmation number.Reconciled automatically. This is identity, not a guess.
ScoredAmount, 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 candidateNothing 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.

Reading the run history

Run history, beneath the tiers, lists every sweep, newest first. It loads twenty at a time and says how many it is showing out of how many exist — Show older runs fetches the next twenty.

Click any row — finished or failed — to open it. A run's detail says:

It answersWith
What did it cover?One filed statement, one bank account, or every enrolled account. Named, not numbered: the account and the statement appear by name, and the statement is a link straight to its page.
Who started it?The nightly sweep, a statement being filed, or a person — by name.
How long did it take?Started, finished, and the elapsed time.
What did it do?The five counters, each with what it actually counts.

Most usefully, Saved for review counts candidate matches rather than lines: one line can produce several candidates, so it can legitimately be larger than the number of lines checked.

Step 3 — Work the statement lines

3. Statement lines is every line the bank sent, and it opens on the ones that need a person. Open a line to reach its own page, which puts the statement evidence beside the possible payments and shows the per-component score for each one, so you can see why something was suggested.

The Exceptions view on that tab is the fast path to trouble: it lists every line carrying an open finding, and the Exception filter beside it narrows to one type.

From a line's page:

ActionUse when
ApproveThe suggested payment is the right one.
RejectThis candidate is wrong; the others stay on offer.
Match manuallyYou know the payment and the engine did not find it.
Post paymentThe 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.
Park this lineThe line is not a rent payment — a bank charge, a transfer, a reversal. Requires a reason, which is kept on the audit trail.
Close this exceptionThe finding on the line has been dealt with, and the line itself stands. Distinct from parking it: "the bank made an error" is not "this is not a payment". Offers the full set of dispositions and requires a note for Other and for anything closed as ignored.

Every one of these is recorded on an append-only trail with who did it and when. Findings already closed on a line stay visible at the foot of its page, so the same line is not worked twice.

The typed findings

An exception is a typed finding, not a generic error. Five of the six hang off a statement line and are worked on that line's own page:

FindingMeaningWhere you work it
Payment not recordedThe bank shows money in, and no payment was recorded for it.The line. Usually resolved with Post payment.
Possible duplicateTwo statement lines claim the same payment.The line.
Amount differsThe reference agrees but the amount does not.The line.
Already matched elsewhereA re-filed line conflicts with one already matched.The line.
Unresolved too longPast the threshold for that account, and nobody has worked it.The line.
Not on any statementA payment was recorded, and no bank statement has shown it.Step 4 — it has no statement line, so it cannot appear on step 3.

Step 4 — Unmatched payments

4. Unmatched payments holds the sixth finding, and it is the only place it exists. A payment lands here once it has gone longer than the account's evidence window without turning up on any statement — either the statement is incomplete, or the payment is not real.

Each row names the payer, the receipt or reference, the amount and the day it was paid, so you can recognise the payment without leaving the page. History on any row opens its trail — who raised it, what has already been tried, who touched it last. On a payment no bank has confirmed, that is usually what decides between chasing the bank and reversing the payment. An open row here blocks period sign-off exactly as a line-side finding does.

Two different actions close a row, and the difference is whether money moves:

ActionWhat it does
CloseFiles the disposition and nothing else. Use it when the statement was simply incomplete and the payment is real.
Reverse paymentReverses the payment itself, then closes the finding as The payment was reversed. Use it when the payment was never real.

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. Filing a statement opens the calendar month containing the statement's earliest transaction, for that bank account, and files every line on that statement 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 statements 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 seeWhat it means
Run completed, 0 lines, no errorThe account is not enrolled, or the feature flag is off for its branch. See Before you start.
Import says 0 line(s) staged, N unreadableThe 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 reasonThe run stopped on that error. Nothing was silently skipped; fix the cause and press Reconcile now.
Statement lines is empty but a statement was filedEvery line matched automatically, or the lines belong to a branch you cannot see. Open the file on 1. Statements to see where its lines went.
A period you did not expectPeriods are opened by filing a statement, never by hand. Its card names the statements and the transaction dates that opened it.
Sign off is greyed outThe button says why beside it: unresolved lines, open exceptions, or no lines filed against that month.