Skip to main content

Error Handling

This guide covers all API error codes, response formats, and best practices for handling errors in your Torque API integration.

๐Ÿš€ Quick Startโ€‹

Basic Error Handlingโ€‹

try {
const response = await fetch('https://api.torque.fi/v1/checkout/generate-link', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});

if (!response.ok) {
const error = await response.json();
throw new TorqueError(error.error, error.code, error.details);
}

return await response.json();
} catch (error) {
if (error instanceof TorqueError) {
console.error('Torque API Error:', error.message, error.code);
// Handle specific error types
switch (error.code) {
case 'AUTH_001':
// Handle authentication error
break;
case 'CHECKOUT_001':
// Handle checkout error
break;
default:
// Handle unknown error
}
} else {
console.error('Network or other error:', error);
}
}

Error Response Formatโ€‹

Standard Error Responseโ€‹

All API errors follow this consistent format:

{
"success": false,
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": {
"field": "Specific field that caused the error",
"message": "Detailed error description",
"value": "Value that caused the error"
},
"timestamp": "2024-01-15T10:00:00Z",
"requestId": "req_abc123"
}

Error Response Fieldsโ€‹

  • success: Always false for errors
  • error: Human-readable error message
  • code: Machine-readable error code
  • details: Additional error context (optional)
  • timestamp: When the error occurred
  • requestId: Unique request identifier for support

HTTP Status Codesโ€‹

Client Errors (4xx)โ€‹

  • 400 Bad Request: Invalid request data or parameters
  • 401 Unauthorized: Missing or invalid authentication
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Resource not found
  • 409 Conflict: Resource conflict or duplicate
  • 422 Unprocessable Entity: Validation errors
  • 429 Too Many Requests: Rate limit exceeded

Server Errors (5xx)โ€‹

  • 500 Internal Server Error: Unexpected server error
  • 502 Bad Gateway: Upstream service error
  • 503 Service Unavailable: Service temporarily unavailable
  • 504 Gateway Timeout: Request timeout

๐Ÿ”‘ Authentication Errorsโ€‹

AUTH_001: Invalid or Missing API Keyโ€‹

Status: 401 Unauthorized

{
"success": false,
"error": "Invalid or missing API key",
"code": "AUTH_001",
"details": {
"field": "Authorization",
"message": "API key is required in Authorization header"
}
}

Causes:

  • Missing Authorization header
  • Invalid API key format
  • Expired or revoked API key

Solutions:

// Ensure proper header format
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}

// Verify API key is valid
const testResponse = await fetch('https://api.torque.fi/v1/auth/test', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});

AUTH_002: Insufficient Permissionsโ€‹

Status: 403 Forbidden

{
"success": false,
"error": "Insufficient permissions for this endpoint",
"code": "AUTH_002",
"details": {
"required": "business:write",
"current": "business:read"
}
}

Causes:

  • API key doesn't have required permissions
  • Business account is suspended
  • Endpoint requires elevated access

Solutions:

  • Check API key permissions in dashboard
  • Contact support for elevated access
  • Verify business account status

AUTH_003: API Key Expiredโ€‹

Status: 401 Unauthorized

{
"success": false,
"error": "API key has expired",
"code": "AUTH_003",
"details": {
"expiredAt": "2024-01-15T00:00:00Z"
}
}

Solutions:

  • Generate new API key
  • Update applications with new key
  • Set up key rotation schedule

๐Ÿ›’ Checkout Errorsโ€‹

CHECKOUT_001: Invalid Cart Dataโ€‹

Status: 400 Bad Request

{
"success": false,
"error": "Invalid cart data",
"code": "CHECKOUT_001",
"details": {
"field": "cart.items",
"message": "At least one item is required",
"value": []
}
}

Common Issues:

  • Empty cart items array
  • Missing required item fields
  • Invalid price format

Solutions:

// Ensure cart has items
const cart = {
items: [
{
productId: 'prod_1',
quantity: 1,
price: 29.99
}
]
};

// Validate before sending
if (!cart.items || cart.items.length === 0) {
throw new Error('Cart must contain at least one item');
}

CHECKOUT_002: Invalid Customer Dataโ€‹

Status: 400 Bad Request

{
"success": false,
"error": "Invalid customer data",
"code": "CHECKOUT_002",
"details": {
"field": "customerData.email",
"message": "Valid email address is required",
"value": "invalid-email"
}
}

Common Issues:

  • Invalid email format
  • Missing required fields
  • Invalid phone number format

Solutions:

// Validate customer data
const customerData = {
email: 'customer@example.com', // Required
firstName: 'John', // Required
lastName: 'Doe' // Required
};

// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(customerData.email)) {
throw new Error('Invalid email format');
}

CHECKOUT_003: Business Not Foundโ€‹

Status: 404 Not Found

{
"success": false,
"error": "Business not found",
"code": "CHECKOUT_003",
"details": {
"businessId": "business_999",
"message": "Business with this ID does not exist"
}
}

Solutions:

  • Verify business ID is correct
  • Check business account is active
  • Use business ID from dashboard

CHECKOUT_004: Checkout Session Expiredโ€‹

Status: 410 Gone

{
"success": false,
"error": "Checkout session has expired",
"code": "CHECKOUT_004",
"details": {
"sessionId": "session_abc123",
"expiredAt": "2024-01-15T10:30:00Z"
}
}

Solutions:

  • Generate new checkout link
  • Reduce session expiry time
  • Implement session refresh logic

Webhook Errorsโ€‹

WEBHOOK_001: Invalid Signatureโ€‹

Status: 401 Unauthorized

{
"success": false,
"error": "Invalid webhook signature",
"code": "WEBHOOK_001",
"details": {
"field": "X-Torque-Signature",
"message": "Signature verification failed"
}
}

Solutions:

// Verify webhook signature
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}

WEBHOOK_002: URL Unreachableโ€‹

Status: 400 Bad Request

{
"success": false,
"error": "Webhook URL is unreachable",
"code": "WEBHOOK_002",
"details": {
"url": "https://example.com/webhooks",
"message": "HTTP request failed with status 404"
}
}

Solutions:

  • Verify webhook endpoint is accessible
  • Check HTTPS is enabled
  • Test endpoint manually

WEBHOOK_003: Processing Timeoutโ€‹

Status: 408 Request Timeout

{
"success": false,
"error": "Webhook processing timeout",
"code": "WEBHOOK_003",
"details": {
"timeout": 30000,
"message": "Response not received within 30 seconds"
}
}

Solutions:

  • Process webhooks asynchronously
  • Return quickly and process in background
  • Use queue systems for heavy processing

๐Ÿ“Š Analytics Errorsโ€‹

ANALYTICS_001: Invalid Date Rangeโ€‹

Status: 400 Bad Request

{
"success": false,
"error": "Invalid date range",
"code": "ANALYTICS_001",
"details": {
"field": "endDate",
"message": "End date must be after start date",
"value": "2024-01-01T00:00:00Z"
}
}

Solutions:

// Validate date range
const startDate = new Date('2024-01-01');
const endDate = new Date('2024-01-31');

if (endDate <= startDate) {
throw new Error('End date must be after start date');
}

// Use ISO 8601 format
const params = {
startDate: startDate.toISOString(),
endDate: endDate.toISOString()
};

ANALYTICS_002: Export Failedโ€‹

Status: 500 Internal Server Error

{
"success": false,
"error": "Export generation failed",
"code": "ANALYTICS_002",
"details": {
"exportId": "export_abc123",
"message": "Failed to process large dataset"
}
}

Solutions:

  • Reduce date range size
  • Use smaller time periods
  • Contact support for large exports

โšก Rate Limiting Errorsโ€‹

RATE_001: Rate Limit Exceededโ€‹

Status: 429 Too Many Requests

{
"success": false,
"error": "Rate limit exceeded",
"code": "RATE_001",
"details": {
"limit": 1000,
"reset": 1642233600,
"retryAfter": 60
}
}

Solutions:

// Implement exponential backoff
async function makeRequestWithRetry() {
try {
return await makeApiRequest();
} catch (error) {
if (error.code === 'RATE_001') {
const retryAfter = error.details.retryAfter || 60;
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return makeRequestWithRetry();
}
throw error;
}
}

// Check rate limit headers
const response = await fetch('/api/endpoint');
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');

if (parseInt(remaining) < 10) {
// Implement rate limiting logic
await delay(1000);
}

๐Ÿ”„ Retry Strategiesโ€‹

Exponential Backoffโ€‹

class TorqueClient {
constructor(apiKey, options = {}) {
this.apiKey = apiKey;
this.maxRetries = options.maxRetries || 3;
this.baseDelay = options.baseDelay || 1000;
}

async makeRequest(endpoint, options = {}) {
let lastError;

for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
const response = await fetch(endpoint, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers
}
});

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

// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
throw new TorqueError(error.error, error.code, error.details);
}

// Retry server errors (5xx) and rate limits
if (response.status >= 500 || response.status === 429) {
throw new Error(`HTTP ${response.status}: ${error.error}`);
}
}

return await response.json();
} catch (error) {
lastError = error;

if (attempt === this.maxRetries) {
break;
}

// Calculate delay with exponential backoff
const delay = this.baseDelay * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
}
}

throw lastError;
}
}

Circuit Breaker Patternโ€‹

class CircuitBreaker {
constructor(failureThreshold = 5, resetTimeout = 60000) {
this.failureThreshold = failureThreshold;
this.resetTimeout = resetTimeout;
this.failureCount = 0;
this.lastFailureTime = null;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
}

async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.resetTimeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}

try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}

onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}

onFailure() {
this.failureCount++;
this.lastFailureTime = Date.now();

if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
}
}
}

// Usage
const circuitBreaker = new CircuitBreaker();
const client = new TorqueClient(apiKey);

const result = await circuitBreaker.execute(() =>
client.makeRequest('/api/endpoint')
);

๐Ÿ“ Error Loggingโ€‹

Structured Error Loggingโ€‹

class ErrorLogger {
constructor() {
this.logger = console; // Replace with your logging service
}

logError(error, context = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
level: 'ERROR',
error: {
message: error.message,
code: error.code,
details: error.details
},
context: {
endpoint: context.endpoint,
requestId: context.requestId,
userId: context.userId,
...context
}
};

this.logger.error(JSON.stringify(logEntry));
}

logApiError(response, context = {}) {
const error = {
status: response.status,
statusText: response.statusText,
url: response.url,
...context
};

this.logger.error('API Error:', error);
}
}

// Usage
const errorLogger = new ErrorLogger();

try {
const response = await client.makeRequest('/api/endpoint');
return response;
} catch (error) {
errorLogger.logError(error, {
endpoint: '/api/endpoint',
requestId: 'req_123',
userId: 'user_456'
});
throw error;
}

๐Ÿงช Testing Error Handlingโ€‹

Error Simulationโ€‹

// Test error handling with mock responses
class MockTorqueAPI {
constructor() {
this.errorMode = false;
this.errorCode = 'AUTH_001';
}

setErrorMode(enabled, code = 'AUTH_001') {
this.errorMode = enabled;
this.errorCode = code;
}

async makeRequest(endpoint) {
if (this.errorMode) {
const errorResponses = {
'AUTH_001': { status: 401, error: 'Invalid API key' },
'CHECKOUT_001': { status: 400, error: 'Invalid cart data' },
'RATE_001': { status: 429, error: 'Rate limit exceeded' }
};

const error = errorResponses[this.errorCode];
throw new TorqueError(error.error, this.errorCode);
}

return { success: true, data: 'Mock response' };
}
}

// Test error handling
const mockAPI = new MockTorqueAPI();

// Test authentication error
mockAPI.setErrorMode(true, 'AUTH_001');
try {
await mockAPI.makeRequest('/api/endpoint');
} catch (error) {
console.log('Handled auth error:', error.code);
}

// Test rate limiting
mockAPI.setErrorMode(true, 'RATE_001');
try {
await mockAPI.makeRequest('/api/endpoint');
} catch (error) {
console.log('Handled rate limit error:', error.code);
}

Next Stepsโ€‹


Need help with specific errors? Check our error troubleshooting guide or contact support at hello@torque.fi.