Our API uses conventional HTTP response codes to indicate the success or failure of an API request. In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error that failed given the information provided, and codes in the 5xx range indicate an error with our servers.
| Status Code | Description |
|---|---|
| 200 | OK - The request was successful |
| 201 | Created - The resource was successfully created |
| 400 | Bad Request - The request was invalid or cannot be served |
| 401 | Unauthorized - The request requires authentication |
| 403 | Forbidden - The server understood the request but refuses to authorize it |
| 404 | Not Found - The requested resource could not be found |
| 422 | Unprocessable Entity - The request was well-formed but contains semantic errors |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error - Something went wrong on our end |
| 502 | Bad Gateway - Invalid response from upstream server |
| 503 | Service Unavailable - The server is temporarily unavailable |
All error responses follow a consistent JSON format:
{
"success": false,
"message": "Error description",
"error_code": "ERROR_CODE"
}
{
"success": false,
"message": "Authentication required",
"error_code": "UNAUTHORIZED"
}
{
"success": false,
"message": "Invalid or expired access token",
"error_code": "INVALID_TOKEN"
}
{
"success": false,
"message": "Invalid username or password",
"error_code": "INVALID_CREDENTIALS"
}
{
"success": false,
"message": "Merchant account is suspended",
"error_code": "ACCOUNT_SUSPENDED"
}
{
"success": false,
"message": "Validation failed",
"error_code": "VALIDATION_FAILED"
}
{
"success": false,
"message": "Invalid data format",
"error_code": "INVALID_FORMAT"
}
{
"success": false,
"message": "Insufficient account balance",
"error_code": "INSUFFICIENT_BALANCE"
}
{
"success": false,
"message": "Reference ID already exists",
"error_code": "DUPLICATE_REFERENCE"
}
{
"success": false,
"message": "Invalid bank account",
"error_code": "INVALID_ACCOUNT"
}
{
"success": false,
"message": "Transaction not found",
"error_code": "TRANSACTION_NOT_FOUND"
}
{
"success": false,
"message": "Bank code is not supported",
"error_code": "INVALID_BANK_CODE"
}
{
"success": false,
"message": "Rate limit exceeded. Please try again later",
"error_code": "RATE_LIMIT_EXCEEDED",
"meta": {
"retry_after": 60
}
}
{
"success": false,
"message": "Internal server error",
"error_code": "INTERNAL_ERROR"
}
{
"success": false,
"message": "Service temporarily unavailable",
"error_code": "SERVICE_UNAVAILABLE"
}
{
"success": false,
"message": "System is under maintenance",
"error_code": "MAINTENANCE_MODE"
}
const response = await fetch(url, options);
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error: ${errorData.message}`);
}
function makeApiCallWithRetry($url, $data, $maxRetries = 3) {
for ($i = 0; $i < $maxRetries; $i++) {
try {
$response = makeApiCall($url, $data);
return $response;
} catch (Exception $e) {
if ($e->getCode() >= 500 && $i < $maxRetries - 1) {
sleep(pow(2, $i)); // Exponential backoff
continue;
}
throw $e;
}
}
}
async function handleRateLimit(response) {
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return true; // Retry the request
}
return false;
}
function logApiError($response, $requestData) {
error_log(json_encode([
'timestamp' => date('c'),
'request' => $requestData,
'response' => $response,
'http_code' => $response['http_code'] ?? null
]));
}
function getUserFriendlyMessage(errorCode) {
const messages = {
'INSUFFICIENT_BALANCE': 'Your account balance is insufficient for this transaction.',
'INVALID_ACCOUNT': 'The bank account information is invalid.',
'RATE_LIMIT_EXCEEDED': 'Too many requests. Please try again in a few minutes.',
'SERVICE_UNAVAILABLE': 'Service is temporarily unavailable. Please try again later.',
'DUPLICATE_REFERENCE': 'This transaction reference already exists.',
'INVALID_BANK_CODE': 'The selected bank is not supported.'
};
return messages[errorCode] || 'An unexpected error occurred. Please try again.';
}
request_id from error responses when contacting support// Handle disbursement-specific errors
async function handleDisbursementError(error) {
switch (error.error_code) {
case 'INSUFFICIENT_BALANCE':
// Check balance and notify user to top up
break;
case 'INVALID_ACCOUNT':
// Validate account using check-account endpoint
break;
case 'DUPLICATE_REFERENCE':
// Generate new reference ID
break;
default:
// Handle generic error
break;
}
}
function handleAuthError($errorCode) {
switch ($errorCode) {
case 'INVALID_TOKEN':
// Refresh token by logging in again
return refreshToken();
case 'ACCOUNT_SUSPENDED':
// Contact support
throw new Exception('Account suspended. Please contact support.');
default:
throw new Exception('Authentication failed');
}
}