Error handling
Every error uses one envelope. Branch on error_code, not on the message text.
{
"success": false,
"error": "Human-readable message",
"error_code": "MACHINE_CODE",
"details": {}
}Validation failures return 400 VALIDATION_ERROR with details.fields, for example {"fields": {"amount": ["The amount field is required."]}}.
| error_code | HTTP | When |
|---|---|---|
API_KEY_NOT_IN_HEADER | 401 | An api_key was sent in the query string or body. Only the Authorization header is accepted. |
INVALID_API_KEY | 401 | No Bearer token, or the key is unknown, inactive or revoked. |
API_KEY_EXPIRED | 401 | The key is past its expiry date. |
USER_INACTIVE | 403 | The account is suspended. |
ACCOUNT_NOT_VERIFIED | 403 | The account email is not verified (and not waived by support). |
IP_NOT_ALLOWED | 403 | The key has an IP allowlist and the caller is not on it. |
RATE_LIMIT_EXCEEDED | 429 | Over the subscription tier's per-minute limit. See Rate limiting. |
SERVICE_NOT_PERMITTED | 403 | The key lacks the route's scope (details.required_scope), or a reversal targets a product the key cannot reverse. |
VALIDATION_ERROR | 400 | Bad input. details.fields lists messages per field when the request validator rejected it. |
NOT_FOUND | 404 | Unknown route, or the resource does not exist on your account. |
METHOD_NOT_ALLOWED | 405 | The path exists but not for this HTTP method. |
ENDPOINT_REMOVED | 410 | POST /v3/account/ (signup) and SwiftPay were removed. |
DUPLICATE_REFERENCE | 409 | external_reference was already used on your account. |
URL_NOT_ALLOWED | 400 | callback_url is not HTTPS, has credentials or a non-standard port, or resolves to a private address. |
INSUFFICIENT_SERVICE_BALANCE | 402 | The service wallet cannot cover the fee. |
INSUFFICIENT_PAYMENT_BALANCE | 402 | The payments wallet cannot cover the send amount. |
INSUFFICIENT_BALANCE | 402 | A wallet without a specific type cannot cover the movement. |
PERSONAL_KYC_VERIFICATION_REQUIRED | 403 | Personal KYC must be approved (sending money, or collecting after the KYC deadline). |
CHANNEL_KYC_VERIFICATION_REQUIRED | 403 | The channel requires business KYC that is not approved. |
WALLET_ACTIVATION_REQUIRED | 403 | The wallet channel has not been activated. |
CHANNEL_UNAVAILABLE | 403 | The channel is inactive or suspended by SwiftWallet. |
TRANSACTION_QUALITY_BELOW_THRESHOLD | 403 | This month's success rate is below the platform threshold. |
LIMIT_EXCEEDED | 422 | A daily payout limit was reached. |
REVERSAL_NOT_ALLOWED | 422 | The original transaction cannot be reversed (state, amount mismatch or already reversed). |
MPESA_REQUEST_FAILED | 502 | M-Pesa rejected the request. No money was moved, or it was refunded. |
NO_MERCHANT_AVAILABLE | 503 | No M-Pesa shortcode is available for this service right now. Retry with backoff. |
INTERNAL_ERROR | 500 | Unhandled failure. Retry with backoff and check service status. |
On 5xx and network errors, retry with exponential backoff and look the transaction up by external_reference before sending a payment again.