> ## Documentation Index
> Fetch the complete documentation index at: https://v2.docs.conduit.financial/llms.txt
> Use this file to discover all available pages before exploring further.

# Convert crypto

> Convert one crypto asset to another between a customer's wallets — same chain, a different chain, a different asset, or both — with a single orders call

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](/guides/transact).

<Note>
  **Test this flow in sandbox.** Drive it end-to-end with simulated money and
  deterministic controls — start with the [sandbox
  quickstart](/sandbox/quickstart), then [conversion
  simulation](/sandbox/conversions) for this flow, and the [cheat
  sheet](/sandbox/cheat-sheet) for every magic value and simulate endpoint.
</Note>

## 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](/guides/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"`:

```bash theme={null}
curl 'https://api.conduit.financial/v2/orders/requirements?sourceCode=USDC&destinationCode=USDT' \
  -H "x-api-key: YOUR_API_KEY"
```

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:

```bash theme={null}
curl https://api.conduit.financial/v2/orders \
  -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{
    "source":      { "type": "wallet", "id": "wlt_<source-wallet>",      "asset": { "code": "USDC", "chain": "ethereum" } },
    "destination": { "type": "wallet", "id": "wlt_<destination-wallet>", "asset": { "code": "USDT", "chain": "tron" } },
    "lockSide": "source",
    "amount": "100",
    "autoExecute": false
  }'
```

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.

<Note>
  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`.
</Note>

## 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:

```bash theme={null}
curl https://api.conduit.financial/v2/orders \
  -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{
    "sourceAsset":  { "code": "USDC", "chain": "ethereum" },
    "destination": { "type": "wallet", "id": "wlt_<destination-wallet>", "asset": { "code": "USDT", "chain": "tron" } },
    "lockSide": "source",
    "amount": "100"
  }'
```

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](/concepts/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

```bash theme={null}
curl https://api.conduit.financial/v2/orders/{orderId}/execute \
  -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: $(uuidgen)"
```

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"`.

| Event                                 | Meaning                                        |
| ------------------------------------- | ---------------------------------------------- |
| `order.created`                       | The conversion order was created.              |
| `transaction.created`                 | The conversion started executing.              |
| `transaction.completed`               | The destination asset was credited. Terminal.  |
| `order.succeeded`                     | The order completed. Terminal.                 |
| `order.failed` / `transaction.failed` | The conversion failed; the source is released. |

## Errors

| Code                                 | Cause                                                                                                                                                  |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `UNSUPPORTED_PAIR`                   | That conversion route isn't enabled for your organization.                                                                                             |
| `INVALID_ORDER_COMBO`                | Source and destination are the **same** asset and chain (nothing to convert — use a payout), or `autoPayout` was set on a wallet-to-wallet conversion. |
| `NON_CUSTODIAL_SOURCE_NOT_SUPPORTED` | The source wallet is non-custodial on a non-EVM chain, where signer approvals aren't supported yet.                                                    |
| `NO_PRICING_CONFIGURED`              | The route is recognized but has no price configured — contact Conduit.                                                                                 |
| `UNSUPPORTED_ASSET`                  | The `sourceAsset` you asked to be funded in cannot fund an order this way. Name a `source` explicitly instead.                                         |

## See also

* [Deposit-Funded Orders](/concepts/deposit-funded-orders) — converting without naming a source.
* [ONRAMP orders](/sandbox/onramps) — convert fiat into crypto.
* [OFFRAMP orders](/sandbox/offramps) — convert crypto into fiat.
* [Transact](/guides/transact) — the requirement gates each flow clears.
