Skip to main content

Error Response Format

All errors follow a consistent format:
{
  "error": {
    "code": "error_code",
    "message": "Human-readable error message",
    "details": {
      "additional": "context"
    }
  }
}

HTTP Status Codes

200
success
Success - Request completed successfully
400
error
Bad Request - Invalid request or business logic error
401
error
Unauthorized - Missing or invalid API key
403
error
Forbidden - API key doesn’t have required permissions
404
error
Not Found - Resource doesn’t exist
409
error
Conflict - Resource conflict (e.g., duplicate operation)
422
error
Validation Error - Request data failed validation
429
error
Rate Limited - Too many requests
500
error
Internal Error - Something went wrong on our end
502
error
Upstream Error - Error from upstream provider (1Shot)
504
error
Timeout - Request took too long

Error Codes

Client Errors (4xx)

Status: 400Request data is malformed or invalid.
{
  "error": {
    "code": "invalid_request",
    "message": "Transaction hash must start with 0x",
    "details": {
      "field": "transaction_hash"
    }
  }
}
Status: 401Missing or invalid API key.
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key"
  }
}
Status: 404Requested resource doesn’t exist.
{
  "error": {
    "code": "not_found",
    "message": "Verification not found: ver_invalid123"
  }
}
Status: 409Resource conflict or duplicate operation.
{
  "error": {
    "code": "conflict",
    "message": "Settlement already initiated for this verification"
  }
}
Status: 429Rate limit exceeded.
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Max 100 requests per minute."
  }
}
Retry-After header indicates when to retry.
Status: 422Idempotency key reused with different request data.
{
  "error": {
    "code": "idempotency_mismatch",
    "message": "Idempotency key already used with different request data"
  }
}

Business Logic Errors

Status: 400Payment verification failed.
{
  "error": {
    "code": "verification_failed",
    "message": "Transaction amount does not match expected amount",
    "details": {
      "expected": "1000000000000000000",
      "actual": "500000000000000000"
    }
  }
}
Status: 400Settlement failed or not allowed.
{
  "error": {
    "code": "settlement_failed",
    "message": "Cannot settle payment with status: pending",
    "details": {
      "verification_id": "ver_abc123",
      "current_status": "pending"
    }
  }
}
Status: 400Insufficient funds for settlement.
{
  "error": {
    "code": "insufficient_funds",
    "message": "Insufficient funds for settlement"
  }
}

Server Errors (5xx)

Status: 500Internal server error.
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred"
  }
}
Status: 502Error from upstream provider (1Shot).
{
  "error": {
    "code": "upstream_error",
    "message": "Upstream service unavailable"
  }
}
Status: 504Request timeout.
{
  "error": {
    "code": "timeout",
    "message": "Request timeout"
  }
}

Error Handling Best Practices

async function verifyPayment(data) {
  try {
    const response = await fetch("https://facilitator.api.0xmeta.ai/v1/verify", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-API-Key": API_KEY,
        "Idempotency-Key": `verify_${Date.now()}`,
      },
      body: JSON.stringify(data),
    });

    const result = await response.json();

    // Check for errors
    if (!response.ok) {
      handleError(response.status, result.error);
      return null;
    }

    return result;
  } catch (error) {
    console.error("Network error:", error);
    throw error;
  }
}

function handleError(status, error) {
  switch (error.code) {
    case "unauthorized":
      console.error("Invalid API key");
      break;

    case "rate_limited":
      console.error(
        "Rate limited, retry after:",
        response.headers.get("Retry-After")
      );
      break;

    case "verification_failed":
      console.error("Verification failed:", error.details);
      break;

    case "upstream_error":
      console.error("Upstream error, retry later");
      break;

    default:
      console.error("Error:", error.message);
  }
}

Retry Strategy

For transient errors (5xx, rate limits), implement exponential backoff:
async function retryRequest(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const shouldRetry =
        error.status >= 500 ||
        error.status === 429 ||
        error.code === "ETIMEDOUT";

      if (!shouldRetry || attempt === maxRetries - 1) {
        throw error;
      }

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

// Usage
const result = await retryRequest(() => verifyPayment(data));
Don’t retry on 4xx errors (except 429). These indicate problems with your request.
Use idempotency keys when retrying to prevent duplicate operations.