Official Java SDK for Statum APIs. Built for secure, production-grade enterprise usage with strict typing, immutable DTO records, and built-in HTTP client connection pooling. Easily send SMS alerts, automate airtime disbursements, and query real-time account balances.
- Features
- Getting Started
- Installation
- Quick Start
- Core Integration Examples
- Error & Exception Handling
- API JSON Payload Specifications
- Integration Guidelines & Gotchas
- License
- JDK 17+ Records: Uses modern Java Records and sealed types for absolute DTO immutability.
- Zero Heavy Dependencies: Uses the native
java.net.http.HttpClientunder the hood (only requires Jackson for lightweight JSON binding). - Thread-Safe: Designed to be instantiated once and shared across multiple application threads.
- Service-Oriented: Logical division between Airtime, SMS, and Account Management APIs.
- Extensive Exceptions: Maps standard HTTP status code failures (401, 403, 422, 5xx) to concrete Java exception classes.
- Sign up for a Statum account: app.statum.co.ke
- Get your API credentials: Retrieve your Consumer Key and Consumer Secret from the Statum Dashboard.
- Read the full API documentation: docs.statum.co.ke
- Install the SDK: Follow the Installation guidelines below.
Add the dependency to your pom.xml:
<dependency>
<groupId>ke.co.statum</groupId>
<artifactId>statum-java-sdk</artifactId>
<version>1.0.3</version>
</dependency>Add the dependency to your build.gradle:
implementation 'ke.co.statum:statum-java-sdk:1.0.3'Ensure your credentials are saved in your system environment variables:
export STATUM_CONSUMER_KEY="your-consumer-key"
export STATUM_CONSUMER_SECRET="your-consumer-secret"Instantiate and reuse the thread-safe client:
import ke.co.statum.sdk.StatumClient;
import ke.co.statum.sdk.config.StatumConfig;
StatumConfig config = new StatumConfig(
System.getenv("STATUM_CONSUMER_KEY"),
System.getenv("STATUM_CONSUMER_SECRET")
);
StatumClient client = new StatumClient(config);Add the properties to your application.properties:
statum.consumer-key=${STATUM_CONSUMER_KEY}
statum.consumer-secret=${STATUM_CONSUMER_SECRET}
statum.base-url=https://api.statum.co.ke/api/v2
statum.timeout-seconds=30Create a configuration class to register the StatumClient bean:
import ke.co.statum.sdk.StatumClient;
import ke.co.statum.sdk.config.StatumConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class StatumConfiguration {
@Bean
public StatumClient statumClient(
@Value("${statum.consumer-key}") String consumerKey,
@Value("${statum.consumer-secret}") String consumerSecret,
@Value("${statum.base-url:https://api.statum.co.ke/api/v2}") String baseUrl,
@Value("${statum.timeout-seconds:30}") int timeoutSeconds) {
StatumConfig config = new StatumConfig(
consumerKey,
consumerSecret,
baseUrl,
Duration.ofSeconds(timeoutSeconds)
);
return new StatumClient(config);
}
}Now, inject the client into any @Service or @RestController:
import ke.co.statum.sdk.StatumClient;
import ke.co.statum.sdk.model.ApiResponse;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
private final StatumClient statumClient;
public NotificationService(StatumClient statumClient) {
this.statumClient = statumClient;
}
public void alertUser(String phoneNumber, String message) {
ApiResponse response = statumClient.getSmsService()
.sendSms(phoneNumber, "STATUM", message);
System.out.println("Alert sent. ID: " + response.requestId());
}
}Fetch organization profile, available balances, and service configuration:
import ke.co.statum.sdk.model.AccountDetailsResponse;
AccountDetailsResponse response = client.getAccountService().getAccountDetails();
System.out.println("Status Code: " + response.statusCode());
System.out.println("Organization: " + response.organization().name());
System.out.println("Available Balance: KES " + response.organization().details().availableBalance());
// List registered services and account codes
response.organization().accounts().forEach(account -> {
System.out.println("Service: " + account.serviceName() + " | Code: " + account.account());
});Send transactional or promotional SMS alerts to a Kenyan phone number:
import ke.co.statum.sdk.model.ApiResponse;
ApiResponse response = client.getSmsService().sendSms(
"254721553678", // Phone number in international format
"STATUM", // Your approved Sender ID
"Hello from the Statum Java SDK!" // Message content
);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Description: " + response.description());
System.out.println("Request ID: " + response.requestId());Disburse airtime rewards or incentives (supports amounts from KES 5 to KES 10,000):
import ke.co.statum.sdk.model.ApiResponse;
ApiResponse response = client.getAirtimeService().sendAirtime(
"254721553678", // Phone number in international format
"100" // Amount must be passed as a String representation
);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Description: " + response.description());
System.out.println("Request ID: " + response.requestId());The SDK maps standard HTTP responses to concrete exception classes that inherit from ke.co.statum.sdk.exceptions.ApiException.
import ke.co.statum.sdk.exceptions.*;
import ke.co.statum.sdk.model.ApiResponse;
try {
ApiResponse response = client.getSmsService().sendSms("2547XXXXXXXX", "STATUM", "Message");
} catch (AuthenticationException e) {
// Credentials failed validation (HTTP 401)
System.err.println("Auth Failure: Check Consumer Key and Secret.");
} catch (AuthorizationException e) {
// Access denied or forbidden resource (HTTP 403)
System.err.println("Access Denied: " + e.getMessage());
} catch (ValidationException e) {
// API-side validation parameters failed (HTTP 422)
System.err.println("Validation failed. Body: " + e.getResponseBody());
e.getValidationErrors().forEach((field, errors) -> {
System.err.println("Field: " + field + " | Errors: " + String.join(", ", errors));
});
} catch (NetworkException e) {
// DNS, connection timeouts, or socket failures
System.err.println("Connection error: " + e.getMessage());
} catch (ApiException e) {
// General API errors (e.g. 402 Insufficient Funds, 500 Server Error)
System.err.println("HTTP Status Code: " + e.getStatusCode());
System.err.println("Error Body: " + e.getResponseBody());
}Here are the wire-level JSON schemas transmitted and returned by the APIs under the hood:
- Endpoint:
POST /sms - Headers:
Authorization: Basic <base64(key:secret)>
JSON Request
{
"phone_number": "254721553678",
"sender_id": "STATUM",
"message": "Hello from Statum SDK!"
}JSON Response (Success - 200)
{
"status_code": 200,
"description": "Operation successful.",
"request_id": "d173a8b3-0f3a-463f-8a03-29826b9a2d78"
}- Endpoint:
POST /airtime
JSON Request
{
"phone_number": "254721553678",
"amount": "100"
}JSON Response (Success - 200)
{
"status_code": 200,
"description": "Operation successful.",
"request_id": "6e0213d5-6df9-47bf-ba2d-9b9470d96854"
}- Endpoint:
GET /account-details
JSON Response (Success - 200)
{
"status_code": 200,
"description": "Operation successful.",
"request_id": "5a45bc7b-bf99-49ae-b089-9daf5f4adbb0",
"organization": {
"name": "Statum Test",
"details": {
"available_balance": 695.15,
"location": "Nairobi - Westlands",
"website": "www.statum.co.ke",
"office_email": "admin@statum.co.ke",
"office_mobile": "+254722199199",
"mpesa_account_top_up_code": "B9E573"
},
"accounts": [
{ "account": "Statum", "service_name": "sms" },
{ "account": "CONNECT", "service_name": "sms" }
]
}
}- Sender ID Approval: SMS requests will throw an HTTP 422
ValidationExceptionif thesenderIdis not registered and approved under your Statum account profile. - Phone Number Formatting: Ensure phone numbers are passed in the international format (with or without
+prefix), e.g.254721553678or+254721553678. - Airtime Limits: Airtime amounts must be passed as strings (e.g.
'100') and must fall strictly within the KES 5 to KES 10,000 range per transaction. - Jackson Deserialization Dependency: If running outside of Spring Boot (which manages Jackson dependencies automatically), ensure that
jackson-databindis present on your classpath.
This project is licensed under the MIT License. See LICENSE for details.