Skip to main content

Overview

The Satsuma API uses standard HTTP status codes and detailed error responses to help you diagnose and handle issues. This guide covers common error scenarios and best practices for robust error handling.

HTTP Status Codes

Success Codes (2xx)

  • 200 OK - Request succeeded
  • 201 Created - Resource created successfully
  • 202 Accepted - Request accepted for processing
  • 204 No Content - Request succeeded with no response body

Client Error Codes (4xx)

  • 400 Bad Request - Invalid request syntax or parameters
  • 401 Unauthorized - Invalid or missing API key
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource does not exist
  • 409 Conflict - Request conflicts with current state
  • 422 Unprocessable Entity - Validation errors
  • 429 Too Many Requests - Rate limit exceeded

Server Error Codes (5xx)

  • 500 Internal Server Error - Unexpected server error
  • 502 Bad Gateway - Upstream service error
  • 503 Service Unavailable - Temporary service outage
  • 504 Gateway Timeout - Request timeout

Error Response Format

All errors return a consistent JSON structure:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "One or more fields failed validation",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format"
      },
      {
        "field": "quantity", 
        "message": "Must be greater than 0"
      }
    ],
    "request_id": "req_abc123xyz789"
  }
}

Common Error Codes

Authentication Errors

Status: 401
Cause: API key is malformed, expired, or doesn’t exist
Solution: Verify your API key and ensure it’s not truncated
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The provided API key is invalid or expired"
  }
}
Status: 403
Cause: API key lacks required permissions for the endpoint
Solution: Use a key with appropriate permissions
{
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Your API key does not have permission to access this resource",
    "required_permission": "orders"
  }
}

Validation Errors

Status: 422
Cause: Request data fails validation rules
Solution: Fix the invalid fields listed in the details array
{
  "error": {
    "code": "VALIDATION_ERROR", 
    "message": "Request validation failed",
    "details": [
      {
        "field": "latitude",
        "message": "Must be between -90 and 90"
      }
    ]
  }
}
Status: 400
Cause: Required request parameter is missing
Solution: Include all required fields in your request
{
  "error": {
    "code": "MISSING_REQUIRED_FIELD",
    "message": "Required field is missing",
    "field": "merchant_id"
  }
}

Resource Errors

Status: 404
Cause: Requested resource doesn’t exist or has been deleted
Solution: Verify the resource ID and ensure it exists
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Product not found",
    "resource_type": "product",
    "resource_id": "prod_nonexistent123"
  }
}
Status: 409
Cause: Request conflicts with current resource state
Solution: Check resource state and retry with correct data
{
  "error": {
    "code": "RESOURCE_CONFLICT",
    "message": "Cannot modify order that has already been fulfilled",
    "resource_state": "completed"
  }
}

Business Logic Errors

Status: 422
Cause: Requested quantity exceeds available inventory
Solution: Reduce quantity or choose different product
{
  "error": {
    "code": "INSUFFICIENT_INVENTORY",
    "message": "Not enough inventory available",
    "available_quantity": 3,
    "requested_quantity": 5,
    "product_id": "prod_123"
  }
}
Status: 422
Cause: Payment processing failed
Solution: Use valid payment method or contact payment provider
{
  "error": {
    "code": "PAYMENT_FAILED", 
    "message": "Payment could not be processed",
    "payment_error": "card_declined",
    "decline_reason": "insufficient_funds"
  }
}

Rate Limiting

Status: 429
Cause: Too many requests in time window
Solution: Implement backoff and retry after specified time
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded",
    "retry_after": 60,
    "limit": 100,
    "reset": "2024-01-15T11:00:00Z"
  }
}

Implementing Error Handling

Basic Error Handling

curl -X GET 'https://api.satsuma.ai/product?latitude=37.7749&longitude=-122.4194' \
  -H 'Authorization: your-api-key' \
  -H 'Content-Type: application/json' \
  -w "Response Code: %{http_code}\n" \
  -s | jq '.'

# Example error response for invalid API key:
# {
#   "error": {
#     "code": "INVALID_API_KEY",
#     "message": "The provided API key is invalid or expired",
#     "request_id": "req_abc123xyz789"
#   }
# }

Retry Logic with Backoff

Implement exponential backoff for transient errors:
# Retry with exponential backoff using a simple script
#!/bin/bash
url="https://api.satsuma.ai/product?latitude=37.7749&longitude=-122.4194"
api_key="your-api-key"
max_retries=3

for attempt in $(seq 0 $max_retries); do
  response=$(curl -s -w "%{http_code}" \
    -H "Authorization: $api_key" \
    -H "Content-Type: application/json" \
    "$url")
  
  http_code="${response: -3}"
  body="${response%???}"
  
  # Success
  if [[ "$http_code" =~ ^2[0-9][0-9]$ ]]; then
    echo "$body" | jq '.'
    exit 0
  fi
  
  # Don't retry 4xx errors (except 429)
  if [[ "$http_code" =~ ^4[0-9][0-9]$ ]] && [ "$http_code" != "429" ]; then
    echo "Client error $http_code: $body" >&2
    exit 1
  fi
  
  # Calculate delay for retry
  if [ "$attempt" -lt "$max_retries" ]; then
    delay=$((2**attempt))
    echo "Retrying in ${delay}s (attempt $((attempt + 1))/$((max_retries + 1)))" >&2
    sleep $delay
  fi
done

echo "Max retries exceeded: $body" >&2
exit 1

Error-Specific Handling

Handle different error types appropriately:
# Error handling in bash script
handle_error() {
  local error_code="$1"
  local error_body="$2"
  
  case "$error_code" in
    "INVALID_API_KEY")
      echo "ERROR: Invalid API key. Please check your credentials." >&2
      # Log security incident
      logger "Security: Invalid API key used"
      exit 1
      ;;
    "INSUFFICIENT_INVENTORY")
      available=$(echo "$error_body" | jq -r '.error.available_quantity // "unknown"')
      echo "ERROR: Insufficient inventory. Available: $available" >&2
      exit 1
      ;;
    "PAYMENT_FAILED")
      reason=$(echo "$error_body" | jq -r '.error.decline_reason // "unknown"')
      echo "ERROR: Payment failed. Reason: $reason" >&2
      exit 1
      ;;
    "RATE_LIMIT_EXCEEDED")
      retry_after=$(echo "$error_body" | jq -r '.error.retry_after // 60')
      echo "Rate limit exceeded. Retry after ${retry_after}s" >&2
      return $retry_after
      ;;
    *)
      echo "ERROR: Unexpected API error: $error_code" >&2
      exit 1
      ;;
  esac
}

Circuit Breaker Pattern

Prevent cascade failures with a circuit breaker:
# Simple circuit breaker state management in bash
#!/bin/bash
CIRCUIT_STATE_FILE="/tmp/satsuma_circuit_state"
FAILURE_THRESHOLD=5
TIMEOUT_DURATION=60

get_circuit_state() {
  if [ -f "$CIRCUIT_STATE_FILE" ]; then
    cat "$CIRCUIT_STATE_FILE"
  else
    echo "CLOSED:0:0"
  fi
}

update_circuit_state() {
  local state="$1"
  local failures="$2"
  local next_attempt="$3"
  echo "$state:$failures:$next_attempt" > "$CIRCUIT_STATE_FILE"
}

execute_with_circuit_breaker() {
  local url="$1"
  local api_key="$2"
  
  IFS=':' read -r state failures next_attempt <<< "$(get_circuit_state)"
  current_time=$(date +%s)
  
  # Check if circuit is open and timeout hasn't expired
  if [ "$state" = "OPEN" ] && [ "$current_time" -lt "$next_attempt" ]; then
    echo "Circuit breaker is OPEN. Retry after: $((next_attempt - current_time))s" >&2
    return 1
  fi
  
  # Make the request
  response=$(curl -s -w "%{http_code}" \
    -H "Authorization: $api_key" \
    "$url")
  
  http_code="${response: -3}"
  
  if [[ "$http_code" =~ ^2[0-9][0-9]$ ]]; then
    # Success - reset circuit breaker
    update_circuit_state "CLOSED" "0" "0"
    echo "${response%???}"  # Return response body
    return 0
  else
    # Failure - increment counter
    failures=$((failures + 1))
    if [ "$failures" -ge "$FAILURE_THRESHOLD" ]; then
      # Open circuit
      next_attempt=$((current_time + TIMEOUT_DURATION))
      update_circuit_state "OPEN" "$failures" "$next_attempt"
      echo "Circuit breaker opened due to failures" >&2
    else
      update_circuit_state "CLOSED" "$failures" "0"
    fi
    return 1
  fi
}

Monitoring & Alerting

Error Tracking

Track errors for monitoring and debugging:
# Error tracking in bash with logging
track_error() {
  local error_code="$1"
  local error_message="$2"
  local status_code="$3"
  local request_id="$4"
  local endpoint="$5"
  local user_id="${6:-unknown}"
  local retry_attempt="${7:-0}"
  
  # Create error data JSON
  local error_data=$(jq -n \
    --arg timestamp "$(date -Iseconds)" \
    --arg error_code "$error_code" \
    --arg error_message "$error_message" \
    --arg status_code "$status_code" \
    --arg request_id "$request_id" \
    --arg user_id "$user_id" \
    --arg endpoint "$endpoint" \
    --arg retry_attempt "$retry_attempt" \
    '{
      timestamp: $timestamp,
      error_code: $error_code,
      error_message: $error_message,
      status_code: ($status_code | tonumber),
      request_id: $request_id,
      user_id: $user_id,
      endpoint: $endpoint,
      retry_attempt: ($retry_attempt | tonumber)
    }')
  
  # Log to file
  echo "$error_data" >> /var/log/satsuma_errors.log
  
  # Send to monitoring service (replace with your monitoring endpoint)
  curl -s -X POST "https://monitoring.yourservice.com/api/errors" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $MONITORING_TOKEN" \
    -d "$error_data" > /dev/null
  
  # Alert on critical errors
  if [ "$status_code" -ge 500 ] || [ "$error_code" = "PAYMENT_FAILED" ]; then
    # Send alert (replace with your alerting service)
    curl -s -X POST "https://alerts.yourservice.com/api/critical" \
      -H "Content-Type: application/json" \
      -d "$error_data" > /dev/null
  fi
}

Health Checks

Implement health checks to monitor API status:
#!/bin/bash
# Health check script
API_KEY="your-api-key"
HEALTH_ENDPOINT="https://api.satsuma.ai/product?latitude=37.7749&longitude=-122.4194"
METRICS_ENDPOINT="https://metrics.yourservice.com/api/gauge"

health_check() {
  local timestamp=$(date +%s)
  
  # Make health check request with timeout
  response=$(timeout 5 curl -s -w "%{http_code}" \
    -H "Authorization: $API_KEY" \
    "$HEALTH_ENDPOINT")
  
  local exit_code=$?
  local http_code="${response: -3}"
  
  if [ $exit_code -eq 0 ] && [[ "$http_code" =~ ^2[0-9][0-9]$ ]]; then
    # Healthy
    local health_data=$(jq -n \
      --arg status "healthy" \
      --arg timestamp "$timestamp" \
      '{ status: $status, timestamp: ($timestamp | tonumber) }')
    
    echo "$health_data"
    
    # Send metrics (1 = healthy)
    curl -s -X POST "$METRICS_ENDPOINT" \
      -H "Content-Type: application/json" \
      -d '{"metric": "api_health", "value": 1, "timestamp": '$timestamp'}' \
      > /dev/null
    
    return 0
  else
    # Unhealthy
    local error_msg="HTTP $http_code or timeout"
    local health_data=$(jq -n \
      --arg status "unhealthy" \
      --arg error "$error_msg" \
      --arg timestamp "$timestamp" \
      '{ status: $status, error: $error, timestamp: ($timestamp | tonumber) }')
    
    echo "$health_data"
    
    # Send metrics (0 = unhealthy)
    curl -s -X POST "$METRICS_ENDPOINT" \
      -H "Content-Type: application/json" \
      -d '{"metric": "api_health", "value": 0, "timestamp": '$timestamp'}' \
      > /dev/null
    
    return 1
  fi
}

# Run health check
health_check

# For continuous monitoring, add to cron:
# */1 * * * * /path/to/health_check.sh

Best Practices

Error Handling Strategy

  1. Fail Fast: Return errors immediately for invalid requests
  2. Graceful Degradation: Provide fallback functionality when possible
  3. User-Friendly Messages: Show helpful error messages to end users
  4. Detailed Logging: Log full error context for debugging

Retry Guidelines

  • Retry transient errors: Network issues, 5xx errors, rate limits
  • Don’t retry permanent failures: 4xx errors (except 429)
  • Use exponential backoff: With jitter to prevent thundering herd
  • Respect rate limits: Use server-provided retry-after values

Security Considerations

  • Never log sensitive data: API keys, payment info, personal data
  • Validate all inputs: Even when retrying failed requests
  • Monitor for anomalies: Unusual error patterns may indicate attacks
  • Rotate compromised keys: Immediately if invalid key errors occur

Troubleshooting Common Issues

  • Ensure API key is not truncated in environment variables
  • Check that you’re using the correct header name (Authentication)
  • Verify you’re hitting the correct environment (staging vs production)
  • Try regenerating your API key if the issue persists
  • Implement retry logic with exponential backoff
  • Check your network connectivity and DNS resolution
  • Consider increasing request timeout limits
  • Monitor our status page for incidents
  • Review your rate limiting and implement proper backoff
  • Consider request batching to reduce API calls
  • Implement caching for frequently accessed data
  • Contact support for rate limit increases if needed

Getting Help

When reporting errors, include:
  • Request ID from the error response
  • Full error message and code
  • Request payload (without sensitive data)
  • Timestamp and retry attempts
  • Your API key prefix (first 8 characters)
Contact support@satsuma.ai for assistance with persistent errors or unexpected behavior.
I