Skip to main content
An ONRAMP order converts a customer’s fiat deposit into a crypto asset. The fiat-in leg is synthetic — no real bank transfer, deterministic and instant. The crypto delivery leg (Conduit → the customer’s wallet) is different on the deployed sandbox: for a real-testnet customer it is a real on-chain transfer, so the delivered txHash is verifiable on the network’s block explorer, it takes minutes to reach finality rather than seconds, and it can occasionally be delayed before it settles.
The crypto delivery is real on-chain (real-testnet customers). order.succeeded no longer fires “within seconds” — the delivery broadcasts on a real testnet and waits for finality (minutes). The customer signs nothing (Conduit sends the delivery), but the transfer is real: the credited txHash resolves on the explorer, and the wallet balance moves on-chain. The delivery can occasionally be delayed before it settles — for example while it waits for shared testnet liquidity on that chain. A short delay clears on its own; watch for order.succeeded. But if the order stays pending well past normal finality time, the delivery is held awaiting that liquidity and does not always resume on its own — contact support to have it released or returned. See Use a real testnet.
The fiat-in and crypto-delivery legs both originate inside Conduit (there is no external actor delivering fiat, and the customer does not send the crypto), so there is no mid-flow injection lever for them. Use orders/:id/simulate/conversion-failed (below) to drive the whole order to a failed terminal state, or create the pending auto-execute order first and then inject a source fiat deposit whose senderInfo.accountNumber carries a failure suffix — see Deposits and Sandbox overview.

Prerequisites

  • An active customer with at least one virtual account for the source fiat asset. Follow Sandbox quickstart if you haven’t set this up yet.
  • A claimed, active destination wallet for the crypto asset you want to receive.
  • Your sandbox API key exported as SANDBOX_API_KEY.
  • The customer’s virtual account ID exported as VA_ID.
  • The destination wallet ID exported as WALLET_ID.

Lifecycle overview

An ONRAMP order moves through these stages: the order is created and rate-locked → the source (fiat-in) and destination (crypto-out) conversion legs finalize automatically → order.succeeded fires. The conversion sub-leg is covered in detail on /sandbox/conversions. Because both legs are internal movements in sandbox, there are no manual settle calls in the happy path. Use orders/:id/simulate/conversion-failed to force the order to a failed terminal state, or create the pending order and then simulate a source fiat deposit with a deposit suffix. The order itself only sends a webhook at its terminal state: order.succeeded, order.failed, or order.cancelled. For progress before that, watch the order’s transaction instead — it carries linkedOrderId matching the order, and fires transaction.created when the conversion starts and transaction.completed when the crypto delivery reaches finality (transaction.failed or transaction.cancelled on the matching failure paths).

Full happy path

Step A — Create the order

Create the order by supplying the source virtual account (USD), the destination wallet (USDC), the lock side, and the amount. Set autoExecute: true to let the platform begin execution immediately. The example uses a small amount (1.00) so the crypto it delivers stays under the per-chain, per-UTC-day liquidity cap a new customer has — a larger amount is refused with 422 SANDBOX_ONRAMP_DAILY_CAP_EXCEEDED. See Errors for the cap and how to free headroom.
202 Accepted. Capture id as ORDER_ID. The fiat-in leg finalizes synthetically; the crypto delivery leg then settles — on a real testnet (minutes, real txHash, can be delayed; see the warning above). No manual settle calls are needed. order.succeeded (status: succeeded) fires once delivery reaches finality; see Lifecycle overview for the events that fire before then.

Send to a wallet outside Conduit

The destination can be a wallet your customer holds elsewhere. The customer needs no Conduit wallet and signs nothing: the converted crypto goes to recipient.address in one on-chain transfer.
  • recipient is the same crypto recipient as on POST /v2/payouts. For a wallet that belongs to someone else, set attestation.custody to third_party and add their identity (type plus firstName, lastName and countryOfCitizenship, or legalName and countryOfRegistration).
  • recipient.chain must equal asset.chain.
  • purpose and documents follow the same rules as a payout. A purpose of intercompany or personal_transfer_of_funds needs the address registered first (Registered Addresses).
  • The order creates two transactions: the conversion, then a withdrawal to the address, linked by the order id. The withdrawal is screened like any crypto payout and cannot be cancelled once it exists.

Simulate conversion failure

Force the in-flight conversion to fail regardless of which leg it is waiting on. The order terminates in failed; any funds already debited from the source are returned automatically.
200 OK returning the order at its current state. Webhook: order.failed carrying orderId, customerId, clientReferenceId, reasonCode, failureMessage (the standard text for that reasonCode, as in production), and failedAt. The reason you supply is kept as the note on the cancelled transfer, not on the order. See /sandbox/conversions for more detail.

Source-deposit compliance failure

For unfunded ONRAMP tests, create the order with autoExecute: true, then inject the fiat source deposit through the deposits simulator with senderInfo.accountNumber ending in 95009001 or 95009002. The deposit holds for a compliance decision and stays pending — it does not freeze on its own. Reject it with POST https://api.sandbox.conduit.financial/v2/sandbox/transactions/{depositId}/simulate/compliance-decision and { "outcome": "reject" } (see Deposits): the deposit then fails with transaction.failed and failureCode: "compliance_hold", and the oldest pending auto-execute order that matches the deposit and is amount-covered emits order.failed with reasonCode: "provider_rejected". orders/:id/simulate/conversion-failed remains the lever for conversion-level failures after the order has begun executing.

Simulate rate-lock expiry

Expires the order’s rate lock now. The order transitions to cancelled with cancellationReason: "expired".
200 OK returning the order at its current state. Webhook: order.cancelled with reason: "expired". Cancellation lands within seconds. There is no fixed delay: wait for order.cancelled.

Order status reference

Intermediate steps (source settled, conversion in progress) are not reflected as a separate order status. Watch the order’s transaction instead — it carries linkedOrderId and fires transaction.created and transaction.completed (or transaction.failed / transaction.cancelled) as it progresses. Only the order’s own terminal outcomes (succeeded, failed, cancelled) surface a webhook on the order itself.

Webhook events

Errors

Each customer has a cap on the net crypto they can draw from sandbox’s shared testnet liquidity, tracked per chain and per UTC day, which keeps that liquidity available across customers. An onramp (and the outbound leg of a cross-chain conversion) draws liquidity down; an offramp (and the inbound leg of a conversion) returns it and frees the customer’s headroom on that chain. An onramp or conversion that would carry the customer’s net draw for the day over the cap returns 422 SANDBOX_ONRAMP_DAILY_CAP_EXCEEDED at order creation or execution. A new customer’s default is about $2 of net crypto per chain per day, so keep test amounts small; ask support to raise it. The cap resets at each UTC midnight: the previous day’s completed draws stop counting and headroom is full again on every chain, except for an onramp still in flight that keeps its reservation until it settles or fails. To free headroom sooner for a priced asset, return crypto with an offramp on the same chain. A testnet asset with no USD price is capped by a per-day transaction count instead; returning crypto does not lower that count, so wait for the next UTC day or ask support to raise it. On live, Conduit refuses an onramp for a customer in the United States. Sandbox accepts it. See Customers in the United States. Simulate endpoints return 404 ORDER_NOT_FOUND when the order does not exist or belongs to a different organization. Replays against an already-terminal order return 200 with the order at its current state (idempotent). If execution has not yet started, the simulator arms the failure for when it does. See /errors for the full error shape. The order.failed payload carries reasonCode (insufficient_funds / provider_unavailable / provider_rejected / internal_error / cancelled / insufficient_onchain_balance / source_account_unavailable) describing the failure category.

Sequence diagram