Skip to main content
When a money movement or verification fails, the resource carries two fields that explain why:
  • failure_reason β€” a stable, machine-readable code. Branch on this.
  • failure_reason_message β€” human-readable copy describing the failure. Show this to a user, but do not hardcode or match on it: the wording is English today and may be reworded or localized later.
Both fields appear in REST responses and in the matching webhook event payloads, so you get the same reason whether you poll or receive a push.
Branch on failure_reason, not on failure_reason_message β€” the code is stable, the message is not. This mirrors how you branch on a top-level error code rather than its message. See Errors.

When the fields are present

failure_reason and failure_reason_message are null (or absent) unless the resource actually reached a bad outcome. They are populated on:
  • Money movements that did not succeed β€” an onramp, offramp, conversion, or transfer with status: "failed", and an onramp, offramp, or conversion with status: "returned".
  • Failed verifications β€” the kyc.rejected webhook event and the verification endpoints.
returned is the one case where these fields are set while status is not "failed". If you read failure_reason only when status === "failed", you will miss it. Gate on the resource not having succeeded, not on the literal string "failed".
Some codes are intentionally generic. We expose what is safe and actionable; we do not reveal internal or provider mechanics. Any internal state we do not surface specifically collapses to a generic code β€” failed for money movements, verification_failed for verifications.

Money movement codes

These apply to an onramp, offramp, conversion, or transfer that did not succeed. temporarily_unavailable is the retryable one β€” retry the request with backoff. failed is deliberately generic: a provider decline, invalid upstream input, a released hold, and anything otherwise unrecognised all collapse to it. quote_expired means nobody paid in before the quote’s rate window closed β€” not that anything went wrong on our side. No funds moved, so it is always safe to create a fresh quote and try again. This is the most common non-success outcome by a wide margin, so if you alert on failures, exclude this code: counting it inflates your apparent failure rate with transactions that were simply never funded. It applies to onramp, offramp, and conversion. funding_failed is reported on an offramp whose funding transfer could not be completed. The offramp fails immediately rather than sitting in processing until its quote expires, and no funds moved β€” create a new quote and retry. refund_failed means the transaction failed and the refund attempt did not complete. Unlike the other codes it may need our involvement to resolve β€” contact support with the resource id rather than retrying. returned accompanies status: "returned", not "failed" (see the warning above). Its message deliberately does not promise a credit, because whether one happens depends on how far the transaction got: an offramp whose payout had already settled is credited back in full β€” appearing as a deposit-type transaction β€” while a transaction that never debited you has nothing to credit back. Check your wallet balance or transactions list rather than inferring a credit from this code.

Verification (KYC) codes

These apply to the kyc.rejected webhook event and the verification endpoints. The first eight are actionable β€” the applicant can usually fix the problem and resubmit. verification_failed is deliberately generic: any rejection we should not detail collapses to it, and we never reveal the underlying detection.