Docs/Reference/Error Codes

Error Codes

Complete reference of error codes and how to handle them

Error Response Format

All API errors follow a consistent JSON structure:

{
  "error": {
    "code": "invalid_amount",
    "message": "Amount must be a positive integer in cents.",
    "details": {
      "field": "amount",
      "provided": -500,
      "expected": "positive integer"
    }
  }
}
FieldTypeDescription
error.codestringMachine-readable error code
error.messagestringHuman-readable error description
error.detailsobject?Optional additional context about the error

HTTP Status Codes

CodeStatusDescription
400Bad RequestThe request body is malformed or missing required fields
401UnauthorizedMissing or invalid API key / JWT token
403ForbiddenAPI key lacks permission for this action
404Not FoundThe requested resource does not exist
409ConflictResource already exists or state conflict (e.g., duplicate idempotency key)
412Precondition FailedPayment cannot transition to the requested state
422Unprocessable EntityRequest is well-formed but contains invalid values
423LockedCustomer account is blocked (CoinFlow fraud detection)
428Precondition RequiredCustomer must re-verify identity (CoinFlow re-verification)
429Too Many RequestsRate limit exceeded (100 requests/minute per API key)
451Unavailable For Legal ReasonsKYC/KYB verification required before processing
500Internal Server ErrorUnexpected error on our end. Contact support if it persists
502Bad GatewayUpstream payment processor is temporarily unavailable

ACH-Specific Error Codes

These errors are returned when ACH transactions fail or are returned by the banking network:

CodeACH ReturnDescription
ach_insufficient_fundsR01Customer account has insufficient funds to cover the debit
ach_account_closedR02The bank account has been closed
ach_unauthorized_debitR07 / R10Customer claims the debit was not authorized
ach_account_frozenR16The bank account has been frozen by the bank or a legal authority

ACH returns can arrive up to 60 days after the original transaction. Always listen for ach.returned webhook events to handle late returns and reverse any fulfilled orders.

CoinFlow-Specific Errors

These errors originate from the CoinFlow payment processor and require specific handling:

423

Customer Blocked

CoinFlow's fraud detection has flagged and blocked this customer. The customer cannot make further purchases until the block is resolved.

Action: Direct the customer to contact support. Do not retry automatically.

428

Re-verification Required

The customer must complete additional identity verification before continuing. This is triggered by changes in transaction patterns or risk signals.

Action: Redirect the customer to the CoinFlow verification flow using the verification_url in the error details.

451

KYC Required

Know Your Customer verification is required before processing this transaction. New customers or high-value transactions may trigger this requirement.

Action: Redirect the customer to complete KYC using the kyc_url in the error details. The payment can be retried after verification.

Error Handling Best Practices

Recommended Pattern

Use the error.code field for programmatic error handling, and display the error.message field to users. The error.details object provides additional context for debugging.

  • Use error codes, not status codes: Multiple error types can share the same HTTP status code. Always check error.code for specific handling
  • Retry with backoff for 5xx errors: Server errors are usually transient. Retry with exponential backoff (1s, 2s, 4s, 8s, max 3 retries)
  • Never retry 4xx errors blindly: Client errors require fixing the request before retrying (except 429, which should be retried after the rate limit resets)
  • Log the full error response: Always log the complete error object including details for debugging
  • Handle 423, 428, 451 gracefully: These CoinFlow-specific errors require user action, not automatic retries
  • Use idempotency keys: For POST requests, include an Idempotency-Key header to safely retry failed requests without creating duplicates
try {
  const payment = await hedge.payments.create({
    amount: 5000,
    currency: 'USD',
    customer_email: 'customer@example.com'
  })
} catch (err) {
  switch (err.code) {
    case 'invalid_amount':
      // Fix the request parameters
      console.error('Invalid amount:', err.details)
      break

    case 'rate_limit_exceeded':
      // Wait and retry
      const retryAfter = err.details?.retry_after || 60
      await sleep(retryAfter * 1000)
      break

    case 'customer_blocked':
      // Direct user to support
      showMessage('Please contact support for assistance.')
      break

    case 'kyc_required':
      // Redirect to KYC flow
      window.location.href = err.details.kyc_url
      break

    default:
      // Log and show generic error
      console.error('Payment error:', err)
      showMessage('Something went wrong. Please try again.')
  }
}