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.

POST
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:

Built-in Reseller Discounts

Apply the account pricing and discounts available to your Statum balance when sending top-ups.

One airtime endpoint

Send top-ups through one endpoint instead of maintaining separate carrier integrations in your application.

Shared account balance

Use one wallet while distributing airtime across Safaricom, Airtel, and Telkom.

Asynchronous Webhooks

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

Initiate Airtime Top-Up
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

Success Response
{
    "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.

Callback Notification
{
    "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 Callback Handler
<?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']);
?>