Error Codes

The PayDirect API uses standard HTTP status codes and returns errors in a consistent JSON format.

Error Response Format
All errors follow a consistent JSON structure
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "Invalid token symbol. Must be USDC, ETH, or ADAO."
}

The error field contains a human-readable description of the problem. Some responses may include additional fields for context.

HTTP Status Codes
StatusNameDescription
200OKRequest succeeded
201CreatedResource created successfully (payments, webhooks, keys)
400Bad RequestMissing or invalid request parameters
401UnauthorizedMissing, invalid, or revoked API key
403ForbiddenValid key but insufficient permissions (e.g. accessing another workspace)
404Not FoundPayment, webhook, or key not found (or not owned by this workspace)
409ConflictIdempotency key already used — existing resource returned
429Too Many RequestsRate limit exceeded. Check Retry-After header.
500Internal Server ErrorUnexpected error. Retry with exponential backoff.
503Service UnavailableWorkspace paused by circuit breaker. Payouts disabled until resumed. See code: CIRCUIT_BREAKER_OPEN.
Common Validation Errors
Frequent 400 errors and how to fix them
Error MessageEndpointFix
"tokenSymbol, amount, and merchantWallet are required"POST /paymentsInclude all three required fields in the request body
"Invalid token symbol"POST /paymentsUse uppercase: USDC, ETH, or ADAO
"Amount must be a positive number"POST /paymentsAmount must parse as a number greater than zero
"Invalid merchant wallet address"POST /paymentsMust be a valid Ethereum address: 0x + 40 hex chars
"Payment cannot be cancelled"POST /payments/:id/cancelOnly pending payments can be cancelled
"Invalid webhook URL"POST /webhooksURL must be a valid, parseable URL
"Invalid event type"POST /webhooksEvents must be from: payment.created, payment.detected, payment.confirmed, payment.forwarded, payment.failed, payment.expired
Circuit Breaker Errors (503)
Workspace automatically paused after consecutive payout failures

When a workspace accumulates 3 consecutive payout failures, PayDirect activates the circuit breaker and pauses all payouts for that workspace. Error responses include a structured code field:

CodeStatusDescription
CIRCUIT_BREAKER_OPEN503Workspace is paused. No payout attempted. Resume via API or wait 30 minutes.
INSUFFICIENT_GAS400Workspace wallet lacks ETH for gas. Send ETH or wait for auto-funding.
PAYOUT_FAILED500On-chain transaction failed. Check balance and destination address.
// Handling circuit breaker in your agent
const res = await fetch(url + "/api/v1/payouts", { method: "POST", ... });
const data = await res.json();

if (data.code === "CIRCUIT_BREAKER_OPEN") {
  // Workspace is paused — stop scheduling payouts
  console.error("Payouts paused:", data.error);
  // Option 1: Wait for auto-resume (30 min)
  // Option 2: Fix the issue, then POST /api/v1/workspaces/{id}/resume
}

if (data.failStreak > 0) {
  console.warn(`Fail streak: ${data.failStreak}, remaining: ${data.remainingBeforePause}`);
}

See Payouts API > Circuit Breaker for the full resume flow and agent integration guide.

Rate Limit Errors (429)

When the rate limit is exceeded, the response includes headers to help you retry appropriately:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708100182
Retry-After: 42

{
  "error": "Rate limit exceeded"
}
HeaderDescription
X-RateLimit-LimitMaximum requests per window (100 or 1000)
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying
// Recommended retry logic
if (response.status === 429) {
  const retryAfter = parseInt(response.headers.get("retry-after") || "5");
  await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
  // Retry the request
}
Need More Help?