Skip to content

Ramp errors

Same envelope as the rest of v2:

json
{
  "error": {
    "code": "invalid_request",
    "message": "intentKey is required",
    "request_id": "req_…"
  }
}

code is stable. Branch on it, not on message. request_id is also the X-Request-Id header; quote it when you report a problem.

Four codes carry extra fields on the error object itself, because each has its own recovery and the screen needs the data to run it. They are listed first.

Codes with a recovery

kyc_required

422. A buy was attempted for a person the provider will not take a deposit from yet. error.kyc is the current review state, in the shape /customers/kyc/status returns, so you can pick the right screen without another call.

json
{
  "error": {
    "code": "kyc_required",
    "message": "Identity verification is needed before a deposit",
    "kyc": { "state": "unverified", "rejectionReasons": [], "…": "…" }
  }
}

Route on kyc.state: unverified and expired go to the document form, pending to the waiting screen, rejected to the form with the reasons.

ramp_customer_required

409. The provider holds no profile for this wallet and the side you are running cannot proceed without one. Send the user to the details form and drop any local "already registered" hint you kept. A generic retry will fail the same way.

quote_drift

409. The executable price moved more than 1% from expectedTargetAmount. Carries expectedTargetAmount, actualTargetAmount, rateLabel, feeLabel and, on a sell, providerRecipientId. Nothing was booked. Show the new terms and, if accepted, resend. Details under Price drift.

provider_refused

422. The provider refused this specific action and said why. message is its sentence, unaltered: an account number the bank does not recognise, a deposit above the corridor cap, a person it will not register. Show it. This is different from upstream_error, which means we could not reach the provider at all and have nothing to tell the user.

All codes

HTTPcodeMeansWhat to do
400invalid_requestA missing or malformed field: no intentKey, an amount that is not positive, a document.type outside the three allowed, an image over 4MB, a sell without payout, a transfer for a wallet with no customer record, or sandbox: true outside stagingFix the request. message names the field
401unauthorizedproof missing, stale, from the wrong wallet, or replayed. details.reason is one of the proof_of_control_* reasonsSign a fresh challenge with the wallet the request names
403forbiddenThe provider does not advertise that corridor, side or chain, the provider is switched off, or sandbox was requested where no sandbox existsRe-read /providers and offer only what it lists
404not_foundNo such transfer for you: wrong id, wrong claim token, or a wallet that did not make it. Also an unknown providerCheck the id and the X-Ramp-Claim header
409ramp_customer_requiredNo provider profile, and the side needs oneDetails form
409quote_driftPrice moved past the boundShow the new terms, resend on accept
422kyc_requiredThe person is not verified for a depositVerification screen, per error.kyc.state
422provider_refusedThe provider declined, with a reasonShow message
429rate_limitedToo many requests from your IPBack off. See Retry-After
502upstream_errorThe provider could not be reached, or a create with this intentKey is still in flightRetry with backoff, same intentKey
500internalSomething broke on our sideRetry; if it persists send us the request_id

Treat 429 and 502 as transient. Treat 400, 401, 403 and 404 as your bug. Treat the four 409/422 codes as normal branches of the flow.

Rate limits

The ramp surface has no API keys, so the only limit is a per-IP ceiling shared with the rest of /v2. It is a brute-force backstop, not a budget: a client that polls one transfer every 3 seconds and one review every 10 is nowhere near it. A tight loop is. Over it you get 429 with Retry-After.

Two throttles sit behind the API on your behalf and are not errors: a transfer's provider state is re-read at most once every 8 seconds, and a person's review state at most once every 15. Polling faster than that returns the stored answer.

Timeouts

POST /v2/ramp/transfers makes several provider calls in sequence (recipient, quote, transfer on a sell) and can take a few seconds. Set your client timeout at 30 seconds or more. If it does time out, retry with the same intentKey; that is what it is for.