Skip to main content
An ONRAMP order converts a customer’s fiat deposit into a crypto asset. In sandbox every bank transfer and crypto delivery is synthetic — no real funds move and no chain is involved. The source (fiat-in) and destination (crypto-out) conversion legs are internal movements that the mock provider finalizes automatically. order.succeeded fires within seconds of order creation — no manual settle calls are required for the happy path. Both legs are Conduit-internal movements (there is no external actor delivering fiat or crypto on either side). Because there is no real external actor that can independently fail these legs, 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.
  • Your sandbox API key exported as SANDBOX_API_KEY.
  • The customer’s virtual account ID exported as VA_ID.
export SANDBOX_API_KEY="ck_sandbox_..."
export CUSTOMER_ID="cus_..."
export VA_ID="vac_..."

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. Intermediate state is observable only via GET /v2/orders/:id (poll). Terminal status surfaces via webhook: order.succeeded or order.failed or order.cancelled.

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.
curl -X POST https://api.sandbox.conduit.financial/v2/orders \
  -H "x-api-key: $SANDBOX_API_KEY" \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "clientReferenceId": "cref-onramp-1",
    "source": { "type": "virtual_account", "id": "'"$VA_ID"'" },
    "destination": {
      "type": "wallet",
      "id": "wlt_<your-wallet-id>",
      "asset": { "code": "USDC", "chain": "ETHEREUM" }
    },
    "lockSide": "source",
    "amount": "100.00",
    "autoExecute": true
  }'
202 Accepted. Capture id as ORDER_ID. The mock provider auto-finalizes the source (fiat-in) and destination (crypto-out) legs internally. Webhook: order.succeeded (status: succeeded) fires within seconds — no manual settle calls are needed.
export ORDER_ID="ord_..."
Poll GET /v2/orders/$ORDER_ID to observe progress if needed. No webhook fires for intermediate leg state — only terminal outcomes emit a webhook.

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.
curl -X POST https://api.sandbox.conduit.financial/v2/sandbox/orders/$ORDER_ID/simulate/conversion-failed \
  -H "x-api-key: $SANDBOX_API_KEY" \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Provider rate stale"}'
200 OK returning the order at its current state. Webhook: order.failed carrying orderId, customerId, clientReferenceId, reasonCode, failureMessage (the reason you supplied), and failedAt. See /sandbox/conversions for more detail.

Source-deposit AML 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 freezes 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 within a few seconds. orders/:id/simulate/conversion-failed remains the lever for conversion-level failures after the order has begun executing.

Simulate rate-lock expiry

Backdates the order’s rate-lock timestamp so the next sweep cycle treats the lock as expired. The order transitions to cancelled with cancellationReason: "expired".
curl -X POST https://api.sandbox.conduit.financial/v2/sandbox/orders/$ORDER_ID/simulate/rate-lock-expired \
  -H "x-api-key: $SANDBOX_API_KEY" \
  -H "idempotency-key: $(uuidgen)"
200 OK returning the order at its current state. Webhook: order.cancelled with cancellationReason: "expired". Cancellation lands within ~1 second via an immediate background sweep tick. No need to wait for the scheduled 30-second sweep.
This endpoint enqueues an immediate sweep tick so you can verify your integration handles rate-lock expiry without waiting for the live lock window to elapse.

Order status reference

StatusMeaningPossible next statuses
pendingOrder created; rate locked. Execution has not started.succeeded, failed, cancelled
succeededSource debited, conversion completed, destination credited. Terminal.
failedExecution failed on the source, conversion, or destination leg. Terminal.
cancelledOrder cancelled before execution. cancellationReason is expired or client_cancelled. Terminal.
Intermediate steps (source settled, conversion in progress) are not reflected as order statuses and do not emit webhooks. Poll GET /v2/orders/{orderId} for current state; only the terminal outcomes (succeeded, failed, cancelled) surface via webhook.

Webhook events

EventWhen it fires
order.succeededOrder reached succeeded: source debited, destination credited. Carries transactionId, sourceAssetAmount, destinationAssetAmount.
order.failedOrder reached failed. reasonCode is INSUFFICIENT_FUNDS, PROVIDER_UNAVAILABLE, PROVIDER_REJECTED, INTERNAL_ERROR, or CANCELLED.
order.cancelledOrder cancelled before execution. cancellationReason is expired or client_cancelled.

Errors

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) describing the failure category.

Sequence diagram