M-Pesa Daraja
Entry point for Safaricom M-Pesa Daraja API work.
When your agent uses it: Entry point for Safaricom M-Pesa Daraja API work. Explains the async request/callback model, base URLs and the conventions every API shares, validates request bodies with a script, and routes to the specific daraja-* skill. Use whenever the user mentions M-Pesa, Mpesa, Daraja, Safaricom payments, Lipa na M-Pesa, Paybill, Till/Buy Goods, or Kenyan mobile-money integration.
Install only this skill:
npx skills add Mr-Macharia/mpesa-daraja-skills-and-mcp --skill mpesa-daraja
Daraja is Safaricom's REST gateway to M-Pesa. Every API follows the same three steps:
- Get a token: an OAuth client-credentials request returns a bearer token that is valid for about 1 hour →
daraja-auth - Send the request: the synchronous reply
ResponseCode: "0"means accepted for processing. It does not mean the payment succeeded. - Receive the result asynchronously on your CallBackURL or ResultURL →
daraja-callbacks
Rule #1: never mark money as moved based on the synchronous response. Only the callback, stk_query or transaction_status can confirm it.
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox.safaricom.co.ke |
| Production | https://api.safaricom.co.ke |
Credentials, passkeys and certificates only work in the environment they were issued for. Mixing environments is the most common cause of errors.
Route to the right skill
| Goal | Skill |
|---|---|
| Tokens, 401s, caching | daraja-auth |
| Charge a customer via a phone prompt (M-Pesa Express / STK Push) | daraja-stk-push |
| Receive Paybill/Till payments the customer starts; Pull missed transactions | daraja-c2b |
| Pay customers (refunds, salaries, Pochi la Biashara) | daraja-b2c |
| Pay businesses, top up a B2C account, B2B USSD checkout | daraja-b2b |
| Pay KRA taxes with a PRN | daraja-tax-remittance |
| Transaction Status, Reversal, Account Balance, reconciliation | daraja-account-ops |
| QR codes customers scan to pay | daraja-dynamic-qr |
| Recurring payments / standing orders | daraja-ratiba |
| SMS e-invoices with reminders and receipts | daraja-bill-manager |
| Webhook receivers | daraja-callbacks |
| Sandbox app, test credentials, tunnels, go-live | daraja-sandbox-setup |
| Secrets, SecurityCredential encryption | daraja-credentials-security |
| Error codes and "nothing happened" debugging | daraja-troubleshooting |
| Payment state machine, ledger tables, confirmations, refunds, reconciliation, go-live review | daraja-payment-design |
Building a whole use case rather than one API call? Start from a recipe:
| Use case | Recipe |
|---|---|
| Customers pay for orders in an app | daraja-checkout |
| Payroll, disbursements, refunds to phones | daraja-payouts |
| Customers pay a Paybill/Till; match to accounts | daraja-paybill-collections |
| Subscriptions and repeat charges | daraja-recurring-billing |
| An AI agent that sells and takes payment | daraja-agent-storefront |
New to Daraja, or want a guided tutorial in the sandbox? Use daraja-teach-me.
Conventions shared by every API
- Phone:
2547XXXXXXXXor2541XXXXXXXX: 12 digits, no+, no leading0. Bill Manager is the exception and uses07…. - Amount: whole shillings only.
- Timestamp:
YYYYMMDDHHmmssin EAT (UTC+3). - Callback URLs: public HTTPS with a valid certificate, no localhost or private IPs. Respond with 200 quickly.
- Persist IDs before the callback can arrive:
CheckoutRequestID,ConversationID,OriginatorConversationID. - Idempotency: callbacks can repeat, so key on the M-Pesa receipt and on your own request ID.
- Daraja's own spellings are required:
RecieverIdentifierType, andOccassionin B2C v3/Pochi. Do not "fix" them.
Validate before sending (feedback loop)
Run this against any request body you build, fix what it reports, and repeat until it prints OK:
python scripts/validate_request.py stk request.json
# apis: stk stk_query c2b_register c2b_simulate b2c b2pochi b2b tax status balance reversal qr ratibaIt checks required fields, phone/amount/timestamp formats, that the STK Password matches the Timestamp, HTTPS and reachable callbacks, CommandID values, field-length limits, and Daraja's spellings.
Integration workflow
Copy this checklist and tick items off as you go:
- [ ] 1. Sandbox app + credentials in env/secret store (daraja-sandbox-setup, daraja-credentials-security)
- [ ] 2. Token helper with caching + 401 retry (daraja-auth)
- [ ] 3. Request builder for the chosen API; validate_request.py prints OK
- [ ] 4. Payments and events tables; persist request IDs before/at send time (daraja-payment-design)
- [ ] 5. Callback receiver: ack fast, store raw, process async, idempotent (daraja-callbacks)
- [ ] 6. Reconciliation: query anything PENDING too long (daraja-account-ops / stk query)
- [ ] 7. Test failure paths in sandbox (cancel, timeout, wrong PIN)
- [ ] 8. `check_integration.py` clean, then the go-live checklist (daraja-payment-design, daraja-sandbox-setup)MCP tools
If the mpesa-daraja server is connected (tool names start with mpesa_; some clients prefix them with the server name), call Daraja directly instead of writing throwaway scripts. For example:
mpesa_check_credentialschecks the configured keys.mpesa_stk_pushandmpesa_stk_queryrun M-Pesa Express.mpesa_parse_callbackparses a callback payload.mpesa_explain_codeexplains an error or result code.
Use mpesa_stk_push_and_wait for checkout and mpesa_get_result to read any final result. Money tools are idempotent per idempotency_key. Money-moving tools refuse to run in production unless the operator has explicitly enabled them.