Skip to content

Error codes

All errors follow this format:

{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Payment of €80 exceeds available balance of €42.50",
"details": {
"requested": 80.00,
"available": 42.50
}
}
}
CodeHTTPDescription
INSUFFICIENT_BALANCE422Payment amount exceeds available wallet balance
ACCOUNT_NOT_FOUND404The specified account code does not exist
INVALID_RECIPIENT422The payment destination is not a valid EURD account
AMOUNT_TOO_LOW422Amount is below the minimum for this operation
AMOUNT_TOO_HIGH422Amount exceeds your profile limit
PAYMENT_REQUEST_EXPIRED422The payment request has passed its expiry date
PAYMENT_REQUEST_PAID422This one-off payment request has already been paid
KYC_REQUIRED403Operation requires a higher KYC tier
SCOPE_REQUIRED403API key lacks the required scope (e.g. BusinessPremium)
DUPLICATE_IDEMPOTENCY_KEY200Request already processed — original result returned
ACCOUNT_BALANCE_NOT_ZERO422Cannot delete account with non-zero balance
OTP_EXPIRED422The OTP has expired (valid for 2 minutes)
RATE_LIMIT_EXCEEDED429Too many requests — back off and retry
INTERNAL_ERROR500Unexpected server error — contact support

LLMs read the message field to give users actionable feedback. The details object provides structured data for conditional logic.

// Example: handle insufficient balance gracefully
if (error.code === "INSUFFICIENT_BALANCE") {
const needed = error.details.requested - error.details.available;
return `You need €${needed.toFixed(2)} more in your wallet.
Would you like to top up via iDEAL?`;
}

If you send a request with an Idempotency-Key that was already processed within 24 hours, the server returns the original result with HTTP 200 — not an error. Check for "dry_run": true in the response if you used dry-run mode.