Skip to main content

Error Handling

All API errors follow a consistent format.

Error Response Format

{
  "error": "Error Type",
  "message": "Human-readable description",
  "code": "ERROR_CODE"
}

HTTP Status Codes

CodeMeaning
400Bad Request - Invalid parameters
401Unauthorized - Invalid/missing API key
403Forbidden - IP not whitelisted
404Not Found - Resource doesn’t exist
409Conflict - Resource already exists
422Unprocessable - Validation failed
429Too Many Requests - Rate limited
500Internal Error - Our fault

Common Error Codes

User Errors

CodeDescription
USER_EXISTSMobile number already registered
USER_NOT_FOUNDUser ID doesn’t exist
KYC_INCOMPLETEUser hasn’t completed KYC
NO_BANK_ACCOUNTNo verified bank account

Transaction Errors

CodeDescription
PENDING_TRANSACTION_EXISTSUser has an active transaction
AMOUNT_TOO_LOWBelow minimum ($10)
AMOUNT_TOO_HIGHAbove maximum ($10,000)
QUOTE_EXPIREDTransaction expired

Payout Errors

CodeDescription
KYC_NOT_VERIFIEDUser KYC status is not verified at payout time
KYC_LEVEL_INSUFFICIENTUser must complete at least basic KYC
BANK_ACCOUNT_INVALIDBank account not found or not verified
PAYOUT_FAILEDPayout provider rejected the transfer

KYC Errors

CodeDescription
PAN_INVALIDInvalid PAN format
PAN_NOT_FOUNDPAN not in database
AADHAAR_OTP_EXPIREDOTP session expired
BANK_VERIFICATION_FAILEDPenny drop failed
NAME_MISMATCHDocument names don’t match

Error Handling Example

async function createTransaction(userId, amount) {
  try {
    const response = await fetch('/v2/transactions', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ userId, amount, asset: 'USDC', network: 'polygon' })
    });

    if (!response.ok) {
      const error = await response.json();

      switch (error.code) {
        case 'KYC_INCOMPLETE':
          return { error: 'Please complete KYC first' };
        case 'PENDING_TRANSACTION_EXISTS':
          return { error: 'You have an active transaction' };
        case 'AMOUNT_TOO_LOW':
          return { error: 'Minimum amount is $10' };
        default:
          return { error: error.message };
      }
    }

    return await response.json();
  } catch (err) {
    return { error: 'Network error. Please try again.' };
  }
}

Idempotency

For safe retries, use idempotency keys:
curl -X POST https://api.stablepay.global/v2/transactions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: unique-request-id-123" \
  -d '{"userId": "usr_abc", "amount": "100", ...}'
Same idempotency key = same response (no duplicate transactions).