Internal Services
Bulk SMS Gateway API
Send transactional SMS, OTP alerts, and campaign messages in Kenya with the Statum Bulk SMS API. See sender ID, request, and delivery details.
The Bulk SMS Gateway API gives applications a single way to send transactional alerts, OTPs, and campaign messages. The service supports delivery to Safaricom, Airtel, and Telkom numbers and provides delivery information after the message is handed to the carrier.
Send messages through the documented REST endpoint. Requests require authentication and should be made over HTTPS from your server.
https://api.statum.co.ke/api/v2/sms
SMS Gateway API Features
The SMS service includes the following features:
High-Speed Throughput
Use the API for application messages such as one-time passwords and account alerts.
Delivery reports
Register a callback URL to receive delivery reports from the supported carrier networks.
Full Unicode Support
Send Unicode content, including Kiswahili text, special characters, and emoji, subject to the character limits below.
Secure Authentication
Authenticate requests with HTTP Basic Authentication and keep the credentials in server-side configuration.
Sender ID Registration in Kenya
A custom-branded Sender ID must be registered before it can be used for messages. The checklist below shows the documents and steps used for the submission to the mobile operators.
Required Support Documents
Prepare the following documents before sending the request. The operators use them to verify the business and the requested Sender ID:
- A signed and stamped Authorization Letter.
- A valid PDF of your Business Registration Certificate, Certificate of Incorporation, or Trade Mark Registration.
Authorization Letter Template
Download the template letter, add your company details on letterhead, sign and stamp it, then send it to for processing.
Registration Timeline
Allow approximately 2 to 3 business days for processing across Safaricom, Airtel, and Telkom.
Setup Cost
Custom Sender Name registration costs KES 10,000 per Mobile Operator (Telco).
SMS Character Limits & Concatenation
Message length affects how an SMS is divided and billed. The relevant limits are:
- Single SMS: Up to 160 characters when standard 7-bit GSM encoding is used.
- Concatenated SMS: Longer messages are split and reassembled on the recipient's phone. Each part can contain up to 153 characters because of the concatenation header.
- Unicode encoding: Messages containing characters outside the GSM set use UCS-2 and are limited to 70 characters per part.
SMS API Request Parameters
Send a POST request with a JSON body containing the following parameters:
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| phone_number | String | Yes | Recipient phone number in international format without the leading plus sign. | 254721553678 |
| message | String | Yes | The message body text to deliver. Keep within GSM character standards. | Your verification code is 8847. |
| sender_id | String | Yes | The registered custom Sender ID or short code. | Statum |
Request Implementation Example
curl -X POST https://api.statum.co.ke/api/v2/sms \
-H "Authorization: Basic MmJlOTg5ZD...==" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "254721553678",
"sender_id": "Statum",
"message": "This is a test message through our API."
}'
$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/sms', [
'headers' => [
'Authorization' => 'Basic ' . $auth,
'Content-Type' => 'application/json'
],
'json' => [
'phone_number' => '254721553678',
'sender_id' => 'Statum',
'message' => 'This is a test message through our API.'
]
]);
$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/sms', {
phone_number: '254721553678',
sender_id: 'Statum',
message: 'This is a test message through our API.'
}, {
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json'
}
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
SMS Gateway API Response Format
The gateway returns a synchronous JSON response after receiving the POST request. This response tells you how the request was accepted or rejected:
| Parameter | Type | Description | Example |
|---|---|---|---|
| status_code | Number | Standard response code (200 = Success, 401 = Unauthorized, etc.) | 200 |
| description | String | Human-readable response message. | Operation successful. |
| request_id | String | A unique UUID transaction ID used to map webhook callbacks. | 9b11ac7b-08bf-4cb1-96ca-eb5f8805985f |
Sample Response Payload
{
"status_code": 200,
"description": "Operation successful.",
"request_id": "9b11ac7b-08bf-4cb1-96ca-eb5f8805985f"
}
Delivery Reports & Webhook Setup
After the message is queued, the carrier processes it separately. Register a callback URL if you want the final delivery report as an asynchronous HTTP POST webhook:
| Parameter | Type | Description | Example |
|---|---|---|---|
| request_id | String | The reference ID returned from the initial POST request. | 9b11ac7b-08bf-4cb1-96ca-eb5f8805985f |
| charge | Decimal | The actual financial charge deducted from your wallet for this SMS. | 50 |
| account_balance | Decimal | Your available wallet balance after the charge deduction. | 950.25 |
| result_code | Number | Status code representing transaction outcome (200 = Delivered). | 200 |
| result_desc | String | The description detailing status (e.g. Delivered, Rejected, Failed). | Successfully delivered |
Sample Webhook Payload
The delivery state is updated asynchronously after the message reaches the carrier. Statum sends a POST request with the JSON payload to your callback URL. Use the request_id to associate it with the original request and read the final carrier code.
{
"request_id": "9b11ac7b-08bf-4cb1-96ca-eb5f8805985f",
"charge": 50,
"account_balance": 950.25,
"result_code": 200,
"result_desc": "Successfully delivered"
}
Sample Callback Handler Implementation
Configure a server endpoint that accepts POST requests with Content-Type: application/json. Read the payload, update your message record, and return HTTP 200 OK within 3 seconds after handling it.
<?php
// Route target: https://yourdomain.com/api/sms/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) {
// SMS was successfully delivered to the device. Update database order/alert status.
updateSmsStatus($requestId, 'delivered');
} else {
// Delivery failed (e.g., number out of service, subscriber absent, etc.)
updateSmsStatus($requestId, 'failed', $payload['result_desc']);
}
// Return 200 to acknowledge receipt of webhook
http_response_code(200);
echo json_encode(['status' => 'acknowledged']);
?>