Errors
Three questions sit behind every failure on this page: did the request
execute, is retrying safe, and what to do. Every error type below is marked
with the first two:
| Did my money move? | What it means |
|---|---|
| No | The request did not execute. Nothing was sent, debited, or changed |
| Maybe | Execution is uncertain. Read the payout back before doing anything |
| Can I retry? | What to do |
|---|---|
| As is | Send it again unchanged, with the same idempotency key |
| After a fix | Fix something first. The exact same request fails identically |
| Never | Retrying cannot succeed |
No error type on this page means “the payout was sent and then failed”. That is
a payout whose status is failed, carrying a failureCode; see
Payout status.
Every failure has the same shape. Branch on type, which is stable across
versions; the HTTP status and the wording are not.
{ "type": "RATE_DRIFT_EXCEEDED", "status": 400, "detail": "Refusing to send: quoted 3384.65 but you expected ~9999 (6615 bps of drift, limit 200). Nothing was sent.", "resolution": "Nothing was sent. Re-quote, show the payer the new amount, and send again.", "requestId": "req-1c",}detail is always a string. resolution is optional, so fall back to
detail. Validation failures add errors, the fields that failed. Every error
body has requestId; on a success it is the x-request-id header, so log it.
There is no retryable field; the Node client derives one from type.
A payout that was accepted and later failed is not an error response. It has
status: failed and a failureCode (Track status & failures).
Request errors
Section titled “Request errors”type |
Status | Money moved? | Retry? | What to do |
|---|---|---|---|---|
VALIDATION_ERROR |
400 | No | After a fix | Fix the fields named in errors |
UNAUTHORIZED |
401 | No | After a fix | Key missing, malformed, unknown, revoked or copied incompletely |
FORBIDDEN |
403 | No | After a fix | The key can’t access that organization or operation. Check AVVIO_ORG_ID |
NOT_FOUND |
404 | No | After a fix | No such payout or recipient. Use ids we returned |
ROUTE_NOT_FOUND |
404 | No | Never | No route matches. Payouts are sent to /payouts and read from /orders |
RATE_LIMITED |
429 | No | As is | Wait for Retry-After, then resend |
BAD_REQUEST |
400 | No | After a fix | A non-field 400 (expired quote, above the corridor maximum, non-positive amount). detail names it |
PROVIDER_REJECTED |
400 | No | After a fix | The network refused before submission, usually below the corridor minimum |
CONFLICT |
409 | Maybe | After a fix | Funding state changed under you (another transaction or request won). Re-read the payout |
ACCOUNT_BLOCKED |
403 | No | Never | Your organization is suspended. Contact us |
LIVE_ |
403 | No | After a fix | A live key wrote before business verification. GETs work; use a test key until approved |
INSUFFICIENT_ |
400 | No | After a fix | Nothing was sent. Top up, then retry. A payroll run must branch on this |
CRYPTO_ |
400 | No | After a fix | Use a key with scopes ["write", "crypto_ |
WALLET_ |
400 | No | After a fix | No EVM wallet to pay a wallet recipient. Finish wallet setup in the dashboard |
HISTORY_TOO_LARGE |
413 | No | Never | Your organization has more orders than GET / will replay. Page GET / instead |
ORDERS_ |
503 | No | As is | A payment-network read failed while building the page, and we won’t return a partial list as complete. Retry. (A cursor we didn’t issue is a 400 VALIDATION_, cursor: unrecognized) |
INTERNAL |
500 | Maybe | As is | Nothing was recorded and the key was released. Retry with the same key; send the requestId if it persists |
PAYOUT_ |
503 | No | As is | Hosted payout links aren’t configured on this environment. Contact support |
CORRIDOR_ |
400 | No | After a fix | Node client: the corridor isn’t on your routing. Pick one the corridors call lists |
TIMEOUT |
504 | Maybe | As is | Node client. A send may exist. Retry with the same Idempotency-Key |
NETWORK_ERROR |
502 | Maybe | As is | Node client (DNS, TLS, dropped connection). Retry with the same key, then read the payout back |
API keys
Section titled “API keys”type |
Status | Money moved? | Retry? | What to do |
|---|---|---|---|---|
KEY_EXPIRED |
401 | No | After a fix | Issue a new key; an expired key can’t be rotated |
KEY_IP_NOT_ALLOWED |
401 | No | After a fix | The key is pinned and the request came from another address |
Idempotency
Section titled “Idempotency”type |
Status | Money moved? | Retry? | What to do |
|---|---|---|---|---|
IDEMPOTENCY_ |
400 | No | After a fix | Add an Idempotency-Key, unique per operation |
IDEMPOTENCY_ |
400 | No | After a fix | Use 1–255 characters of A-Z a-z 0-9 _ . : -. A UUID works |
IDEMPOTENCY_ |
409 | No | Never | Same key, different body. Don’t retry; a different request needs a new key |
IDEMPOTENCY_ |
409 | Maybe | As is | An identical request is still running. Back off and retry the same key |
IDEMPOTENCY_ |
503 | No | As is | We couldn’t record the key and nothing executed. Retry the same key |
PAYOUT_ |
500, 409 | Maybe | Never | No proof a money operation didn’t execute; the payout may exist and the key is kept. Replays answer 409. Do not retry with a new key. Poll GET /, send support the requestId (Timeouts) |
DUPLICATE_ |
409 | No | After a fix | An identical body under a different key within 15 minutes. Nothing executed. See below |
Some HTTP clients mint a new key per attempt, so every retry would pay.
DUPLICATE_REQUEST_DETECTED catches that for 15 minutes, and its body names
originalIdempotencyKey, plus originalPayoutId when the first request
finished with a payout.
- If it was a retry, send it again with
originalIdempotencyKeyin theIdempotency-Keyheader (not the body, which rejects unknown properties). It replays and returns the original payout. - If you meant two payments, add
X-Allow-Duplicate: true. This sends a second real payment.
Fifteen minutes covers a crashed job that requeues on a backoff. It is shorter than the seven-day key retention because content can’t
tell a retry from a genuine repeat: two identical advances in a week are
ordinary payroll. A unique reference per payment means the guard never fires
falsely. Idempotency
covers retention and exceptions.
Sending and funding
Section titled “Sending and funding”type |
Status | Money moved? | Retry? | What to do |
|---|---|---|---|---|
DESTINATION_ |
404 | No | After a fix | Refused before pricing, so a stale id can’t report completed with nobody paid. Pay only ids we returned |
RATE_DRIFT_EXCEEDED |
400 | No | After a fix | The quote moved past expectDestination. Re-quote, show the new amount, send again |
QUOTE_UNVERIFIABLE |
400 | No | After a fix | We couldn’t compare the quote to your expectation, so nothing was sent. detail says why |
QUOTE_NOT_POSITIVE |
400 | No | After a fix | Nothing would arrive after fees. Increase the amount and re-quote |
EXACT_ |
400 | No | After a fix | The routing can’t lock the receiving amount. Check capabilities.exactOutput |
INDICATIVE_ |
400 | No | Never | The routing prices only against a recipient (capabilities.indicativePricing: false). Create the recipient, then price |
PAYOUT_ |
400 | No | Never | Only a payout awaiting your own funds can be canceled |
INSUFFICIENT_SCOPE |
403 | No | After a fix | Read-only key, or a missing consent (refunds). Use a write key; an owner or admin can add refunds |
LIVE_ |
400 | No | After a fix | A live key on a sandbox-only route. Use an avvio_test_* key |
PAYOUT_ |
422 | No | After a fix | Over a single, daily or per-end-user limit, named in the message. Split it, or ask us to raise it |
PAYOUT_REFUSED |
422 | No | Never | Final refusal for this recipient. Contact us with the requestId |
USE_POST_PAYOUTS |
403 | No | After a fix | An approval threshold or velocity cap applies, which only POST /payouts enforces. Send it there; it may answer 202 |
BENEFICIARY_ |
409 | No | After a fix | The externalId names a recipient with other account details. Use a new one, or read the existing recipient |
BANK_ |
409 | No | After a fix | The account is on another recipient. Pay existingRecipientId / existingMethodId |
PAYOUT_ |
503 | No | As is | The account couldn’t be registered and nothing was saved. Retry with the same Idempotency-Key |
FUNDING_ |
501 | No | Never | The payout settles from your balance. Fund only when requiresFunding is true |
FUNDING_ |
400 | No | After a fix | The transaction doesn’t fund this payout (reverted, wrong token, address or wallet, short). Nothing recorded |
FUNDING_ |
409 | No | As is | Not mined or readable yet. Nothing recorded. Confirm the same hash later |
FUNDING_ |
409 | No | After a fix | It funded another payout, named in detail. Send a separate transfer |
PAYOUT_NOT_FUNDABLE |
400 | No | Never | The payout is canceled or finished. If you still owe the recipient, create a new payout |
FUNDING_ |
409 | No | After a fix | Dashboard wallet funding: the quote expired and the payout wasn’t sent. Funding already sent stays recorded. Refresh and review the new price |
LINK_EXPIRED |
400 | No | After a fix | A checkout draft’s expiresAt has passed. PATCH a later one (or null) and publish. In a publish: true create it is publishError.type on a 201 |
Refunds
Section titled “Refunds”Every refusal here left the money where it was. Only REFUND_OUTCOME_UNKNOWN
may have moved it.
type |
Status | Money moved? | Retry? | What to do |
|---|---|---|---|---|
REFUND_NOT_ALLOWED |
400 | No | Never | Already refunded in full, charged back, failed or pending |
REFUND_DISPUTED |
400 | No | After a fix | A dispute decides. Wait; a loss arrives as checkout_ |
REFUND_ |
400 | No | After a fix | More than amountBase - refundedBase. Lower it, or omit it to refund the rest |
REFUND_IN_PROGRESS |
409 | No | After a fix | One refund at a time. Once refundedBase moves, send another if needed |
REFUND_ |
502 | Maybe | Never | It may have gone through. Don’t resend; the payment is re-read and the event follows if it did |
Batches and payout links
Section titled “Batches and payout links”type |
Status | Money moved? | Retry? | What to do |
|---|---|---|---|---|
BATCH_NOT_FOUND |
404 | No | After a fix | No batch with that id in your organization |
PAYOUT_ |
409 | No | After a fix | Not awaiting_. Read it back; it may still be validating |
PAYOUT_ |
409 | No | Never | Creation started, so the run is committed. Cancel single pending payouts with POST …/ |
PAYOUT_ |
409 | No | After a fix | The run needs approvers, or one rejected it. approvalId names the request |
PAYOUT_ |
409 | No | After a fix | externalReferenceId already names run originalBatchId. Read it, or pick a new id |
MASS_ |
403 | No | Never | Your organization opted out of batches. Contact support |
PAYOUT_ |
400 | No | After a fix | Expired, spent, or failed at execution. Links are single-use; mint a new one |
A failed line inside an accepted batch is an item with status invalid or
create_failed and errors[], read with ?status=invalid. Submitting a spent
payout link returns the original payout with status: "already_submitted". A
404 on a link route covers expired, spent, forged and unknown links alike, so
probing learns nothing.
Was this page helpful?