Internal Services
Airtime Disbursement API
Send airtime to Safaricom, Airtel, and Telkom numbers in Kenya with one API. See request fields, callbacks, and transaction handling.
The Airtime Disbursement API lets your application send airtime to mobile subscribers in Kenya. It supports Safaricom, Airtel, and Telkom from the same Statum balance and can be used for rewards, payouts, and scheduled top-ups.
To create a top-up, send an HTTP POST request to the documented endpoint and authenticate it with Basic Auth.
https://api.statum.co.ke/api/v2/airtime
What the airtime API supports
The endpoint is designed for applications that need to fund several mobile networks through one integration:
Apply the account pricing and discounts available to your Statum balance when sending top-ups.
Send top-ups through one endpoint instead of maintaining separate carrier integrations in your application.
Use one wallet while distributing airtime across Safaricom, Airtel, and Telkom.
Use the callback payload to record the final carrier result and reconcile your local order.
Use Cases
- Marketing and incentives: Send airtime to users who complete a survey, download an application, or take part in a campaign.
- Small disbursements: Use airtime as a payout in gaming, digital finance, or other customer-facing services.
- Corporate allocation: Schedule top-ups for sales representatives, field staff, or support teams.
- Retail resale: Add airtime purchases to a website, Android application, or WhatsApp workflow.
Mobile Number Portability (MNP) in Kenya
Mobile Number Portability means that a subscriber can change networks without changing the number. Because a prefix alone is not always enough to identify the current carrier, the routing decision is handled by the service rather than by a hard-coded prefix list in your application.
Transaction Safety & Idempotency
Airtime API Request Parameters
Send a POST request with a JSON body containing the parameters below:
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| phone_number | String | Yes | The recipient phone number in international format without the leading plus sign. | 254721553678 |
| amount | String | Yes | The top-up amount in Kenyan Shillings (KES). Must fall within telco limits. | 50 |
Request Implementation Example
curl -X POST https://api.statum.co.ke/api/v2/airtime \
-H "Authorization: Basic MmJlOTg5ZD...==" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "254721553678",
"amount": "50"
}'
$consumerKey = "YOUR_CONSUMER_KEY";
$consumerSecret = "YOUR_CONSUMER_SECRET";
$auth = base64_encode($consumerKey . ":" . $consumerSecret);
$client = new \GuzzleHttp\Client();
$response = $client->post('https://api.statum.co.ke/api/v2/airtime', [
'headers' => [
'Authorization' => 'Basic ' . $auth,
'Content-Type' => 'application/json'
],
'json' => [
'phone_number' => '254721553678',
'amount' => '50'
]
]);
$result = json_decode($response->getBody()->getContents(), true);
const axios = require('axios');
const auth = Buffer.from('YOUR_CONSUMER_KEY:YOUR_CONSUMER_SECRET').toString('base64');
axios.post('https://api.statum.co.ke/api/v2/airtime', {
phone_number: '254721553678',
amount: '50'
}, {
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json'
}
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
Airtime API Response Format
The initial response is synchronous. It returns the status information available when the request is accepted and queued:
| Parameter | Type | Description | Example |
|---|---|---|---|
| status_code | Number | API operation code (200 = Success, 401 = Unauthorized, etc.) | 200 |
| description | String | Details explaining transaction queuing state. | Operation successful. |
| request_id | String | A unique UUID transaction ID used to map async callbacks. | 35235f08-c981-474a-bd38-8755ed43a427 |
Sample Response Payload
{
"status_code": 200,
"description": "Operation successful.",
"request_id": "35235f08-c981-474a-bd38-8755ed43a427"
}
Asynchronous Webhook Callbacks
After the mobile operator reports the top-up result, the gateway sends the details to your callback URL in an HTTP POST webhook:
| Parameter | Type | Description | Example |
|---|---|---|---|
| request_id | String | The reference ID returned from the initial POST request. | 35235f08-c981-474a-bd38-8755ed43a427 |
| charge | Decimal | The actual amount deducted from your wallet for this top-up (amount minus discount). | 48.50 |
| account_balance | Decimal | Your available wallet balance after the transaction charge. | 5000.00 |
| result_code | Number | Carrier status code representing outcome (200 = Success). | 200 |
| result_desc | String | Human-readable details regarding transaction outcome. | You have topped up 254721553678 with Ksh. 50. |
Sample Webhook Payload
When the disbursement is processed by Safaricom, Airtel, or Telkom, your registered callback URL receives a POST request with the final JSON payload. Use it to update the local transaction and balance records.
{
"request_id": "35235f08-c981-474a-bd38-8755ed43a427",
"charge": 48.5,
"account_balance": 5000,
"result_code": 200,
"result_desc": "You have topped up 254721553678 with Ksh. 50."
}
Sample Callback Handler Implementation
Your callback listener should accept HTTP POST requests with Content-Type: application/json. Use the request_id to match the callback to the original transaction, update your ledger, and return 200 OK within 3 seconds when the payload has been handled.
<?php
// Route target: https://yourdomain.com/api/airtime/callback
$input = file_get_contents('php://input');
$payload = json_decode($input, true);
if (!$payload) {
http_response_code(400);
exit();
}
$requestId = $payload['request_id'];
$resultCode = $payload['result_code'];
if ($resultCode === 200) {
// Airtime disbursement completed successfully. Update local wallet ledger.
updateTransactionStatus($requestId, 'success', $payload['charge']);
} else {
// Airtime disbursement failed. Reverse client action and log error.
updateTransactionStatus($requestId, 'failed', 0, $payload['result_desc']);
}
http_response_code(200);
echo json_encode(['status' => 'acknowledged']);
?>