Guide · Developers

Crypto payment API errors, rate limits and safe retries

Errors are plain JSON with conventional status codes. The skill is knowing which ones are an answer and which are an outage, and retrying the second kind without ever paying twice.

Updated · 3 min read

Build it with an AI agent

Give this to your AI agent

Paste it into Claude Code, Cursor or any agent that can read a URL. It reads the OpenAPI contract and the signing recipe, and follows the rules for handling real funds.

Read https://www.payaries.com/ai-agents.md and integrate Payaries crypto payments into this project. Follow its rules for handling real funds.
OpenAPI specDocs as markdown

Never paste your API secret into an agent. Keep it in an environment variable.

01The error shape

There is no envelope. A validation failure is { "error": "Validation failed", "details": { ... } }; everything else is { "error": "Human-readable message" }, often with a machine-readable code and context. Match on the status code first and on code where present, never on the text.

02Status codes and what to do

StatusMeaningRetry?
400Validation failed, or structurally invalid: unsupported or disabled asset, bad address, missing Idempotency-Key, or a successUrl/cancelUrl host that isn't a redirect domainNo. Fix the request
401Bad or missing auth, stale timestamp, replayed signature, revoked session, or a 2FA code required or already usedOnly after re-signing, and only once. Don't loop
402FEE_WALLET_INSUFFICIENT: the asset requires a funded fee walletNo. Top up, then retry
403Account rejected, onboarding incomplete, IP not whitelisted, blocked destination, or a dashboard-only action attempted with a keyNo. It needs a person or a setting change
404Not found, or not yoursNo
409Conflict: insufficient balance, an invalid state change, or an Idempotency-Key reused with different parametersNo. Read the state
429Rate limited, or an IP locked after repeated auth failuresYes, with backoff. Honour the limit headers
502An internal service can't be reachedYes. Safe to retry
503A security dependency is unavailable and the gateway fails closedYes, with a fresh signature and the same Idempotency-Key

03Error codes worth handling

`code`WhereMeaning and action
ONBOARDING_REQUIREDPOST /v1/invoicesBusiness profile not submitted. Surface to a person; read onboardingStatus
ACCOUNT_REJECTEDInvoices, withdrawals, fee-wallet top-upsThe application was rejected. Final; contact support
REDIRECT_URL_NOT_ALLOWEDPOST /v1/invoicesCarries field (successUrl or cancelUrl). Register the host as a redirect domain in the dashboard, or fix the URL (hosted checkout)
FEE_WALLET_INSUFFICIENTInvoices, withdrawalsCarries requiredUsd. Top up the fee wallet
BELOW_MINIMUM_INVOICEPOST /v1/invoicesCarries chainId and minimumUsd. Raise the amount or pick another chain
BELOW_MINIMUM_WITHDRAWALWithdrawals, quoteCarries minimumUsd
AMOUNT_TOO_SMALL_FOR_NETWORK_FEEWithdrawals, quoteCarries networkFee and minimumAmount. Withdraw at least that
NETWORK_FEE_UNAVAILABLEWithdrawals, quote503: the fee couldn't be priced. Retry; nothing was charged
two_factor_requiredDashboard-session withdrawals2FA isn't enabled on the account
TOKEN_NOT_ACTIVE_YETCustom tokensCarries usableFrom. Retry after that time
TOKEN_DISABLEDCustom tokensNo new invoices in this token; existing balances can still be withdrawn

04Rate limits

LimiterApplies toLimit
GeneralEvery request120 per minute per IP
StrictWrites: creating invoices and withdrawals, settings changes20 per minute per IP
SignupPOST /v1/merchants/signup5 per hour per IP
LoginPOST /v1/auth/login10 per 15 minutes per IP, and 5 failures per 15 minutes per account
Auth failuresAny request with a bad key or signature30 failures in 10 minutes locks that IP for 10 minutes

A 429 includes standard rate-limit headers. Limits are shared across gateway replicas.

05A retry loop that can't double-pay

Retry only 429, 502 and 503, with jittered exponential backoff. The request function signs afresh on every call; a withdrawal's Idempotency-Key is generated once, stored with your payout record, and reused on every attempt, so a network-level retry can never pay twice.

Node.js
async function withRetry(makeRequest, { attempts = 5 } = {}) {
  for (let i = 0; i < attempts; i++) {
    const res = await makeRequest();          // makeRequest signs afresh each time: new timestamp, new signature
    if (res.status < 400) return res;
    const retryable = res.status === 429 || res.status === 502 || res.status === 503;
    if (!retryable || i === attempts - 1) return res;   // 4xx is an answer, not an outage: do not retry it
    await new Promise((r) => setTimeout(r, Math.min(30000, 500 * 2 ** i) * (0.5 + Math.random() / 2)));
  }
}

// A withdrawal keeps the SAME Idempotency-Key on every attempt:
const key = 'wd_2026_10_10_0001';   // generated once, stored with your payout record
await withRetry(() => call('POST', '/v1/withdrawals', body, { 'Idempotency-Key': key }));
  • A timeout is not a failure. The request may have succeeded; retry with the same idempotency key, or read the resource.
  • Don't retry a 4xx. On invoice creation a 4xx is a real refusal, not a transient fault.
  • A gas shortfall on a withdrawal is not an API error: the request succeeds as processing. Watch withdrawal.status_changed or poll GET /v1/withdrawals/:id.

Questions

Which Payaries API errors are safe to retry?
429 (with backoff), 502, and 503, which means the gateway is failing closed on a security check. Re-sign every attempt, and keep the same Idempotency-Key on a withdrawal. Other 4xx responses are answers about the request and should not be retried.
What are the Payaries API rate limits?
120 requests a minute per IP overall and 20 a minute for writes such as creating invoices and withdrawals. Thirty failed API-key authentications from one IP within ten minutes lock that IP for ten minutes.
How do I stop a retry from creating a second withdrawal?
Send an Idempotency-Key header and reuse the same value on every retry of that withdrawal. The same key with the same parameters returns the original withdrawal; the same key with different parameters is a 409.

Next

Create a test key and run a real invoice

Test keys work immediately, with no approval step. Payaries runs on real mainnet networks, so use a small real payment to test.