Skip to navigation

Error Handling

All errors return a consistent JSON body and an appropriate HTTP status code.

Error format

{
"error": "VALIDATION_ERROR",
"message": "grossAmount must be positive",
"details": {}
}

HTTP status codes

Statuserror codeWhen
400VALIDATION_ERRORRequest body is invalid
401UNAUTHORIZEDMissing or invalid x-api-key
403FORBIDDENKey exists but lacks permission
404NOT_FOUNDResource doesn’t exist
502PROVIDER_ERRORPayment provider rejected the request
500APP_ERRORStakaba internal error — contact support

Common errors

Invalid API key

{ "error": "UNAUTHORIZED", "message": "Invalid merchant API key" }

Check that you’re sending the key in the x-api-key header (not Authorization).

Amount too small

{ "error": "VALIDATION_ERROR", "message": "grossAmount must be positive" }

Mobile money operators require a minimum of ~500 TZS. Card payments have no set minimum.

Invalid network

{ "error": "VALIDATION_ERROR", "message": "network must be Mpesa, TigoPesa, AirtelMoney, or HaloPesa" }

The network field is case-sensitive. Use the exact values from the networks list.

Payment unavailable

{ "error": "PROVIDER_ERROR", "message": "Payouts are temporarily unavailable. Please try again in a few minutes." }

A temporary issue on the operator’s side. Retry the request after a short wait.

Invalid mobile number

{ "error": "PROVIDER_ERROR", "message": "The recipient number is not registered for mobile money on this network." }

The number exists but is not active on the selected network. Verify the number and network are correct.

Retrying requests

Safe to retry on 502 and 500 errors — use exponential backoff (1s, 2s, 4s). Do not retry 400 or 401 — these indicate a problem with your request that retrying won’t fix.

Each payment request generates a unique internalReference. If you retry, check first whether the original payment succeeded using GET /api/v1/transactions/{reference}.