Skip to main content
A conversion exchanges one crypto asset for another between two of a customer’s wallets. It’s one flow regardless of what changes:
  • a different asset on the same chain — e.g. USDC → USDT on Ethereum,
  • the same asset on a different chain — e.g. USDC on Ethereum → USDC on Base,
  • a different asset and a different chain — e.g. USDC on Ethereum → USDT on Tron.
All of them use the same endpoint (POST /v2/orders) and surface the same public type: "conversion". A conversion only converts — to send funds to an external address, use a payout.
Test this flow in sandbox. Drive it end-to-end with simulated money and deterministic controls — start with the sandbox quickstart, then conversion simulation for this flow, and the cheat sheet for every magic value and simulate endpoint.

Prerequisites

  • An active customer with the crypto-wallet feature.
  • A source wallet holding the asset you’re converting from, and a destination wallet for the asset and chain you want. Custodial sources convert without any extra step. A non-custodial source on an EVM chain collects your signers’ approvals first — the conversion stays pending, a transaction.awaiting_signature webhook delivers the signing link, and the transfer proceeds once the signing quorum approves (the same flow as a non-custodial payout). The signing link applies to a wallet in the passkey signing mode; a wallet in a programmatic signing mode instead carries a signingRequestId on that webhook — see Machine-signer stamping. Non-custodial sources on non-EVM chains (Tron, Solana, Stellar, Bitcoin) aren’t supported yet and return NON_CUSTODIAL_SOURCE_NOT_SUPPORTED.
  • Your API key.

Step 1 — Check recipient requirements (optional)

/v2/orders/requirements tells you what recipient details a route needs. A wallet destination needs none — it returns destinationRecipient: "none":
This validates the asset codes only; it does not check the specific chains. The authoritative route check is previewing the order (Step 2) — a route that isn’t enabled for your organization returns 422 UNSUPPORTED_PAIR there.

Step 2 — Preview the order

Create with autoExecute: false to lock a rate and read the quote without committing. destination is always a wallet with its own asset (code + chain). source is optional: name a wallet to convert a balance the customer already holds, or omit it and send sourceAsset to have Conduit issue a funding address instead (Step 2b below). Naming the source:
The response carries type: "conversion", status: "pending", a lockExpiresAt, and the quote in rate (referenceRate, totalSpreadBps, endUserRate — all strings; endUserRate already includes the spread). Read destinationAsset.amount to see what the customer receives.
There is no separate quote endpoint — previewing with autoExecute: false is the quote. lockSide: "source" fixes what’s spent and shows what’s received; lockSide: "destination" fixes what’s received. Cancel a pending order any time with POST /v2/orders/{orderId}/cancel.

Step 2b — Convert crypto that hasn’t arrived yet

When the customer doesn’t hold the source asset yet, omit source and send sourceAsset instead. Conduit returns an address to send the funds to, and the order executes on its own once they clear:
The response carries depositInstructions[0].address — where to send the funds — and no source at all. Send the order’s totalDebit to that address before lockExpiresAt (a funding deadline here, not a rate expiry). Omit autoExecute: these orders always auto-execute, and sending the field is a 400. Skip Step 3 (there is nothing to execute) and go straight to Step 4. Read Deposit-Funded Orders before you build on this — funds that no order claims are sent back to the sender, and Conduit may change the address at any time, so never cache it per customer.

Step 3 — Execute

To lock and execute in one call, pass autoExecute: true on create instead.

Step 4 — Settle

The order settles once the on-chain transfers confirm. Poll GET /v2/orders/{orderId} until status is succeeded (terminal), or watch the webhooks below. In the sandbox, a custodial-source conversion settles automatically after execute; a non-custodial source settles once its signing quorum approves (via the sandbox orders/:id/simulate/cosign lever or the real signing page), then confirms automatically. Order status is lowercase pending | succeeded | failed | cancelled.

Webhooks

The conversion surfaces on both the order and the transaction topics; the transaction events carry data.type: "conversion".

Errors

See also