Error Handling

HITL.sh APIs use conventional HTTP status codes and provide detailed error messages to help you debug issues quickly. All errors follow a consistent format for easy parsing and handling.

Error Response Format

All API errors return a JSON response with the following structure:
{
  "error": true,
  "msg": "Human-readable error description",
  "data": {
    // Additional error details (optional)
  }
}

Error Response Fields

error
boolean
Always true for error responses
msg
string
Human-readable error message describing what went wrong
data
object
Additional error context, validation details, or debugging information (optional)

HTTP Status Codes

2xx Success

4xx Client Errors

5xx Server Errors

Common Error Scenarios

Validation Errors

Request Text Too Long:
{
  "error": true,
  "msg": "Validation failed",
  "data": "request_text must be between 1 and 2000 characters"
}
Invalid Response Configuration:
{
  "error": true,
  "msg": "Invalid response configuration",
  "data": "options array required for select response type"
}
Invalid Enum Value:
{
  "error": true,
  "msg": "Validation failed",
  "data": "priority must be one of: low, medium, high, critical"
}

Resource Access Errors

Loop Not Found:
{
  "error": true,
  "msg": "Loop not found"
}
Request Already Cancelled:
{
  "error": true,
  "msg": "Request cannot be cancelled in current state"
}
No Active Members:
{
  "error": true,
  "msg": "No active members found in the loop"
}

Authentication Errors

Missing API Key:
{
  "error": true,
  "msg": "API key required"
}
Invalid API Key Format:
{
  "error": true,
  "msg": "Invalid API key format"
}
Expired Session:
{
  "error": true,
  "msg": "Session has expired. Please log in again."
}

Error Handling Best Practices

1. Implement Retry Logic

Use exponential backoff for transient errors:
import time
import random
import requests

def make_request_with_retry(url, headers, data=None, max_retries=3):
    retryable_status_codes = [429, 500, 502, 503, 504]
    
    for attempt in range(max_retries):
        try:
            if data:
                response = requests.post(url, headers=headers, json=data)
            else:
                response = requests.get(url, headers=headers)
            
            if response.status_code not in retryable_status_codes:
                return response
            
            if attempt < max_retries - 1:  # Don't delay on last attempt
                # Exponential backoff with jitter
                delay = (2 ** attempt) + random.uniform(0, 1)
                time.sleep(delay)
                
        except requests.exceptions.RequestException as e:
            if attempt == max_retries - 1:
                raise e
            time.sleep(2 ** attempt)
    
    return response

2. Handle Rate Limits Gracefully

import time
import requests

class HITLAPIClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.hitl.sh/v1"
        
    def make_request(self, method, endpoint, data=None):
        url = f"{self.base_url}{endpoint}"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        
        response = requests.request(method, url, headers=headers, json=data)
        
        if response.status_code == 429:
            # Extract reset time from response
            reset_time = response.headers.get('X-RateLimit-Reset')
            if reset_time:
                wait_time = int(reset_time) - int(time.time())
                if wait_time > 0:
                    print(f"Rate limited. Waiting {wait_time} seconds...")
                    time.sleep(wait_time)
                    # Retry the request
                    return self.make_request(method, endpoint, data)
        
        return response

3. Validate Requests Client-Side

Implement client-side validation to catch errors early:
def validate_create_request_payload(payload):
    """Validate request payload before sending to API"""
    errors = []
    
    # Required fields
    required_fields = ['processing_type', 'type', 'priority', 'request_text', 
                      'response_type', 'response_config', 'default_response', 'platform']
    
    for field in required_fields:
        if field not in payload or not payload[field]:
            errors.append(f"{field} is required")
    
    # Enum validations
    if 'processing_type' in payload:
        valid_processing_types = ['time-sensitive', 'deferred']
        if payload['processing_type'] not in valid_processing_types:
            errors.append(f"processing_type must be one of: {', '.join(valid_processing_types)}")
    
    if 'priority' in payload:
        valid_priorities = ['low', 'medium', 'high', 'critical']
        if payload['priority'] not in valid_priorities:
            errors.append(f"priority must be one of: {', '.join(valid_priorities)}")
    
    # Custom validations
    if payload.get('processing_type') == 'time-sensitive' and 'timeout_seconds' not in payload:
        errors.append("timeout_seconds is required for time-sensitive requests")
    
    if 'timeout_seconds' in payload:
        timeout = payload['timeout_seconds']
        if not isinstance(timeout, int) or timeout < 60 or timeout > 86400:
            errors.append("timeout_seconds must be between 60 and 86400")
    
    return errors

4. Log Errors for Debugging

Implement comprehensive error logging:
import logging
import json

logger = logging.getLogger(__name__)

def log_api_error(response, endpoint, payload=None):
    """Log API errors with context for debugging"""
    try:
        error_data = response.json()
    except:
        error_data = {"msg": "Failed to parse error response"}
    
    log_entry = {
        "endpoint": endpoint,
        "status_code": response.status_code,
        "error_message": error_data.get("msg", "Unknown error"),
        "error_data": error_data.get("data"),
        "request_payload": payload,
        "response_headers": dict(response.headers)
    }
    
    logger.error(f"API Error: {json.dumps(log_entry, indent=2)}")
    
    return log_entry

# Usage
response = make_api_request(url, payload)
if response.status_code >= 400:
    log_api_error(response, endpoint, payload)

Debugging Guide

Common Issues and Solutions

Request/Response Debugging

Enable verbose logging to see full HTTP requests and responses:
# Use -v flag for verbose output
curl -v -H "Authorization: Bearer your_api_key" \
     -H "Content-Type: application/json" \
     -d '{"name": "Test Loop", "icon": "test"}' \
     https://api.hitl.sh/v1/loops

Getting Help

Self-Service Resources

Contacting Support

When contacting support, please include:
  1. Request ID (if available from response headers)
  2. Timestamp of when the error occurred
  3. Full error response including status code and message
  4. Request details (endpoint, method, payload)
  5. Your API key ID (not the actual key)

Next Steps

Authentication Guide

Learn about API keys, JWT tokens, and security best practices.

Rate Limits

Understand API rate limits and optimization strategies.

Webhooks

Set up webhooks to avoid polling and reduce API calls.