Reference
Common
Errors & Idempotency
Every error, from any endpoint, is one JSON shape. Combined with mandatory idempotency keys on money movement, any failure is always safe to retry.
The envelope
{
"error": {
"type": "insufficient_funds",
"code": "insufficient_funds",
"message": "Available balance is 1031 cents USD; requested 20000"
}
}
| HTTP | error.type | Meaning ยท what to do |
|---|---|---|
| 400 | invalid_request | Malformed input โ fix the request; don't retry as-is. |
| 401 | auth_error | Missing/unknown/revoked key โ check the header and the key's mode. |
| 402 | insufficient_funds | The movement exceeds your balance. Nothing was debited. |
| 403 | auth_error | Key valid but the account isn't active, or the action isn't allowed (e.g. mockpay on live). |
| 404 | invalid_request | No such object in this mode โ a test key cannot see live objects and vice versa. |
| 409 | invalid_request | Idempotency conflict โ same key, different body (or original still in flight). |
| 429 | rate_limited | Slow down; retry after a short backoff. |
| 502 | gateway_error | InnBucks was briefly unavailable. Retry with the same Idempotency-Key. |
| 500 | api_error | Platform fault โ retry with the same key; contact support if it persists. |
Common error.code values
| Code | Endpoint | Meaning |
|---|---|---|
invalid_amount | any money POST | amount_cents not a positive integer. |
invalid_currency | any money POST | Not USD / ZWG. |
missing_idempotency_key | money POSTs | Add the Idempotency-Key header. |
idempotency_mismatch | money POSTs | Key reused with a different body โ mint a new key per distinct operation. |
idempotency_in_flight | money POSTs | The original request is still processing โ wait and retry the same key. |
code_generation_failed | payments, withdrawal codes | InnBucks couldn't mint a code right now. Retry same key. |
payout_destination_unverified | payouts | Your wallet hasn't been KYC-verified yet โ contact the platform team. |
bank_change_limit | bank changes | Above USD 5.00 โ InnBucks hard limit. |
invalid_msisdn | bank changes, lookup | Phone numbers must be 263XXXXXXXXX. |
invalid_url | webhook endpoints | Live endpoints must be https. |
merchant_inactive | all | Account pending or suspended โ contact the platform team. |
Idempotency keys
Required on every money-moving POST (/v1/payments, /v1/payouts,
/v1/agent/*, /v1/utilities/payments). Semantics:
01
First request
Executes normally; the response is stored against your key for ~48 hours.
โ
02
Retry, same body
The stored response is replayed โ status code and all. Nothing moves twice.
โ
03
Retry, different body
409 idempotency_mismatch. A key identifies one operation, ever.
โฆ
Derive keys from your own identifiers
order-42, invoice-2026-0042, payout-2026-06-11. Then crashes,
timeouts and double-clicks anywhere in your stack collapse into one operation. Random UUIDs work too โ
just persist them with the operation they belong to before calling.
Retry playbook
| You saw | Do this |
|---|---|
| Network timeout / connection reset | Retry with the same Idempotency-Key. If the first attempt actually succeeded you'll get its stored response. |
502 gateway_error | Retry same key with backoff (e.g. 2 s, 5 s, 15 s). The InnBucks gateway hiccupped. |
429 rate_limited | Back off a few seconds. Steady-state integrations rarely hit the 60 req/10 s ceiling. |
409 idempotency_in_flight | The first attempt is still running โ wait 1โ2 s, retry the same key. |
| 4xx anything else | A bug in the request. Fix it and use a new key (it's a new operation). |
Rate limits
- API: 60 requests per 10 seconds per merchant across
/v1/*. - Payment status: poll
GET /v1/payments/:idfreely โ the platform throttles the upstream InnBucks inquiry itself, you can't break the contract. - Webhooks beat polling: if you're rate-limited while polling, you want webhooks.