Bank reconciliation
Your ledger and your bank's ledger will disagree. Reconcile them.
By Solomon Ajayi · Free to read, no signup
End of month. You generate the Bank Account statement from your ledger, it says ₦15,000. Your bank emails you their statement, it says ₦14,650. ₦350 gap. Reconciliation is the routine practice of explaining every penny of that gap. The gaps fall into three buckets: bank charges you missed (book them), deposits in transit (no entry, bank will catch up), outstanding checks (no entry, bank will catch up). This lesson focuses on the first bucket, the one that needs adjusting entries. Apply the setup, then post the adjustments to bring your books in line with the bank.
You keep a ledger. Your bank keeps its own ledger. These are two independent records of the same money, and they will disagree, not because anyone made an error, but because each side learns about events at a different time and records things the other does not. Reconciliation is the monthly ritual of explaining every kobo of that gap until you can point at each difference and say exactly why it exists.
The gaps fall into three buckets, and the bucket tells you what to do. Bank charges you never booked, like a ₦200 maintenance fee, are real cash that already left; you fix your side with an adjusting entry that debits Bank Charges Expense and credits Bank Account. Deposits in transit and outstanding checks are the opposite: your books are already right and the bank simply has not caught up, so you post nothing and wait. Knowing which bucket a difference belongs to is the whole skill.
Reconciliation is also your earliest fraud detector. An unexplained difference is not a rounding quirk to wave away; it is money you cannot account for, which is precisely the signal of a missed fee, a duplicated payout, or a theft in progress. The discipline is refusing to close the month until the gap is fully explained, never forcing a balancing entry just to make two numbers match.
Worked example, step by step
Set the stage: user deposits ₦20,000
Earlier this month a user deposited ₦20,000. Standard funding entry to set up the bank balance.
| Account | Debit | Credit |
|---|---|---|
| Bank Account (1200) | ₦20,000.00 | |
| User Wallet (2000) | ₦20,000.00 |
Bank UP ₦20,000, User Wallet UP ₦20,000. Bank Account balance is now ₦20,000 debit. Switch to Statement view and select Bank Account to watch the running balance.
User withdraws ₦5,000 to external bank
User pulls ₦5,000 back to their own external bank. Bank Account drops, User Wallet drops.
| Account | Debit | Credit |
|---|---|---|
| User Wallet (2000) | ₦5,000.00 | |
| Bank Account (1200) | ₦5,000.00 |
User Wallet DOWN ₦5,000, Bank Account DOWN ₦5,000. After this, your ledger says Bank Account = ₦15,000 debit. Open the Bank Account statement and confirm the running balance walks 20,000 → 15,000.
Reconcile: book the missed ₦200 maintenance fee
End of month. Your bank emails the statement: ₦14,650. Your ledger says ₦15,000. ₦350 gap. Looking at the bank statement line items, you find two charges your code never booked: a ₦200 maintenance fee and a ₦150 SMS notifications fee. Book the maintenance one first.
| Account | Debit | Credit |
|---|---|---|
| Bank Charges Expense (5500) | ₦200.00 | |
| Bank Account (1200) | ₦200.00 |
Bank Charges Expense UP ₦200 (an expense, this is YOUR cost). Bank Account DOWN ₦200 (cash actually left, even though you forgot to record it). After this: your ledger Bank Account = ₦14,800. Still ₦150 off from the bank's ₦14,650.
Reconcile: book the ₦150 SMS fee
Same shape. Book the SMS fee. After this, your ledger Bank Account should exactly match the bank statement at ₦14,650.
| Account | Debit | Credit |
|---|---|---|
| Bank Charges Expense (5500) | ₦150.00 | |
| Bank Account (1200) | ₦150.00 |
Bank Charges Expense UP ₦150. Bank Account DOWN ₦150. Your ledger Bank Account closing balance is now ₦14,650, matches the bank statement to the kobo. Reconciled.
Takeaway
Bank reconciliation is monthly hygiene that catches fraud early, surfaces missed fees, and keeps your books trustworthy. Three gap categories: (1) bank charges you didn't book, book them, (2) deposits in transit, no entry, bank catches up, (3) outstanding checks, no entry, bank catches up. The art is identifying which gap is which. Fintechs that skip monthly recon get surprises at year-end audit, the bad kind.
The code behind it
FULL OUTER JOIN ledger to bank feed on amount + reference within a +/-2 day window, classifying every row as matched or as an unmatched leftover.
-- Both tables hold Bank Account (code 1200) movements in kobo (minor units).
-- ledger(id, txn_date, amount_kobo, reference) -- our books
-- bank_feed(id, posted_date, amount_kobo, reference) -- the bank's record
-- txn_date and posted_date are DATE columns.
SELECT
l.id AS ledger_id,
b.id AS bank_id,
COALESCE(l.amount_kobo, b.amount_kobo) AS amount_kobo,
COALESCE(l.reference, b.reference) AS reference,
CASE
WHEN l.id IS NULL THEN 'UNMATCHED_BANK' -- bank knows, books do not (e.g. missed fee)
WHEN b.id IS NULL THEN 'UNMATCHED_LEDGER' -- books know, bank behind (deposit in transit)
ELSE 'MATCHED'
END AS status
FROM ledger l
FULL OUTER JOIN bank_feed b
ON l.amount_kobo = b.amount_kobo -- same money
AND l.reference = b.reference -- same reference
AND b.posted_date BETWEEN l.txn_date - 2 -- +/- 2 day window (inclusive)
AND l.txn_date + 2
ORDER BY status, amount_kobo;Practice this on a real ledger
Reading is half of it. Open this lesson in the lab to post the entries yourself against a real Postgres-backed double-entry ledger, with the validation on. Free, your sandbox is yours.