The verdict in three sentences
Mobile money reconciliation is about proving that every shilling announced by the webhook actually exists on the operator statement, and vice versa. In Nairobi in 2026, a PostgreSQL pipeline comparing three sources — webhooks, internal ledger, M-Pesa statements — detects discrepancies automatically and leaves humans only the ~30 disputed cases per 10,000 transactions. That is the difference between driving your books and being dragged by them.
The three sources and their divergences
Every reconciliation crosses three truths that never match perfectly:
| Source | Role | Typical drift |
|---|---|---|
| Operator webhook | Notifies payment in real time | Can be missed or arrive twice |
| Internal ledger | Application entry | Depends on webhook receipt |
| Operator statement (T+1) | Official financial truth | One day late, fees withheld |
The reconciliation job runs nightly, imports the T+1 statement, and matches each line by transaction reference.
Discrepancy taxonomy and handling
Across 10,000 transactions/month at 0.3 % discrepancy (~30 cases), here is the 2026 order-of-magnitude split:
| Discrepancy type | Frequency (~) | Cause | Automatic action |
|---|---|---|---|
| Missing webhook | 12 cases | Lost notification | Create entry from statement |
| Orphan transaction | 8 cases | Payment without order | Flag for manual review |
| Double credit | 4 cases | Replayed webhook | Blocked by unique constraint |
| Amount mismatch | 4 cases | Misestimated fees | Automatic adjustment |
| Divergent status | 2 cases | Timeout then success | Resync |
The remaining cases need only a few minutes of human validation each, versus hunting manually through all 10,000 lines.
The anti-double-credit mechanism
In the database, the transactions table carries a UNIQUE constraint on the operator reference. When an M-Pesa webhook is replayed (which happens regularly), the second insert is rejected by PostgreSQL and the app returns 200 OK without creating a duplicate. It is the simplest, most reliable protection against double credits.
Mini case study
Need a professional website?
Kolonell builds websites that attract clients, optimized for the Sénégalese market. Free quote in 2 minutes.
Otieno runs an e-commerce SME in Nairobi processing 10,000 transactions/month. Before automation, an accountant spent 15 h/month ticking off M-Pesa statements at ~KSh 400/h, i.e. KSh 6,000/month. The PostgreSQL pipeline cuts this to 2 h/month (~KSh 800), saving KSh 5,200/month and KSh 62,400/year, not counting errors avoided and cash secured.
FAQ
How often should reconciliation run?
A nightly daily run suffices for most SMEs. It imports the T+1 statement available in the morning and flags discrepancies before opening. Very high volumes can move to an hourly cycle.
What do you do with an orphan transaction?
A payment with no matching order is flagged for review: often a customer paid twice or a test slipped into production. About 8 cases per 10,000 transactions; never ignore them.
How do you handle operator fees withheld?
The statement shows the net amount after fees (often ~1 %). The pipeline computes the expected difference and only flags it if it exceeds a tolerance, e.g. KSh 5.
Is PostgreSQL robust enough for this?
More than enough. Unique constraints, ACID transactions and join-based matching queries cover 100 % of an SME's needs without external tooling.
Should you keep a history of discrepancies?
Yes, a timestamped audit table keeps each discrepancy and its resolution. It is essential for audits and to improve detection over time.
Let's talk about your project. We set up your multi-operator PostgreSQL reconciliation pipeline. WhatsApp +221 77 596 93 33.
Mohamed Bah
Fondateur, Kolonell
Passionate about digital and entrepreneurship in Africa, Mohamed has been helping Sénégalese businesses with their digital transformation since 2020. Founder of Kolonell, he believes every SME deserves a professional and accessible online présence.

