Errors

MigraPay uses standard HTTP status codes and a consistent JSON envelope to help you diagnose failed requests.

Error response

Failed requests use the same envelope as successful ones. status is "error" and message is a machine-readable error code you can branch on. data adds detail where it helps, often a readable message you can show or log.

422 Unprocessable Entity
{
  "status": "error",
  "message": "RECIPIENT_INACTIVE",
  "data": { "message": "This recipient is inactive. Reactivate it to send money." }
}

Validation errors

When the body or query fails validation, data maps each invalid field (as a dotted path) to the first problem found with it:

400 Bad Request
{
  "status": "error",
  "message": "VALIDATION_ERROR",
  "data": {
    "currency": "String must contain exactly 3 character(s)",
    "destination.accountNumber": "Account number is required"
  }
}

HTTP status codes

StatusMeaningWhat to do
400Bad RequestThe request is invalid. Fix the fields listed in data before retrying.
401UnauthorizedThe API key is missing or invalid. Check the key and its environment.
403ForbiddenThe key lacks the scope, or the action is not allowed for this key.
404Not FoundThe resource does not exist in this environment. Check the id.
409ConflictThe value is already in use (ALREADY_EXISTS), or an Idempotency-Key clash.
422Unprocessable EntityWell-formed but not possible right now, such as an insufficient balance, an unsupported corridor or an account restriction.
429Too Many RequestsYou hit the rate limit (RATE_LIMITED). Wait for Retry-After.
500Server ErrorSomething went wrong on our side (SOMETHING_WENT_WRONG).
502Bad GatewayThe receiving network rejected or failed the payout. Funds are returned to your wallet.
503Service UnavailableRates or a payout route are temporarily unavailable. Retry later.

Common error codes

CodeStatusReturned when
VALIDATION_ERROR400Required fields are missing or malformed.
INVALID_JSON400The request body is not valid JSON.
IDEMPOTENCY_KEY_INVALID400The Idempotency-Key is longer than 255 characters.
IDEMPOTENCY_KEY_REUSED409The Idempotency-Key was already used with a different body.
IDEMPOTENCY_KEY_IN_USE409A request with the same Idempotency-Key is still running.
RATE_LIMITED429Too many requests. See Rate limits.
missing_api_key401No API key was sent.
invalid_api_key401The API key is unknown or revoked.
insufficient_scope403The key lacks the scope for this endpoint.
INSUFFICIENT_BALANCE422The source wallet cannot cover the payout and its fees.
AMOUNT_BELOW_MINIMUM400The payout is under the corridor minimum (data.min, data.currency).
AMOUNT_ABOVE_MAXIMUM400The payout is over the corridor maximum (data.max, data.currency).
UNSUPPORTED_CORRIDOR422Payouts to this currency or country are not available.
RAIL_NOT_AVAILABLE422The payout method is not available for this currency.
INSTITUTION_UNKNOWN422The bank or mobile money code is not recognised. Use GET /payouts/banks.
ACCOUNT_NOT_VERIFIED422The recipient's bank could not confirm the account (NGN bank recipients and payouts, validate-account). data.message is a sentence you can show your user. Check the bank code and account number. See Bank-confirmed names.
WALLET_NOT_FOUND404You have no wallet in that currency.
RECIPIENT_NOT_FOUND404No recipient with that id in this environment.
TRANSACTION_NOT_FOUND404No payout or transaction with that id or reference.
REQUEST_NOT_FOUND404No collection with that id.
business_restricted422Payouts are paused on your account. Contact support.
limit_exceeded422The payout is over a limit set on your account.
PAYOUT_FAILED502The receiving network declined the payout. Your wallet is credited back.
RATES_UNAVAILABLE503Exchange rates are temporarily unavailable.
TEMPORARILY_UNAVAILABLE503This route is briefly unavailable. Retry later.
UPSTREAM_UNAVAILABLE503We could not reach the payout network. Retry later.
SOMETHING_WENT_WRONG500An unexpected error.

Each endpoint page lists the errors specific to it.

Retry guidance

  • Do not retry 4xx errors unchanged. Fix the request first. The exception is 429: wait for Retry-After, then retry.
  • Retry 500 and 503 with exponential backoff for reads, and for POST /collections and POST /payouts sent with an Idempotency-Key.
  • Never retry a payout sent without an Idempotency-Key until you have checked GET /payouts. A timeout does not mean it failed, and you may pay twice.
  • Log the full response body (status, message and data) of every failed call; include it when you contact support.