M-Pesa Disbursements to Business Central: How to Reconcile Without Re-keying
How Kenyan finance teams can link M-Pesa B2C and B2B disbursements to Microsoft Dynamics 365 Business Central: result callbacks, payment references, OData posting, failed and reversed payments, and month-end reconciliation.

Short answer: To reconcile M-Pesa disbursements with Microsoft Dynamics 365 Business Central without re-keying, make the approved request the single source of both the payment and the ledger entry. Put your own unique reference on every M-Pesa instruction, treat the asynchronous result callback (not the initial response) as the truth, write the M-Pesa transaction ID back to the request, and post to Business Central from that same record over OData. Month-end then becomes a check of exceptions rather than a line-by-line match.
This article is for finance managers, Business Central owners and IT teams at Kenyan organisations that pay staff, field teams or suppliers on M-Pesa and post to Business Central. It is technical enough to brief a developer and plain enough for the finance lead who signs off the reconciliation. Creodata builds expense management software that pays over M-Pesa and posts to Business Central, so we describe our approach near the end.
Why M-Pesa and Business Central drift apart
In most organisations the payment and the posting are two acts by two people:
- A request is approved by email or on paper.
- Someone sends the money through a gateway or M-Pesa portal and saves the confirmation.
- Days or weeks later, someone else keys a journal in Business Central from the voucher.
- At month-end, a third person matches the M-Pesa statement to the ledger.
Each hand-off loses information. The M-Pesa statement has a transaction ID, a phone number or till, and an amount. The ledger has an account, a cost centre and a description someone typed. Nothing links them except memory and file names. The fix is structural: the payment and the posting must come from the same record.
M-Pesa B2C vs B2B: which one for which payment?
Safaricom exposes both through its Daraja API and business products, usually reached through a licensed payment gateway.
| M-Pesa B2C | M-Pesa B2B | |
|---|---|---|
| Pays | An individual's M-Pesa wallet | Another business's paybill or till |
| Typical use | Staff reimbursements, petty cash, field allowances, per-activity payments | Supplier and vendor payments |
| Recipient identifier | Phone number | Paybill or till number, plus an account reference for paybills |
| What to capture | Recipient name returned by M-Pesa, transaction ID, completion time | Transaction ID, the account reference you sent, completion time |
| Common failures | Unregistered or wrong number, recipient wallet limits, insufficient float in the paying account | Wrong paybill or account reference, insufficient float |
Whichever you use, the paying organisation's own business account is the source of funds, never a staff member's phone. That alone fixes most segregation-of-duties findings.
How M-Pesa result callbacks work
The single most common integration mistake is treating the first response as the payment outcome.
When a system sends a B2C or B2B request through Daraja, the immediate reply only says the request was accepted for processing. The actual outcome arrives later, as a callback to a result URL you supplied, with a result code, a description and, on success, the M-Pesa transaction ID. Daraja also asks for a queue time-out URL, which is called if the request expires in the queue. If you use a gateway, it may wrap these callbacks in its own format, but the principle is the same.
That gives three states a request can be in after approval:
- Sent, awaiting result. Money may or may not have moved. Do not post and do not re-send.
- Completed. The callback reports success with a transaction ID. Now it is safe to post.
- Failed or timed out. The callback reports failure, or no result arrives. Investigate before any retry.
Callbacks can be delayed or, occasionally, never arrive. A good integration does not just wait: it runs a status query on requests that have been "sent, awaiting result" for too long, and only then decides whether the payment happened.
Put your own payment reference on every request
Reconciliation is only as good as the key you match on. The M-Pesa transaction ID is created by M-Pesa, so you do not know it until the result arrives. You need your own reference, generated when the request is approved and sent with the payment instruction.
Good practice:
- One request, one reference. Use the expense request number, or a payment ID derived from it, as the originator reference on the M-Pesa instruction.
- Never reuse a reference for a retry without first confirming the original failed. Reusing it, or checking it before sending, is how you avoid paying twice.
- Carry the reference into Business Central as the document number or external document number on the posted entry, alongside the M-Pesa transaction ID.
With both identifiers on the ledger entry, anyone can go from a line on the M-Pesa statement to the approved request and its receipts in one step.
Posting to Business Central over OData
Business Central exposes its data through web services and APIs based on OData. An integration can create journal lines or purchase documents directly, instead of producing a file for someone to import. For M-Pesa disbursements, the questions to settle with your Business Central partner are:
- Where does the entry land? A general journal batch for M-Pesa payments, a payment journal against a vendor, or a posted purchase document for supplier invoices. Keep M-Pesa payments in their own batch so they are easy to review.
- Which bank account represents M-Pesa? Most organisations set up the M-Pesa business account as a bank account in Business Central, so payments reduce it and the statement can be reconciled against it.
- What dimensions travel with the entry? Department, programme, project or cost centre should come from the request, not be typed again.
- When does posting happen? After the payment result says completed, not at approval. Posting on approval creates entries for money that never left.
- Where do documents go? Link the receipts or invoice to the entry, so an auditor looking at the ledger can open the evidence.
Handling failed and reversed M-Pesa payments
Failures are where manual processes break, because the obvious fix, sending again by hand, is also how duplicate payments happen.
| Situation | What the system should do | What finance should see |
|---|---|---|
| Result callback reports failure | Mark the request "payment failed" with the M-Pesa reason; do not post | A queue of failed payments to correct and resubmit |
| No callback within the expected time | Run a status query before anything else | Requests flagged "unconfirmed" until the query answers |
| Payment succeeded but posting to Business Central failed | Keep the payment as completed and hold a posting exception | A list of paid-but-unposted items, cleared before close |
| Payment reversed after completion | Record the reversal against the original request and post a reversing entry | Reversals visible on the request, not only on the statement |
| Duplicate submission of the same request | Refuse a second payment for a reference already completed | Nothing, because it never happened |
Ask any vendor or developer to demonstrate each row in a test environment. Most integrations handle the happy path well; the difference shows up here.
Month-end reconciliation, step by step
When payment and posting come from the same record, month-end looks like this:
- Export the M-Pesa statement for the business account for the period.
- Match on transaction ID to the completed requests. This should match almost everything automatically.
- Review the exceptions: statement lines with no request (payments made outside the system), requests completed with no statement line, and reversals.
- Clear paid-but-unposted items so the Business Central M-Pesa bank account agrees to the statement.
- Check charges. M-Pesa transaction charges need their own posting rule so they do not become unexplained differences.
- Sign off, with the exception list and its resolution kept as audit evidence.
The goal is that step 3 is short. If it is long, look for payments being made outside the workflow.
Where Creodata fits
Our expense management system is built around this model. Supplier invoices, petty cash and cashbook payments are raised with attachments, approved through a configurable multi-stage chain (for example Head of Department, Finance Reviewer, CFO) with automatic SLA escalation, and paid on final approval:
- M-Pesa B2C for petty cash and reimbursements, and M-Pesa B2B for supplier payments, through the KentaPay / Eclectics Swivel gateway.
- Callback and status reconciliation, with the payment reference written back to the request.
- Posting to Microsoft Dynamics 365 Business Central over OData, with SharePoint document links attached, so nobody re-keys the entry.
- Every attachment stored in SharePoint Online and linked to the request; sign-in through Microsoft Entra ID; role dashboards and CSV exports for finance and audit.
What we do not do: we post only to Business Central, not to Sage, SAP or other ERPs, and we do not make bank or PesaLink payments. If you need those, ask whether a different tool fits better. For petty cash specifically, see our petty cash management software and the guide to petty cash management in Kenya. For a worked example, read the composite scenario of an NGO that closed the gap between M-Pesa and Business Central.
To see an approved request paid on M-Pesa and posted to Business Central, book a demo.
Frequently asked questions
Can M-Pesa integrate directly with Business Central?
Business Central does not pay over M-Pesa on its own. An integration, either custom or through an expense or payments system, calls M-Pesa through Daraja or a gateway, receives the result callback, and posts to Business Central through its OData web services. The design choice that matters is making one record drive both the payment and the posting.
How do I reconcile M-Pesa B2C payments?
Match the M-Pesa statement to your approved requests on the M-Pesa transaction ID, with your own payment reference as a second key. Then review the exceptions: payments with no request, requests with no payment, reversals and charges. If every payment is triggered from an approved request, almost everything matches automatically.
Why does an M-Pesa payment show as accepted but never complete?
The first response only confirms the request was accepted for processing. The outcome comes later on the result callback. If the callback is delayed or lost, run a transaction status query before retrying, otherwise you risk paying twice.
When should a payment be posted to Business Central?
After M-Pesa confirms the payment completed, not at approval. Posting on approval creates ledger entries for payments that may fail. Keep approved-but-unpaid items in the workflow system until the result arrives.
What is the difference between M-Pesa B2C and B2B for businesses?
B2C sends money from a business account to an individual's M-Pesa wallet, which suits staff reimbursements and petty cash. B2B sends money to another business's paybill or till, which suits supplier payments. Both return a result callback with a transaction ID that should be recorded against the request.





