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.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
| Status | Meaning | Retry? |
|---|---|---|
400 | Validation failed, or structurally invalid: unsupported or disabled asset, bad address, missing Idempotency-Key, or a successUrl/cancelUrl host that isn't a redirect domain | No. Fix the request |
401 | Bad or missing auth, stale timestamp, replayed signature, revoked session, or a 2FA code required or already used | Only after re-signing, and only once. Don't loop |
402 | FEE_WALLET_INSUFFICIENT: the asset requires a funded fee wallet | No. Top up, then retry |
403 | Account rejected, onboarding incomplete, IP not whitelisted, blocked destination, or a dashboard-only action attempted with a key | No. It needs a person or a setting change |
404 | Not found, or not yours | No |
409 | Conflict: insufficient balance, an invalid state change, or an Idempotency-Key reused with different parameters | No. Read the state |
429 | Rate limited, or an IP locked after repeated auth failures | Yes, with backoff. Honour the limit headers |
502 | An internal service can't be reached | Yes. Safe to retry |
503 | A security dependency is unavailable and the gateway fails closed | Yes, with a fresh signature and the same Idempotency-Key |
03Error codes worth handling
| `code` | Where | Meaning and action |
|---|---|---|
ONBOARDING_REQUIRED | POST /v1/invoices | Business profile not submitted. Surface to a person; read onboardingStatus |
ACCOUNT_REJECTED | Invoices, withdrawals, fee-wallet top-ups | The application was rejected. Final; contact support |
REDIRECT_URL_NOT_ALLOWED | POST /v1/invoices | Carries field (successUrl or cancelUrl). Register the host as a redirect domain in the dashboard, or fix the URL (hosted checkout) |
FEE_WALLET_INSUFFICIENT | Invoices, withdrawals | Carries requiredUsd. Top up the fee wallet |
BELOW_MINIMUM_INVOICE | POST /v1/invoices | Carries chainId and minimumUsd. Raise the amount or pick another chain |
BELOW_MINIMUM_WITHDRAWAL | Withdrawals, quote | Carries minimumUsd |
AMOUNT_TOO_SMALL_FOR_NETWORK_FEE | Withdrawals, quote | Carries networkFee and minimumAmount. Withdraw at least that |
NETWORK_FEE_UNAVAILABLE | Withdrawals, quote | 503: the fee couldn't be priced. Retry; nothing was charged |
two_factor_required | Dashboard-session withdrawals | 2FA isn't enabled on the account |
TOKEN_NOT_ACTIVE_YET | Custom tokens | Carries usableFrom. Retry after that time |
TOKEN_DISABLED | Custom tokens | No new invoices in this token; existing balances can still be withdrawn |
04Rate limits
| Limiter | Applies to | Limit |
|---|---|---|
| General | Every request | 120 per minute per IP |
| Strict | Writes: creating invoices and withdrawals, settings changes | 20 per minute per IP |
| Signup | POST /v1/merchants/signup | 5 per hour per IP |
| Login | POST /v1/auth/login | 10 per 15 minutes per IP, and 5 failures per 15 minutes per account |
| Auth failures | Any request with a bad key or signature | 30 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.
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 a4xxis a real refusal, not a transient fault. - A gas shortfall on a withdrawal is not an API error: the request succeeds as
processing. Watchwithdrawal.status_changedor pollGET /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.
Keep reading
- How to sign crypto payment API requests (HMAC-SHA256)How to sign Payaries API requests with HMAC-SHA256, with working Node.js, Python, Go and shell code and published test vectors to check your implementation against.
- Withdrawing crypto through the API: quote, idempotency and approvalHow to withdraw crypto through the Payaries API: check the balance, quote the fees, create an idempotent withdrawal, follow its status and understand auto-approval.
- Crypto payment API quickstart: from signup to your first payoutTake your first crypto payment through the Payaries API: create a key, submit the business profile, create an invoice, handle the webhook and withdraw. Real requests and responses.
- A crypto payment API that behaves like the rest of your stackA REST crypto payment API: create an invoice, get a one-time deposit address, receive signed webhooks and withdraw on demand. HMAC-signed and idempotent.
- Hosted crypto checkout: send the customer, get them backUse Payaries's hosted payment page without building a checkout: create an invoice with a success and cancel URL, redirect the customer, verify the signed return and fulfil from the webhook.
- Developer hubAPI reference, guides and the OpenAPI contract in one place.
- Build a crypto payment integration with an AI agent, safelyEverything an AI agent or coding assistant needs to integrate Payaries: an OpenAPI spec, llms.txt, markdown docs, signing test vectors and the rules for handling real funds safely.