Skip to main content

Error Handling

Zentra uses conventional HTTP response codes and returns detailed error information to help you debug issues quickly.

Error Response Format

All errors follow this structure:
{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "Invalid email address provided",
    "field": "email",
    "status": 400,
    "details": {
      "reason": "Email format is incorrect"
    }
  }
}

Error Object

FieldTypeDescription
codestringMachine-readable error code
messagestringHuman-readable error message
fieldstringField that caused the error (if applicable)
statusnumberHTTP status code
detailsobjectAdditional error details (optional)

HTTP Status Codes

2xx Success

CodeNameDescription
200OKRequest successful
201CreatedResource created successfully
204No ContentRequest successful, no content returned

4xx Client Errors

CodeNameDescription
400Bad RequestInvalid request format or parameters
401UnauthorizedMissing or invalid API key
403ForbiddenAPI key lacks required permissions
404Not FoundResource doesn’t exist
409ConflictRequest conflicts with current state
422Unprocessable EntityValidation failed
429Too Many RequestsRate limit exceeded

5xx Server Errors

CodeNameDescription
500Internal Server ErrorSomething went wrong on our end
502Bad GatewayUpstream service error
503Service UnavailableAPI temporarily offline
504Gateway TimeoutRequest took too long

Error Codes

Authentication Errors

CodeHTTP StatusDescription
unauthorized401Missing or invalid API key
invalid_api_key401API key format is incorrect
expired_api_key401API key has been revoked
insufficient_permissions403API key lacks required permissions
wrong_environment401Using sandbox key on live endpoint or vice versa
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key provided",
    "status": 401
  }
}

Validation Errors

CodeHTTP StatusDescription
validation_error422Request validation failed
missing_required_field422Required field is missing
invalid_format422Field format is incorrect
invalid_value422Field value is not allowed
{
  "error": {
    "code": "validation_error",
    "message": "Invalid phone number format",
    "field": "phone",
    "status": 422,
    "details": {
      "expected_format": "+234XXXXXXXXXX"
    }
  }
}

Resource Errors

CodeHTTP StatusDescription
resource_not_found404Resource doesn’t exist
resource_already_exists409Resource already exists
resource_deleted410Resource has been deleted
resource_locked423Resource is locked for processing
{
  "error": {
    "code": "resource_not_found",
    "message": "Customer with ID 'cus_123' not found",
    "status": 404
  }
}

Transaction Errors

CodeHTTP StatusDescription
insufficient_balance422Wallet balance is too low
invalid_amount422Amount is invalid or out of range
duplicate_transaction409Transaction with same reference exists
transaction_failed422Transaction processing failed
account_inactive422Account is not active
daily_limit_exceeded422Daily transaction limit exceeded
invalid_bank_code422Bank code is not supported
{
  "error": {
    "code": "insufficient_balance",
    "message": "Insufficient wallet balance",
    "status": 422,
    "details": {
      "required": 10000,
      "available": 5000,
      "currency": "NGN"
    }
  }
}

Rate Limiting Errors

CodeHTTP StatusDescription
rate_limit_exceeded429Too many requests
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Try again in 60 seconds",
    "status": 429,
    "details": {
      "retry_after": 60,
      "limit": 1000,
      "window": "1 minute"
    }
  }
}

Server Errors

CodeHTTP StatusDescription
internal_error500Unexpected server error
service_unavailable503API temporarily offline
gateway_timeout504Request timeout
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again",
    "status": 500,
    "details": {
      "request_id": "req_abc123"
    }
  }
}

Handling Errors

JavaScript/Node.js

try {
  const transfer = await client.transfers.create({
    amountMinor: 10000,
    recipient_account: '0123456789',
    recipient_bank_code: '058'
  });
  
  console.log('Transfer successful:', transfer.id);
  
} catch (error) {
  if (error.response) {
    const { code, message, status } = error.response.error;
    
    switch (code) {
      case 'insufficient_balance':
        console.error('Not enough funds:', message);
        // Show user balance top-up option
        break;
        
      case 'rate_limit_exceeded':
        console.error('Rate limited:', message);
        // Implement exponential backoff
        break;
        
      case 'validation_error':
        console.error('Validation failed:', message);
        // Show form error to user
        break;
        
      default:
        console.error('Error:', message);
    }
  } else {
    console.error('Network error:', error.message);
  }
}

Python

from zentra import Client, ZentraError

client = Client(api_key='your_key')

try:
    transfer = client.transfers.create(
        amount=10000,
        recipient_account='0123456789',
        recipient_bank_code='058'
    )
    
    print(f'Transfer successful: {transfer.id}')
    
except ZentraError as e:
    if e.code == 'insufficient_balance':
        print(f'Not enough funds: {e.message}')
        # Handle insufficient balance
        
    elif e.code == 'rate_limit_exceeded':
        print(f'Rate limited: {e.message}')
        # Implement retry logic
        
    elif e.code == 'validation_error':
        print(f'Validation error: {e.message}')
        # Show error to user
        
    else:
        print(f'Error: {e.message}')

PHP

use Zentra\Client;
use Zentra\Exception\ZentraException;

$client = new Client(['api_key' => 'your_key']);

try {
    $transfer = $client->transfers->create([
        'amount' => 10000,
        'recipient_account' => '0123456789',
        'recipient_bank_code' => '058'
    ]);
    
    echo "Transfer successful: {$transfer->id}";
    
} catch (ZentraException $e) {
    switch ($e->getCode()) {
        case 'insufficient_balance':
            echo "Not enough funds: {$e->getMessage()}";
            break;
            
        case 'rate_limit_exceeded':
            echo "Rate limited: {$e->getMessage()}";
            break;
            
        case 'validation_error':
            echo "Validation error: {$e->getMessage()}";
            break;
            
        default:
            echo "Error: {$e->getMessage()}";
    }
}

Best Practices

Wrap all API calls in try-catch blocks and handle different error types appropriately.
Error messages may change. Always check the code field for programmatic error handling.
Include the request_id from error responses when contacting support.
For 5xx errors and rate_limit_exceeded, implement exponential backoff retry logic.
Don’t expose raw error messages to users. Map errors to friendly messages.
Track error rates in your application to catch issues early.

Retry Logic

For transient errors (network issues, timeouts, server errors), implement retry logic:
async function retryRequest(fn, maxRetries = 3) {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Only retry on specific errors
      if (error.status >= 500 || error.code === 'rate_limit_exceeded') {
        // Exponential backoff: 1s, 2s, 4s
        const delay = Math.pow(2, i) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      // Don't retry client errors
      throw error;
    }
  }
  
  throw lastError;
}

// Usage
const transfer = await retryRequest(() => 
  client.transfers.create({
    amountMinor: 10000,
    destinationAccountNumber: '0123456789',
    destinationBankCode: '058'
  })
);

Error Monitoring

Integrate error tracking tools like Sentry:
const Sentry = require('@sentry/node');

try {
  await client.transfers.create({...});
} catch (error) {
  // Log to Sentry
  Sentry.captureException(error, {
    tags: {
      service: 'zentra',
      error_code: error.code
    },
    extra: {
      request_id: error.request_id
    }
  });
  
  throw error;
}

Getting Help

If you encounter errors you don’t understand:
  1. Check this error reference
  2. Search our FAQ
  3. Contact support with the request_id from the error response
  4. Review Support and escalation paths

Next Steps

Rate Limits

Learn about rate limiting

Webhooks

Handle events asynchronously

Testing

Test error scenarios in sandbox

FAQ

Common questions and answers