Errors

Handle API errors effectively with comprehensive error codes, types, and troubleshooting guidance

Errors

The GetPaidHQ API uses conventional HTTP status codes to indicate success or failure and returns detailed error information in a consistent JSON format.

HTTP status codes

The API returns these HTTP status codes:

CodeDescription
200OK - Request succeeded
201Created - Resource created successfully
204No Content - Request succeeded, no content returned
400Bad Request - Invalid request parameters
401Unauthorized - Invalid or missing authentication
403Forbidden - Insufficient permissions
404Not Found - Resource doesn't exist
409Conflict - Resource already exists or conflicting state
422Unprocessable Entity - Valid request but semantic errors
429Too Many Requests - Rate limit exceeded
500Internal Server Error - Server error
502Bad Gateway - Gateway error
503Service Unavailable - Service temporarily unavailable

Error response format

All errors return a consistent JSON structure:

{
  "error": {
    "type": "validation_error",
    "message": "The provided email address is invalid",
    "field": "email",
    "code": "invalid_email_format",
    "request_id": "req_1234567890abcdef"
  }
}

Error object properties

  • type: The category of error (see types below)
  • message: Human-readable error description
  • field: The specific field that caused the error (validation errors only)
  • code: Machine-readable error code for programmatic handling
  • request_id: Unique identifier for debugging with support

Error types

validation_error

Request contains invalid or missing required parameters.

{
  "error": {
    "type": "validation_error",
    "message": "Email is required",
    "field": "email",
    "code": "missing_required_field"
  }
}

Common validation error codes:

  • missing_required_field
  • invalid_email_format
  • invalid_currency_code
  • amount_too_small
  • invalid_date_format

authentication_error

API key is missing, invalid, or expired.

{
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key provided",
    "code": "invalid_api_key"
  }
}

Common authentication error codes:

  • invalid_api_key
  • api_key_expired
  • missing_api_key
  • organization_suspended

permission_error

Valid authentication but insufficient permissions for the requested operation.

{
  "error": {
    "type": "permission_error",
    "message": "Insufficient permissions to access this resource",
    "code": "insufficient_permissions"
  }
}

not_found_error

The requested resource doesn't exist or has been deleted.

{
  "error": {
    "type": "not_found_error",
    "message": "Customer not found",
    "code": "customer_not_found"
  }
}

rate_limit_error

Too many requests in a given time period. See Rate Limits for details.

{
  "error": {
    "type": "rate_limit_error",
    "message": "Rate limit exceeded. Retry after 60 seconds",
    "code": "rate_limit_exceeded"
  }
}

conflict_error

Request conflicts with the current state of the resource.

{
  "error": {
    "type": "conflict_error",
    "message": "Customer with this email already exists",
    "code": "customer_already_exists"
  }
}

payment_error

Payment processing failures.

{
  "error": {
    "type": "payment_error",
    "message": "Payment method declined by issuer",
    "code": "card_declined",
    "payment_error": {
      "decline_code": "insufficient_funds",
      "issuer_message": "Not sufficient funds"
    }
  }
}

Common payment error codes:

  • card_declined
  • expired_card
  • invalid_cvc
  • processing_error
  • insufficient_funds

server_error

Internal server errors. These should be rare and are automatically logged.

{
  "error": {
    "type": "server_error",
    "message": "An internal error occurred",
    "code": "internal_server_error"
  }
}

Error handling best practices

Check HTTP status codes

Always check the HTTP status code before processing the response:

try {
  const customer = await getpaidhq.customers.create(customerData);
  console.log('Customer created:', customer.id);
} catch (error) {
  if (error.status === 400) {
    console.log('Validation error:', error.message);
  } else if (error.status === 409) {
    console.log('Customer already exists');
  } else {
    console.log('Unexpected error:', error);
  }
}

Handle specific error types

Use the error type and code for specific handling:

import { GetPaidHQError } from '@getpaidhq/node';

try {
  const payment = await getpaidhq.payments.create(paymentData);
} catch (error) {
  if (error instanceof GetPaidHQError) {
    switch (error.type) {
      case 'payment_error':
        handlePaymentError(error);
        break;
      case 'rate_limit_error':
        scheduleRetry(paymentData, error.retryAfter);
        break;
      case 'validation_error':
        showValidationError(error.field, error.message);
        break;
      default:
        logError(error);
    }
  }
}

function handlePaymentError(error) {
  switch (error.code) {
    case 'card_declined':
      showMessage('Payment declined. Please try a different card.');
      break;
    case 'expired_card':
      showMessage('Card expired. Please update your payment method.');
      break;
    case 'insufficient_funds':
      showMessage('Insufficient funds. Please try a different payment method.');
      break;
    default:
      showMessage('Payment failed. Please try again.');
  }
}

Implement retry logic

For transient errors, implement exponential backoff:

async function apiCallWithRetry(apiCall, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await apiCall();
    } catch (error) {
      // Don't retry validation or authentication errors
      if (error.type === 'validation_error' || error.type === 'authentication_error') {
        throw error;
      }
      
      // Retry server errors and rate limits
      if (error.status >= 500 || error.status === 429) {
        if (attempt < maxRetries - 1) {
          const delay = Math.pow(2, attempt) * 1000;
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
      }
      
      throw error;
    }
  }
}

Log errors for debugging

Always log errors with the request ID:

function logError(error, context = {}) {
  console.error('API Error:', {
    type: error.type,
    message: error.message,
    code: error.code,
    requestId: error.requestId,
    status: error.status,
    context
  });
  
  // Send to error tracking service
  errorTracker.captureException(error, {
    tags: {
      errorType: error.type,
      errorCode: error.code
    },
    extra: {
      requestId: error.requestId,
      context
    }
  });
}

Common error scenarios

Creating duplicate resources

// Request
POST /api/customers
{
  "email": "existing@example.com",
  "name": "John Doe"
}

// Response: 409 Conflict
{
  "error": {
    "type": "conflict_error",
    "message": "Customer with this email already exists",
    "code": "customer_already_exists",
    "field": "email"
  }
}

Invalid subscription state changes

// Request: Cancel already canceled subscription
DELETE /api/subscriptions/sub_123

// Response: 422 Unprocessable Entity
{
  "error": {
    "type": "validation_error",
    "message": "Cannot cancel subscription in 'canceled' state",
    "code": "invalid_subscription_state"
  }
}

Payment method failures

// Request
POST /api/payments
{
  "amount": 5000,
  "currency": "USD",
  "payment_method": "pm_declined_card"
}

// Response: 402 Payment Required
{
  "error": {
    "type": "payment_error",
    "message": "Your card was declined",
    "code": "card_declined",
    "payment_error": {
      "decline_code": "generic_decline",
      "issuer_message": "Do not honor"
    }
  }
}

SDK error handling

Official SDKs provide structured error objects:

Node.js

import { GetPaidHQError } from '@getpaidhq/node';

try {
  const customer = await getpaidhq.customers.create(data);
} catch (error) {
  if (error instanceof GetPaidHQError) {
    console.log('Error type:', error.type);
    console.log('Error code:', error.code);
    console.log('Request ID:', error.requestId);
  }
}

Python

from getpaidhq import GetPaidHQError

try:
    customer = client.customers.create(**data)
except GetPaidHQError as e:
    print(f"Error type: {e.type}")
    print(f"Error code: {e.code}")
    print(f"Request ID: {e.request_id}")

Getting help

When contacting support about errors:

  1. Include the request ID from the error response
  2. Provide the full error response (remove sensitive data)
  3. Describe what you were trying to accomplish
  4. Include relevant request parameters (remove sensitive data)

The request ID helps our support team quickly locate the specific API call in our logs for faster resolution.