Skip to main content

Error Response Format

All Zochil API errors follow a consistent structure to help you handle them programmatically:
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "metadata": {
      "field": "additional_context",
      "details": "More information about the error"
    }
  }
}

HTTP Status Codes

The API uses standard HTTP status codes to indicate the success or failure of requests:
Status CodeDescription
200OK - Request succeeded
201Created - Resource created successfully
204No Content - Request succeeded, no response body
400Bad Request - Invalid request parameters
401Unauthorized - Authentication required
403Forbidden - Insufficient permissions
404Not Found - Resource not found
409Conflict - Resource conflict (e.g., duplicate email)
422Unprocessable Entity - Validation errors
429Too Many Requests - Rate limit exceeded
500Internal Server Error - Server error
503Service Unavailable - Temporary service outage

Common Error Codes

Authentication Errors

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired access token"
  }
}

Validation Errors

{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request validation failed",
    "metadata": {
      "fields": {
        "email": "Invalid email format",
        "password": "Password must be at least 8 characters"
      }
    }
  }
}

Resource Errors

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Resource not found",
    "metadata": {
      "resource": "user",
      "id": "123e4567-e89b-12d3-a456-426614174000"
    }
  }
}

Business Logic Errors

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Insufficient balance for transaction",
    "metadata": {
      "available": 50.0,
      "required": 100.0,
      "currency": "USD"
    }
  }
}

Error Handling Best Practices

1. Always Check the Response Status

const response = await fetch("/api/users/profile", {
  headers: {
    "access-token": token,
    "device-id": deviceId
  }
});

if (!response.ok) {
  const error = await response.json();
  throw new Error(`API Error: ${error.error.message}`);
}

const data = await response.json();

2. Handle Different Error Types

class APIError extends Error {
  constructor(code, message, metadata = {}) {
    super(message);
    this.code = code;
    this.metadata = metadata;
    this.name = "APIError";
  }
}

async function makeAPIRequest(endpoint, options) {
  try {
    const response = await fetch(endpoint, options);
    const data = await response.json();

    if (!data.success) {
      throw new APIError(
        data.error.code,
        data.error.message,
        data.error.metadata
      );
    }

    return data.data;
  } catch (error) {
    if (error instanceof APIError) {
      switch (error.code) {
        case "UNAUTHORIZED":
          // Redirect to login
          window.location.href = "/login";
          break;
        case "VALIDATION_FAILED":
          // Show validation errors to user
          showValidationErrors(error.metadata.fields);
          break;
        case "INSUFFICIENT_FUNDS":
          // Show payment options
          showPaymentDialog();
          break;
        default:
          // Generic error handling
          showErrorMessage(error.message);
      }
    }
    throw error;
  }
}

3. Implement Retry Logic

async function makeRequestWithRetry(endpoint, options, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(endpoint, options);

      if (response.status === 429) {
        // Rate limited, wait and retry
        const retryAfter = response.headers.get("Retry-After") || 1;
        await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
        continue;
      }

      if (response.status >= 500 && attempt < maxRetries) {
        // Server error, wait and retry
        await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
        continue;
      }

      return response;
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
    }
  }
}

4. User-Friendly Error Messages

function getDisplayMessage(errorCode, message) {
  const friendlyMessages = {
    UNAUTHORIZED: "Please log in to continue",
    VALIDATION_FAILED: "Please check your input and try again",
    NOT_FOUND: "The requested item could not be found",
    PRODUCT_OUT_OF_STOCK: "This item is currently out of stock",
    INSUFFICIENT_FUNDS: "Insufficient balance for this transaction",
    RATE_LIMITED: "Too many requests. Please wait a moment and try again"
  };

  return friendlyMessages[errorCode] || message;
}

Rate Limiting

The API implements rate limiting to ensure fair usage:
  • Standard endpoints: 100 requests per minute
  • Authentication endpoints: 10 requests per minute
  • Upload endpoints: 10 requests per minute
When rate limited, you’ll receive:
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests. Please try again later.",
    "metadata": {
      "retry_after": 60,
      "limit": 100,
      "remaining": 0
    }
  }
}

Logging and Monitoring

For production applications, implement proper error logging:
function logError(error, context = {}) {
  console.error("API Error:", {
    code: error.code,
    message: error.message,
    metadata: error.metadata,
    context: context,
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent,
    url: window.location.href
  });

  // Send to monitoring service
  if (window.errorTracker) {
    window.errorTracker.captureException(error, context);
  }
}

Next Steps