Mobile Money Integration
Accept mobile money payments across Africa — Telebirr, M-Pesa, MTN MoMo, and Airtel Money — through a single API with real-time webhooks.
Overview
Mobile money is the dominant payment rail in most African markets. AfriRoute abstracts each provider behind one consistent flow:
- Initiate a payment for a customer's phone number
- The customer approves via USSD push or their wallet app
- AfriRoute sends a webhook and you fulfill the order
Base URL: https://api.afriroute.ai — Auth: Authorization: Bearer $AFRIROUTE_API_KEY
Supported Providers
| Provider | Countries | Currency |
|---|---|---|
| Telebirr | Ethiopia | ETB |
| M-Pesa | Kenya, Tanzania | KES, TZS |
| MTN MoMo | Ghana, Uganda, Rwanda, Zambia | GHS, UGX, RWF, ZMW |
| Airtel Money | Kenya, Tanzania, Uganda, Zambia | KES, TZS, UGX, ZMW |
Endpoints
Initiate Payment
POST /v1/payments/mobile/initiate
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount in major units |
currency | string | Yes | ISO 4217, e.g. ETB, KES |
phone_number | string | Yes | Customer phone (E.164) |
provider | string | Yes | telebirr, mpesa, mtn_momo, airtel_money |
description | string | No | Shown on the customer prompt |
callback_url | string | No | Webhook for status updates |
metadata | object | No | Your reference data |
curl -X POST https://api.afriroute.ai/api/v1/payments/mobile/initiate \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"currency": "ETB",
"phone_number": "+251911234567",
"provider": "telebirr",
"description": "Order #12345",
"callback_url": "https://yourapp.com/payment/callback",
"metadata": { "order_id": "12345" }
}'
const res = await fetch('https://api.afriroute.ai/api/v1/payments/mobile/initiate', {
method: 'POST',
headers: { 'Authorization': 'Bearer $AFRIROUTE_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: 1000,
currency: 'KES',
phone_number: '+254712345678',
provider: 'mpesa',
description: 'Subscription renewal'
})
});
const payment = await res.json();
console.log(payment.payment_id, payment.status); // pay_abc123 pending
import requests
r = requests.post(
'https://api.afriroute.ai/api/v1/payments/mobile/initiate',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
json={
'amount': 50,
'currency': 'GHS',
'phone_number': '+233241234567',
'provider': 'mtn_momo'
}
)
print(r.json())
{
"payment_id": "pay_abc123xyz",
"status": "pending",
"amount": 500,
"currency": "ETB",
"provider": "telebirr",
"created_at": "2026-05-28T09:30:00Z",
"expires_at": "2026-05-28T09:35:00Z"
}
Check Status
GET /v1/payments/mobile/:payment_id
curl https://api.afriroute.ai/api/v1/payments/mobile/pay_abc123xyz \
-H "Authorization: Bearer $AFRIROUTE_API_KEY"
{
"payment_id": "pay_abc123xyz",
"status": "completed",
"amount": 500,
"currency": "ETB",
"provider_reference": "TXN123456789",
"completed_at": "2026-05-28T09:31:23Z"
}
Statuses: pending, processing, completed, failed, cancelled, expired.
Webhook Handler
const crypto = require('crypto');
app.post('/payment/callback', (req, res) => {
const signature = req.headers['x-afriroute-signature'];
const hash = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(JSON.stringify(req.body)).digest('hex');
if (hash !== signature) return res.status(401).send('Invalid signature');
const { event, data } = req.body;
if (event === 'payment.completed') fulfillOrder(data.metadata.order_id);
res.sendStatus(200);
});
Best Practices
- Always verify the
X-AfriRoute-SignatureHMAC before trusting a webhook. - Rely on webhooks for final status; poll only as a fallback after
expires_at. - Show the customer the exact amount and provider before initiating to reduce abandonment.
- Respect provider transaction limits (e.g. M-Pesa 250,000 KES per transaction).
Error Handling
| Code | Description | Action |
|---|---|---|
INSUFFICIENT_FUNDS | Customer wallet low | Ask them to top up |
INVALID_PHONE | Wrong number format | Validate to E.164 |
PROVIDER_UNAVAILABLE | Provider outage | Offer another method |
AMOUNT_TOO_LOW | Below provider minimum | Show minimum amount |
Related Links
Last Updated: May 2026