Build
Errors
Every error is JSON. request_id is the id to quote if you ask for help. param is present when one field is the problem.
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Available balance is not enough for this payout.",
"request_id": "req_..."
}
}| HTTP | Code | What to do |
|---|---|---|
| 400 | VALIDATION_ERROR | Fix the field named in param. Amounts are decimal strings. Phones are E.164. |
| 401 | INVALID_API_KEY | The key is missing, revoked, or for the wrong environment. |
| 401 | INVALID_CREDENTIALS | The portal password is wrong, or the account is locked. |
| 403 | INSUFFICIENT_SCOPE | The key does not include that action. |
| 403 | MERCHANT_NOT_ACTIVE | The account is not allowed to move money yet. |
| 403 | LIMIT_EXCEEDED | The amount is over the merchant limit. |
| 404 | PAYMENT_NOT_FOUND | That payment id is not on this merchant. |
| 404 | PAYOUT_NOT_FOUND | That payout id is not on this merchant. |
| 409 | IDEMPOTENCY_KEY_REUSED | The key was already used with a different body. Use the original response, or choose a new key for a new request. |
| 409 | IDEMPOTENCY_IN_PROGRESS | The same key is still running. Wait, then retry that same key. |
| 409 | INSUFFICIENT_BALANCE | Top up before the payout. Nothing was reserved. |
| 422 | PROVIDER_UNAVAILABLE | No route is configured, or the rail is down. Retry later. |
| 422 | FEE_EXCEEDS_AMOUNT | The fee is larger than the amount. Increase the amount or change the fee setup. |
| 429 | RATE_LIMITED | Wait and retry. Retry-After is in seconds. |
A collection can also succeed at the HTTP layer and then fail on the wallet. That failure is on the payment: status failed and a failure_message. A blank rejection is shown as “The payment was rejected.”
How to handle one
Read error.code first. Show error.message to the person who can fix the request. Store request_id with your log line. If param is present, the named field is the one to change. Do not retry a 400 or a 409 IDEMPOTENCY_KEY_REUSED unchanged. Do retry a 429 after Retry-After, and retry a timed-out create with the same idempotency key.
Payment failure is not this object
When the HTTP status is 201, there is no error object. A later wallet refusal is status: failed on the payment, with failure_message. Your order system should listen for that on the webhook, not only for HTTP errors.