Errors
Every error is predictable. Match oncode — not message — to drive your error handling logic.
{
"success": false,
"error": {
"message": "Insufficient service wallet balance to cover the transaction fee.",
"code": "INSUFFICIENT_SERVICE_BALANCE",
"details": {}
},
"requestId": "c1b2a3d4-e5f6-7890-abcd-ef1234567890"
}
requestId from every error. It is required when contacting support.
HTTP status codes
| Status | Meaning |
|---|---|
200 | Success |
204 | Success (no body) |
400 | Validation error — fix the request |
401 | Authentication failed |
402 | Insufficient balance |
403 | Permission denied |
404 | Resource not found |
409 | Conflict — resource state prevents the action |
422 | Unprocessable entity |
429 | Rate limit or abuse protection triggered |
500 | Internal server error — contact support |
Error codes
Authentication
| Code | HTTP | What it means | What to do |
|---|---|---|---|
INVALID_API_KEY | 401 | Key not found, revoked, or header malformed | Verify the key and Authorization header format. Do not retry automatically. |
TENANT_INACTIVE | 401 | Account is suspended | Contact support to resolve the account status. |
Rate limiting
| Code | HTTP | What it means | What to do |
|---|---|---|---|
RATE_LIMIT_EXCEEDED | 429 | 60 requests/minute exceeded | Wait for Retry-After seconds, then retry. |
STK_TEMP_BANNED | 429 | STK Push temporarily disabled due to suspected abuse | Wait details.retryAfterSeconds before retrying. |
STK_TEMP_BANNED includes:
{
"retryAfterSeconds": 3600,
"bannedUntil": "2026-03-01T12:00:00.000Z",
"scope": "apiKey"
}
Payments
| Code | HTTP | What it means | What to do |
|---|---|---|---|
INSUFFICIENT_SERVICE_BALANCE | 402 | Service wallet too low to cover the fee | Top up the service wallet, then retry. |
INVALID_PHONE | 400 | Phone number format is invalid | Send a valid Kenyan number: 07XXXXXXXX, 01XXXXXXXX, or 254XXXXXXXXX. |
CREDENTIAL_NOT_FOUND | 404 | Specified credentialId does not exist | Verify the credential ID in the console. |
CREDENTIAL_INACTIVE | 409 | Specified credential is disabled | Re-enable the credential in the console. |
CHANNEL_NOT_FOUND | 400 | Specified channelId does not exist for your account | Verify the channel ID in the console, or omit it to use your default channel. |
NO_PAYMENT_CHANNELS | 400 | Account has no payment channels configured | Create at least one payment channel in the console before initiating payments. |
NO_DEFAULT_CHANNEL | 400 | Channels exist but none is marked as default | Set a channel as default in the console, or pass an explicit channelId in the request. |
B2C payouts
| Code | HTTP | What it means | What to do |
|---|---|---|---|
ACCOUNT_INACTIVE | 403 | Account is not active | Contact support. |
KYC_NOT_VERIFIED | 403 | KYC approval required | Submit KYC from the console and await approval. |
INSUFFICIENT_FUNDS | 409 | B2C wallet balance too low | Top up the B2C wallet, then retry. |
WALLET_INACTIVE | 409 | B2C wallet is not active | Contact support to activate the wallet. |
UNSUPPORTED_CURRENCY | 400 | Only KES is supported | Send "currency": "KES". |
Transactions
| Code | HTTP | What it means | What to do |
|---|---|---|---|
TRANSACTION_NOT_FOUND | 404 | No transaction with that ID for your account | Verify the ID. Check that the key has transactions:read scope. |
Channels
| Code | HTTP | What it means | What to do |
|---|---|---|---|
CHANNEL_IN_USE | 409 | Channel has linked transactions — cannot be deleted | Wait for active transactions to settle, then delete. |
Retry guidance
| Status | Retry? | Notes |
|---|---|---|
400 | No | Fix the request first |
401 | No | Fix credentials first |
402 | No | Top up wallet first |
403 | No | Resolve account or KYC issue first |
404 | No | Resource does not exist |
409 | No | Resolve conflict state first |
429 | Yes | Wait for Retry-After seconds |
500 | Yes | Exponential backoff: 1s, 2s, 4s, 8s |
| Network timeout | Yes | Exponential backoff |