Skip to main content

Error Handling

The PayRequest API uses conventional HTTP response codes and returns detailed error messages to help you diagnose issues quickly.

HTTP Status Codes

CodeMeaningDescription
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestInvalid request parameters or body
401UnauthorizedMissing or invalid authentication
403ForbiddenValid auth but insufficient permissions
404Not FoundResource doesn’t exist or doesn’t belong to you
422Unprocessable EntityValidation errors
429Too Many RequestsRate limit exceeded
500Internal Server ErrorSomething went wrong on our end

Error Response Format

All error responses follow this structure:
{
  "success": false,
  "error": "Human-readable error message",
  "errors": {
    "field_name": [
      "Specific validation error"
    ]
  }
}
FieldTypeDescription
successbooleanAlways false for errors
errorstringGeneral error description
errorsobjectField-specific validation errors (optional)

Common Errors

Authentication Errors

Cause: Missing, invalid, or expired access token.
{
  "message": "Unauthenticated."
}
Solutions:
  • Ensure you’re including the Authorization header
  • Check that your token starts with Bearer
  • Generate a new token if expired
Cause: Your token doesn’t have the required scope.
{
  "success": false,
  "error": "Invalid scope(s) provided."
}
Solutions:
  • Check which scope the endpoint requires
  • Create a new token with the required scopes
  • Use the Authentication guide for scope details

Validation Errors

Cause: Request body failed validation rules.
{
  "success": false,
  "error": "The given data was invalid.",
  "errors": {
    "customer_id": ["The customer id field is required."],
    "items": ["The items field is required."],
    "due_date": ["The due date must be a date after or equal to date."]
  }
}
Solutions:
  • Check each field in the errors object
  • Ensure required fields are provided
  • Verify data types and formats match the documentation

Resource Errors

Cause: Resource doesn’t exist or doesn’t belong to your account.
{
  "success": false,
  "error": "Invoice not found"
}
Solutions:
  • Verify the resource ID is correct
  • Ensure the resource belongs to your account
  • Check if the resource was deleted
Cause: Business logic prevented the action.
{
  "success": false,
  "error": "Invoice is already paid"
}
Solutions:
  • Check the current state of the resource
  • Review the error message for specific guidance

Rate Limiting

Cause: Too many requests in a short period.
{
  "success": false,
  "error": "Too many requests. Please try again later."
}
Headers included:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706540400
Retry-After: 45
Solutions:
  • Implement exponential backoff
  • Cache responses where possible
  • Wait until X-RateLimit-Reset timestamp

Best Practices

Implement Proper Error Handling

async function apiRequest(endpoint, options = {}) {
  const response = await fetch(`https://payrequest.app/api/v1${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${process.env.PAYREQUEST_TOKEN}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      ...options.headers
    }
  });

  const data = await response.json();

  if (!response.ok) {
    // Handle specific error types
    switch (response.status) {
      case 401:
        throw new AuthenticationError('Invalid or expired token');
      case 403:
        throw new PermissionError('Insufficient permissions');
      case 404:
        throw new NotFoundError(data.error || 'Resource not found');
      case 422:
        throw new ValidationError(data.errors);
      case 429:
        const retryAfter = response.headers.get('Retry-After');
        throw new RateLimitError(`Rate limited. Retry after ${retryAfter}s`);
      default:
        throw new ApiError(data.error || 'Unknown error');
    }
  }

  return data;
}

Retry Strategy

For transient errors (5xx, network issues), implement exponential backoff:
async function apiRequestWithRetry(endpoint, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await apiRequest(endpoint, options);
    } catch (error) {
      // Don't retry client errors (4xx)
      if (error.status >= 400 && error.status < 500) {
        throw error;
      }

      // Don't retry on last attempt
      if (attempt === maxRetries - 1) {
        throw error;
      }

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

Validate Before Sending

Catch validation issues early:
function validateInvoiceData(data) {
  const errors = {};

  if (!data.customer_id) {
    errors.customer_id = ['Customer ID is required'];
  }

  if (!data.items || data.items.length === 0) {
    errors.items = ['At least one line item is required'];
  }

  if (data.due_date && data.date && new Date(data.due_date) < new Date(data.date)) {
    errors.due_date = ['Due date must be on or after invoice date'];
  }

  if (Object.keys(errors).length > 0) {
    throw new ValidationError(errors);
  }
}

Getting Help

If you encounter persistent errors:
  1. Check this documentation for the endpoint requirements
  2. Verify your token has the correct scopes
  3. Test with a minimal request body
  4. Contact [email protected] with:
    • The endpoint you’re calling
    • Your request (without the token)
    • The error response
    • Your account email