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

# Multi-currency wallets

> Opt in through the API, estimate USD funding, convert within sub-accounts, and withdraw from the selected currency balance.

Multi-currency is **off by default**. Existing integrations keep their USD transfer
and payout flows. A brand can opt in for its managed sub-accounts through its
server-side Connect API key. Live and sandbox have separate preferences.

<Warning>
  This feature is in a gated pilot. Saving an opt-in does not override platform
  availability, account restrictions, or payout permissions. Read the returned
  `enabled` value before offering new conversions. No conversion quote guarantees
  an exact future exchange rate or destination amount.
</Warning>

## 1. Read or change the brand preference

Use the base URL `https://app.sideshift.app/api/embed`. Keep the key on your server;
do not put it in the widget, browser, or mobile app.

```bash theme={"system"}
BASE_URL="https://app.sideshift.app/api/embed"
curl "$BASE_URL/settings/currencies" \
  -H "x-api-key: $SIDESHIFT_CONNECT_KEY"
```

For the examples below, set `BASE_URL` to the base URL above. A new integration
returns this shape; `rolloutAvailable` depends on platform availability:

```json theme={"system"}
{
  "success": true,
  "data": {
    "environment": "live",
    "multiCurrencyEnabled": false,
    "rolloutAvailable": false,
    "enabled": false
  }
}
```

Enable it for the environment selected by your key:

```bash theme={"system"}
curl -X PATCH "$BASE_URL/settings/currencies" \
  -H "x-api-key: $SIDESHIFT_CONNECT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'
```

The response has the same shape as GET. When the preference, platform rollout,
and existing payout permission are all enabled, both `multiCurrencyEnabled` and
`enabled` are `true`. If `multiCurrencyEnabled` is `true` but `enabled` is `false`,
the preference was saved but new conversions are not available yet.

| Field                  | Meaning                                                                                                 |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| `environment`          | `live` for `sk_live_*`, `sandbox` for `sk_test_*`.                                                      |
| `multiCurrencyEnabled` | The brand's saved preference; missing settings mean `false`.                                            |
| `rolloutAvailable`     | Whether SideShift has made the feature available to this integration.                                   |
| `enabled`              | Whether the brand can request new estimates, quotes, and conversions, subject to each account's checks. |

PATCH accepts only a JSON boolean `enabled`. Do not send a company ID, account ID,
environment, rollout field, or the string `"true"`. The API key determines the
brand and environment. Repeating the same PATCH is safe. This setting applies to
all managed sub-accounts in that environment, not to other brands or linked
personal accounts. It does not change USD transfers or other Connect settings.

## 2. Choose an amount and its units

The wallet catalog covers 84 fiat currencies. Crypto, stablecoin, and gold-token
wallets are outside this feature. A listed currency does not guarantee that Whop
can convert a particular pair or pay a particular bank account. The provider's
quote determines eligibility for the account, amount, method, and country.

Amounts are integers in the currency's **minor units**. The API retains the
`*Cents` field names for USD compatibility; those names do not mean every
currency has two decimals.

| Currency | `decimals` | Desired amount | Integer amount |
| -------- | ---------- | -------------- | -------------- |
| USD      | 2          | \$28.64        | `2864`         |
| EUR      | 2          | €25.00         | `2500`         |
| JPY      | 0          | ¥2,500         | `2500`         |
| KWD      | 3          | KWD 25.123     | `25123`        |

For `quote` and `convert`, `amountCents` uses the **source** currency. For
`estimate`, `destinationAmountCents` uses the **target** currency, and the
returned `amountCents` always uses USD cents. Supported inputs range from 1 to
20,000,000 minor units. Account limits also apply to executed conversions using
a USD valuation. Do not add balances in different currencies together.

## 3. Estimate the USD needed before funding

This example asks how much USD is needed to convert into about €25 inside a
managed sub-account. Replace the account ID with its **SideShift ID**, not a
Whop `biz_*` ID.

```bash theme={"system"}
curl -X POST "$BASE_URL/accounts/currencies" \
  -H "x-api-key: $SIDESHIFT_CONNECT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action":"estimate",
    "accountId":"YOUR_SUB_ACCOUNT_ID",
    "destinationCurrency":"eur",
    "destinationAmountCents":2500
  }'
```

Illustrative response, not a current rate:

```json theme={"system"}
{
  "success": true,
  "data": {
    "currency": "usd",
    "destinationCurrency": "eur",
    "amountCents": 2864,
    "destinationAmountCents": 2500,
    "estimatedReceivedCents": 2501,
    "feeBps": 0,
    "rate": "0.8735",
    "estimatedAt": "2026-09-23T12:00:00.000Z",
    "rateLocked": false,
    "fundingFeesIncluded": false
  }
}
```

The estimate works with an empty sub-account. It does not move or reserve money.
It rounds the USD amount up and checks it with a forward conversion quote.
`amountCents` is the **total USD needed for this conversion**, not a shortfall
calculated after subtracting existing USD or foreign-currency balances.
The quoted conversion cost is included in the net output. Funding and withdrawal
fees are separate. Rates can change before funding arrives or conversion executes.

## 4. Fund the sub-account in USD

Use the existing transfer endpoint. The amount below is only the illustrative
estimate above; use the amount returned by your request.

```bash theme={"system"}
curl -X POST "$BASE_URL/accounts/transfer" \
  -H "x-api-key: $SIDESHIFT_CONNECT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "toAccountId":"YOUR_SUB_ACCOUNT_ID",
    "destinationBalance":"withdrawal",
    "amountCents":2864,
    "idempotencyKey":"order-123-usd-funding",
    "metadata":{
      "obligationType":"creator_agreement",
      "obligationReference":"agreement-123",
      "description":"Approved payment for completed creator work",
      "approvalReference":"approval-456"
    }
  }'
```

Use real commercial references. Existing authorization, funding fees, transfer
limits, records, and completion events apply. Wait for transfer completion, then
read the available provider balance. Pending funds and internal wallet-only
credits cannot be converted. A successful estimate does not authorize a transfer.

## 5. Read balances and preview conversion

```bash theme={"system"}
curl "$BASE_URL/accounts/currencies?accountId=YOUR_SUB_ACCOUNT_ID" \
  -H "x-api-key: $SIDESHIFT_CONNECT_KEY"
```

The response is `{ "success": true, "data": { ... } }`. Data fields:

| Field        | Meaning                                                                                          |
| ------------ | ------------------------------------------------------------------------------------------------ |
| `accountId`  | Managed SideShift sub-account ID.                                                                |
| `enabled`    | New conversions are enabled for this account.                                                    |
| `visible`    | Show currency controls; false keeps the existing USD wallet.                                     |
| `balances`   | Separate rows with `currency`, `decimals`, `availableCents`, `pendingCents`, and `reserveCents`. |
| `activities` | Recent provider activity, or `null` if history is unavailable.                                   |

An enabled wallet receives the full currency catalog, including zero balances.
A brand that has never opted in receives `visible: false` and no currency rows;
the existing USD wallet continues to supply its balance. After opt-out, funded
foreign balances remain accessible. Missing provider data is not replaced with
invented zero balances.

Refresh the quote after funding arrives:

```json theme={"system"}
{
  "action": "quote",
  "accountId": "YOUR_SUB_ACCOUNT_ID",
  "currency": "usd",
  "destinationCurrency": "eur",
  "amountCents": 2864
}
```

POST this to `/accounts/currencies`. The quote returns `amountCents`, both currency
codes, `estimatedReceivedCents`, `feeBps`, and `rate`. Show the estimate to the user.
It is not a locked quote and does not spend money.

## 6. Convert after explicit confirmation

POST to the same endpoint:

```json theme={"system"}
{
  "action": "convert",
  "accountId": "YOUR_SUB_ACCOUNT_ID",
  "currency": "usd",
  "destinationCurrency": "eur",
  "amountCents": 2864,
  "acceptExecutionRate": true,
  "idempotencyKey": "order-123-conversion"
}
```

Use a different key from the funding transfer. The conversion executes within the
sub-account. It does not transfer money to another account. Creators can also
convert through their managed payout widget. Reverse conversion uses the same
API with the source and destination currencies exchanged.

| Result status   | Client action                                                                                   |
| --------------- | ----------------------------------------------------------------------------------------------- |
| `completed`     | Show the actual `receivedCents` when present; refresh balances.                                 |
| `pending`       | Keep the request body and key. Repeat the exact POST to check or resume it. HTTP status is 202. |
| `failed`        | Show the error and check account history before starting another request.                       |
| `manual_review` | Stop automatic retries and contact SideShift with the `operationId`.                            |

HTTP200 alone is not proof of completion. A timeout is not proof of failure.
Never make a new conversion key to work around an unknown outcome. The server
retains permanent request identities and stops provider retries after 23 hours.
Changing an amount, account, currency, or other field under the same key is a
conflict. A pending conversion blocks another conversion on that source account.
Funding and conversion are separate: a declined conversion does not reverse a
completed USD transfer.

## 7. Withdraw from the chosen currency

Use the [managed payout widget](/connect/widgets). When multi-currency is enabled,
it lets the creator select a source balance, choose a saved payout method, and
review fees and delivery before confirming. The widget's existing user token is
scoped to that creator; it cannot change the brand's multi-currency preference.

* **Same currency:** a EUR balance paid to an eligible EUR destination stays in
  EUR. There is no FX leg, but a withdrawal fee can still apply.
* **Different currency:** the provider quote supplies an exchange rate and an
  expected destination amount. The UI shows both.
* **Unavailable method or quote:** do not submit a payout. Let the creator choose
  another supported method or retry the quote.

The server selects the source balance explicitly, validates the signed payout
quote's money fields, and passes its token to Whop. The confirmed destination
amount is retained on retry; a new quote below that amount is refused. The
internal widget withdrawal route uses a user bearer session, not the brand's
Connect API key. Do not call it as a new server-to-server Connect payout API.

KYC, account restrictions, country eligibility, bank support, payout limits, and
fees still apply. A currency in the catalog is not a promise that any bank can
receive it. Payouts and their existing withdrawal events remain asynchronous.

## Disable multi-currency

```bash theme={"system"}
curl -X PATCH "$BASE_URL/settings/currencies" \
  -H "x-api-key: $SIDESHIFT_CONNECT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":false}'
```

New estimates, quotes, and conversions stop. Existing foreign balances remain
visible and withdrawable; opt-out does not auto-convert or forfeit funds. Pending
conversions continue through their original request key and reconciliation.
The other environment is unchanged. To convert remaining funds, opt back in.

## Errors and sandbox testing

Errors use `{ "success": false, "error": { "code": "...", "message": "..." } }`.
Keep the original operation key when the outcome may be unknown.

| HTTP status | What to check                                                                                                 |
| ----------- | ------------------------------------------------------------------------------------------------------------- |
| 400         | Strict request fields, boolean opt-in, supported code, integer units, range, and rate acceptance.             |
| 401         | API key or session authentication.                                                                            |
| 403         | Brand preference, rollout availability, payout permission, or account restrictions.                           |
| 404         | Managed account ID and ownership.                                                                             |
| 409         | Payment-account setup, available balance, another pending operation, or a changed request under the same key. |
| 5xx         | Provider or service failure. Do not assume an attempted conversion failed.                                    |

Use a sandbox key to save the sandbox preference; it never enables live currency
operations. Conversions require actual provider sandbox accounts and available
sandbox funds. Legacy simulated USD transfers do not create those provider
balances. Sandbox currency withdrawals are currently unavailable.

The pilot remains disabled until live settlement and recovery checks are complete.
Public quote success and automated tests do not prove that a bank payout settled.
