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

# Create an exchange connection

> Create a Swapped Connect flow for funding from a centralized exchange balance.

Start an exchange order for a registered account, then show Swapped's hosted Connect flow in your app. Headless means you do not use the Rhinestone widget; users still complete the provider flow in Swapped's UI.

## How it works

1. Your backend starts an order with `POST /onramp/orders` and `kind: "exchange"`.
2. Your app shows the hosted flow in an iframe or in-app browser.
3. The user chooses an exchange and completes the transfer in Swapped's hosted flow.
4. The order advances through its [stages](/deposits/headless/processing-and-tracking/track-onramp-and-exchange-orders#order-stages), pushed as [`ramp-order`](/deposits/headless/processing-and-tracking/webhooks#ramp-order) webhooks.
5. The funded crypto lands on the account and enters the standard deposit pipeline.

## Prerequisites

* The account is [registered](/deposits/headless/setup/account-registration). Starting an order for an account that is not registered to your project returns `403 Unauthorized`.
* A [webhook URL](/deposits/headless/processing-and-tracking/webhooks#configure-webhooks) is configured if you want order updates pushed rather than polled.
* Calls are made from your backend. The endpoints require your API key, which never ships in client-side code.

```ts theme={null}
const DEPOSIT_SERVICE_URL =
  "https://v1.orchestrator.rhinestone.dev/deposit-processor";
const API_KEY = "YOUR_RHINESTONE_API_KEY";
```

## Start an order

```ts theme={null}
const response = await fetch(`${DEPOSIT_SERVICE_URL}/onramp/orders`, {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": API_KEY },
    body: JSON.stringify({
        provider: "swapped",
        kind: "exchange",
        account: "0xUSER_ACCOUNT_ADDRESS",
        connection: "binance",
    }),
});

const { orderId, nextAction, expiresAt } = await response.json();
```

`provider`, `kind`, and `account` are required. There's no amount or payment-method prefill; the user chooses those inside the exchange flow. The optional fields:

| Field | Type | Description |
| - | - | - |
| `connection` | `string` | Preselects the exchange or wallet (e.g. `"binance"`) |
| `connections` | `string[]` | Your allowlist. Requires `connection`, which must be one of them; an unknown slug returns `400` |
| `baseCountry` | `string` | User's country (ISO 3166-1 alpha-2) |
| `baseCurrencyCode` | `string` | Fiat currency code |
| `locale` | `string` | Flow language |
| `merchantName` | `string` | Name shown on Swapped's confirm screen |
| `merchantLogoUrl` | `string` | HTTPS logo shown beside `merchantName` |

`connections` is not forwarded to Swapped, whose picker is configured per partner. Only `connection` narrows what a session shows, which is why declaring an allowlist without one is refused.

The response matches the [onramp checkout response](/deposits/headless/funding/create-an-onramp-checkout#start-an-order): `202` with `orderId`, `nextAction`, and `expiresAt`. Full schema: [`POST /onramp/orders`](/api-reference/deposit-service/onramp/start-an-onramp-order).

## List exchanges

List the connections to offer:

```ts theme={null}
const response = await fetch(
    `${DEPOSIT_SERVICE_URL}/onramp/options?provider=swapped&kind=exchange`,
    { headers: { "x-api-key": API_KEY } },
);
const { connections } = await response.json();
// [{ connection: "binance", name: "Binance", logoUrl: null, kind: "exchange", appHandoff: "none" }, …]
```

By default this lists the centralized exchanges. Pass `connectionKind=wallet` or `connectionKind=all` for Web3 wallets, or `connections=binance,coinbase` to resolve your own allowlist. Inside an embedded browser, offer no `kind: "wallet"` entry: that rail hands off to a wallet app over a URL scheme the container can't open. Full schema: [`GET /onramp/options`](/api-reference/deposit-service/onramp/list-onramp-options).

## Show the hosted connection flow

Embed `nextAction.url` in an iframe with the permissions needed by the provider:

```html theme={null}
<iframe src={nextAction.url} allow="payment; camera; microphone; clipboard-write" />
```

In a mobile app, open the URL in an in-app browser (`SFSafariViewController` on iOS, Chrome Custom Tabs on Android) rather than a plain WebView. Camera access and provider authentication can be restricted in bare WebViews.

The URL has no redirect or callback parameter. Detect completion through the [`ramp-order`](/deposits/headless/processing-and-tracking/webhooks#ramp-order) webhook or by [polling the order](/deposits/headless/processing-and-tracking/track-onramp-and-exchange-orders#get-an-order), then dismiss the provider UI from your own interface.

<Warning>
  A user who abandons the connection flow produces no terminal event. Apply your own timeout when waiting on an order.
</Warning>

## Migrate from the deprecated routes

`POST /onramp/swapped/connect-url` and `GET /onramp/swapped/connect-exchanges` are deprecated. They keep working unchanged until their retirement is announced. Move to `POST /onramp/orders` with `kind: "exchange"` (`smartAccount` becomes `account`) and `GET /onramp/options?kind=exchange` (the `kind` filter becomes `connectionKind`, and the list is `connections` instead of `exchanges`). The mapping of the response is the same as for [checkouts](/deposits/headless/funding/create-an-onramp-checkout#migrate-from-the-deprecated-route).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.