Skip to main content

Error Handling

This guide explains how to handle errors when using the Selva API.

Error Response Format

All API errors follow a consistent format:

{
"error": "error_code",
"message": "Human-readable error message",
"details": {
"field": "Additional error details"
}
}

HTTP Status Codes

The API uses standard HTTP status codes:

Status CodeMeaningDescription
200OKRequest successful
201CreatedResource created successfully
204No ContentRequest successful, no content to return
400Bad RequestInvalid request parameters or body
401UnauthorizedInvalid or missing authentication token
403ForbiddenInsufficient permissions
404Not FoundResource does not exist
500Internal Server ErrorServer error occurred
503Service UnavailableService temporarily unavailable

Common Error Codes

Authentication Errors

invalid_client

  • Status: 401
  • Description: Invalid client ID or secret
  • Solution: Verify your credentials are correct
{
"error": "invalid_client",
"message": "Invalid client credentials"
}

invalid_grant

  • Status: 401
  • Description: Invalid or expired authorization code
  • Solution: Request a new authorization code
{
"error": "invalid_grant",
"message": "The authorization code is invalid or has expired"
}

unauthorized

  • Status: 401
  • Description: Invalid or expired access token
  • Solution: Refresh your access token or re-authenticate
{
"error": "unauthorized",
"message": "Invalid or expired access token"
}

Request Errors

invalid_request

  • Status: 400
  • Description: Missing or invalid request parameters
  • Solution: Check required parameters and their formats
{
"error": "invalid_request",
"message": "The request is missing a required parameter",
"details": {
"missing_field": "amount"
}
}

validation_error

  • Status: 400
  • Description: Request body validation failed
  • Solution: Review validation errors and fix the request
{
"error": "validation_error",
"message": "Request validation failed",
"details": {
"amount": "Amount must be greater than 0"
}
}

Resource Errors

not_found

  • Status: 404
  • Description: Requested resource does not exist
  • Solution: Verify the resource ID is correct
{
"error": "not_found",
"message": "The requested resource was not found"
}

forbidden

  • Status: 403
  • Description: Insufficient permissions
  • Solution: Check your OAuth scopes and permissions
{
"error": "forbidden",
"message": "You do not have permission to access this resource"
}

Server Errors

internal_error

  • Status: 500
  • Description: Internal server error
  • Solution: Retry the request with exponential backoff
{
"error": "internal_error",
"message": "An internal server error occurred"
}

service_unavailable

  • Status: 503
  • Description: Service temporarily unavailable
  • Solution: Retry after the indicated retry period
{
"error": "service_unavailable",
"message": "Service temporarily unavailable",
"details": {
"retry_after": 60
}
}

Error Handling Strategies

1. Retry Logic

Implement retry logic for transient errors (5xx status codes):

async function apiCallWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);

if (response.ok) {
return await response.json();
}

// Don't retry client errors
if (response.status >= 400 && response.status < 500) {
const error = await response.json();
throw new Error(error.message);
}

// Retry server errors
if (response.status >= 500) {
throw new Error('Server error');
}
} catch (error) {
if (attempt === maxRetries - 1) {
throw error;
}

// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}

2. Rate Limit Handling

Handle rate limit errors (429 status code):

async function handleRateLimit(response) {
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;

console.log(`Rate limited. Retrying after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
return true; // Indicate retry needed
}
return false;
}

3. Token Refresh

Automatically refresh expired tokens:

async function makeAuthenticatedRequest(url, options = {}) {
let accessToken = getStoredAccessToken();

const response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${accessToken}`,
},
});

// If unauthorized, try refreshing token
if (response.status === 401) {
accessToken = await refreshAccessToken();

// Retry with new token
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${accessToken}`,
},
});
}

return response;
}

4. User-Friendly Error Messages

Map error codes to user-friendly messages:

const errorMessages = {
'invalid_request': 'Please check your input and try again',
'unauthorized': 'Your session has expired. Please log in again',
'not_found': 'The requested item could not be found',
'forbidden': 'You do not have permission to perform this action',
'internal_error': 'A server error occurred. Please try again later',
};

function getUserFriendlyError(errorCode) {
return errorMessages[errorCode] || 'An unexpected error occurred';
}

Validation Errors

Payment and account creation endpoints return detailed validation errors:

{
"error": "validation_error",
"message": "Request validation failed",
"details": {
"amount": "Amount must be greater than 0",
"currency": "Invalid currency code",
"recipient_identifier": "Invalid account identifier format"
}
}

Display these errors to users to help them fix their input.

Webhook Error Handling

When receiving webhooks, always return a 200 status code to acknowledge receipt, even if processing fails:

app.post('/webhooks', async (req, res) => {
try {
// Verify signature
if (!verifySignature(req.headers['signature'], req.body)) {
return res.status(401).json({ error: 'Invalid signature' });
}

// Process webhook
await processWebhook(req.body);

// Always return 200 to acknowledge receipt
res.status(200).json({ status: 'received' });
} catch (error) {
// Log error but still acknowledge receipt
console.error('Webhook processing error:', error);
res.status(200).json({ status: 'received' });
}
});

Testing Error Scenarios

Test your error handling by:

  1. Invalid credentials: Use wrong client ID/secret
  2. Expired tokens: Wait for token expiration
  3. Invalid requests: Send malformed request bodies
  4. Missing resources: Request non-existent IDs
  5. Rate limiting: Make rapid requests

Monitoring and Logging

Log all errors for monitoring and debugging:

async function logError(error, context) {
console.error('API Error:', {
error: error.error,
message: error.message,
status: context.status,
endpoint: context.url,
timestamp: new Date().toISOString(),
});

// Send to error tracking service (e.g., Sentry)
// trackError(error, context);
}

Next Steps