This document provides a comprehensive reference for all error codes, their causes, and recommended resolution steps.
All API errors follow a consistent JSON structure:
JSON
{
"__all__": {
"message": "Descriptive error message",
"code": "error_code"
}
}
Field-specific errors:
JSON
{
"email": {
"message": "Enter a valid email address",
"code": "invalid"
},
"purchase.currency": {
"message": "This field is required",
"code": "required"
}
}
invalid_token
HTTP Status: 401
Message: Invalid or expired authentication token
Causes:
Resolution:
missing_authorization
HTTP Status: 401
Message: Authorization header is required
Causes:
Resolution:
BASH
# Correct format
-H "Authorization: Bearer YOUR_API_KEY"
# Common mistakes
-H "Authorization: YOUR_API_KEY" # Missing "Bearer"
-H "Bearer YOUR_API_KEY" # Missing "Authorization:"
required
HTTP Status: 400
Message: This field is required
Example:
JSON
{
"brand_id": {
"message": "This field is required",
"code": "required"
}
}
Resolution: Include all required fields in your request.
invalid
HTTP Status: 400
Message: Invalid value for this field
Common Cases:
invalid_uuid
HTTP Status: 400
Message: Invalid UUID format
Causes:
client_id or brand_id is not a valid UUID
Valid UUID format:
3fa85f64-5717-4562-b3fc-2c963f66afa6
max_length
HTTP Status: 400
Message: Ensure this field has no more than X characters
Field Limits:
min_value / max_value
HTTP Status: 400
Message: Ensure this value is greater/less than X
Amount Limits:
purchase_not_found
HTTP Status: 404
Message: Purchase not found
Causes:
Resolution:
purchase_already_paid
HTTP Status: 409
Message: Purchase has already been paid
Causes:
Resolution:
purchase_cancelled
HTTP Status: 409
Message: Purchase has been cancelled
Causes:
Resolution:
purchase_expired
HTTP Status: 409
Message: Purchase has expired
Causes:
due the date has passed with due_strict: true
Resolution:
invalid_amount
HTTP Status: 400
Message: Invalid amount specified
Causes:
Resolution:
capture_not_allowed
HTTP Status: 409
Message: Capture is not allowed for this purchase
Causes:
skip_capture: truehold status
Resolution:
holdskip_capture: true for pre-auth
refund_exceeds_amount
HTTP Status: 400
Message: Refund amount exceeds available balance
Causes:
Resolution:
PYTHON
# Check available refund amount
purchase = get_purchase(purchase_id)
available = purchase['purchase']['total'] - purchase.get('refunded_amount', 0)
payment_method_not_available
HTTP Status: 400
Message: Payment method not available
Causes:
payment_method_whitelist
Resolution:
payment_method_whitelist in request
recurring_token_not_found
HTTP Status: 404
Message: Recurring token not found
Causes:
Resolution:
GET /clients/{id}/recurring_tokens/
recurring_token_expired
HTTP Status: 409
Message: Recurring token has expired
Causes:
Resolution:
card_declined
HTTP Status: 422
Message: Card was declined
Decline Reasons:
3ds_authentication_failed
HTTP Status: 422
Message: 3D Secure authentication failed
Causes:
Resolution:
client_not_found
HTTP Status: 404
Message: Client not found
Resolution:
duplicate_client
HTTP Status: 409
Message: Client with this email already exists
Causes:
Resolution:
GET /clients/?email=customer@example.com
billing_template_not_found
HTTP Status: 404
Message: Billing template not found
subscriber_already_exists
HTTP Status: 409
Message: Client is already subscribed to this template
Resolution:
subscription_inactive
HTTP Status: 409
Message: Subscription is not active
Causes:
subscription_active: false on template
webhook_url_invalid
HTTP Status: 400
Message: Invalid webhook URL
Requirements:
webhook_url_unreachable
HTTP Status: 400
Message: Webhook URL is not reachable
Resolution:
rate_limit_exceeded
HTTP Status: 429
Message: Rate limit exceeded
Response Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1619740860
Resolution:
Retry Logic:
PYTHON
import time
def api_request_with_retry(func, max_retries=3):
for attempt in range(max_retries):
response = func()
if response.status_code != 429:
return response
reset_time = int(response.headers.get('X-RateLimit-Reset', 0))
wait_time = max(reset_time - time.time(), 2 ** attempt)
time.sleep(wait_time)
raise Exception("Rate limit exceeded after retries")
direct_post_expired
HTTP Status: 400
Message: Direct post URL has expired
Causes:
Resolution:
invalid_card_data
HTTP Status: 400
Message: Invalid card data submitted
Field Validation:
1. Always Check HTTP Status
PYTHON
response = requests.post(url, json=data, headers=headers)
if response.status_code == 201:
# Success - process response
purchase = response.json()
elif response.status_code == 400:
# Validation error - show user-friendly message
errors = response.json()
handle_validation_errors(errors)
elif response.status_code == 401:
# Auth error - check credentials
refresh_credentials()
elif response.status_code == 429:
# Rate limited - retry with backoff
retry_with_backoff()
else:
# Other error - log and alert
log_error(response)
2. Display User-Friendly Messages
PYTHON
ERROR_MESSAGES = {
'card_declined': 'Your card was declined. Please try a different payment method.',
'insufficient_funds': 'Insufficient funds. Please use a different card.',
'expired_card': 'Your card has expired. Please use a valid card.',
'3ds_authentication_failed': 'Authentication failed. Please try again.',
'invalid': 'Please check your information and try again.'
}
def get_user_message(error_code):
return ERROR_MESSAGES.get(error_code, 'An error occurred. Please try again.')
3. Log Errors for Debugging
PYTHON
import logging
def log_api_error(response, request_data):
logging.error(
"API Error",
extra={
'status_code': response.status_code,
'response_body': response.text,
'request_url': response.url,
'request_data': sanitize_sensitive_data(request_data)
}
)
4. Implement Retry Logic
PYTHON
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=lambda e: isinstance(e, RetryableError)
)
def make_api_request():
response = requests.post(...)
if response.status_code in [429, 502, 503]:
raise RetryableError()
return response