# Conversions

## Overview

Ryft allows you to convert funds between currencies using foreign exchange (FX). For more background, see [Conversions](/documentation/overview/core_concepts/conversions) in Core Concepts.

## Checking the Estimated Rate

Before creating a conversion, you can use the [conversionsGetRate](/documentation/api/reference/openapi#operation/conversionsGetRate) endpoint to retrieve an indicative gross exchange rate and buy amount, along with the fees that will be charged.

The rate returned is **indicative only** and is not held — the rate applied when you create a conversion may differ. Request a rate only when needed rather than polling; this endpoint is rate-limited and will return a `429 Too Many Requests` response if exceeded.

Rates are briefly unavailable during a short daily window (~90 seconds) while FX trading closes. During this window the endpoint returns a `400 Bad Request` response with a message indicating that rates are temporarily unavailable — retry shortly. Creating a conversion is unaffected, as pricing is applied asynchronously.

## Creating a Conversion

Use the [conversionCreate](/documentation/api/reference/openapi#operation/conversionCreate) endpoint to create a conversion. Specify the sell currency and amount and the buy currency.

The `sell.amount` must be within the minimum and maximum conversion limits for the sell currency; requests outside the range are rejected with a `400`. See [conversion amount limits](/documentation/overview/core_concepts/currencies#minimum-and-maximum-conversion-amounts).

Before creating a conversion, you must obtain agreement from the account holder to [Ryft's conversion terms](https://dash.ryftpay.com/about/conversion-terms). Set `termAgreement: true` in the request body to confirm this — the request is rejected if it is `false`.

Payload
```json conversionCreate - Payload Example
{
    "sell": {
        "currency": "EUR",
        "amount": 100000
    },
    "buy": {
        "currency": "GBP"
    },
    "reason": "Monthly EUR settlement",
    "termAgreement": true
}
```

Headers
```json conversionCreate - Headers Example
{
    "Authorization": "<secret_key>"
}
```

This creates a conversion on your own account. If creating a conversion for a specific Sub-Account, make sure to include the `Account` header in your API request with the Sub-Account ID as its value.

The API responds with the new conversion in `status: InProgress`. The `rate`, `buy.amount`, `sell.fees`/`buy.fees`, and `estimatedSettlementDate` are not present — pricing is applied asynchronously.

```json conversionCreate - Response Example
{
    "id": "cv_XXXXXXXX",
    "sell": {"amount": 100000, "currency": "EUR"},
    "buy": {"currency": "GBP"},
    "status": "InProgress",
    "reason": "Monthly EUR settlement",
    "createdTimestamp": 1750000000
}
```

Next, subscribe to the `Conversion.updated` webhook event to track the conversion through the rest of its lifecycle: the first event carries the pricing (`rate`, `buy.amount`, `sell.fees`/`buy.fees` and `estimatedSettlementDate`), and a later one moves `status` to `Settled` once the bought funds are credited to the account's balance. See [Conversions Webhooks](/documentation/get_started/webhooks/conversions) for the full payload examples.

## Settlement times

Conversions are booked for **same-day settlement if created before the cut-off time** for the currencies involved, provided the relevant FX markets are open. Otherwise, they settle on the next available business day. Cut-off times vary by currency, and FX markets are closed on weekends and public holidays. See [Settlement times](/documentation/overview/core_concepts/conversions#settlement-times) for specific currency cut-offs.

Because the deadline depends on both currencies and the calendar, don't assume a fixed offset. Instead, use the `estimatedSettlementDate` (ISO date) returned by Ryft:

- **[conversionsGetRate](/documentation/api/reference/openapi#operation/conversionsGetRate)** — returned alongside the indicative rate, so you can show an expected settlement date before creating a conversion.
- **The conversion object** — populated once pricing has been applied, from the first `Conversion.updated` event onward. It is **not** present on the create response or the `Conversion.created` event.


`estimatedSettlementDate` is an estimate and may not exactly match the final settlement date. A conversion is settled once a `Conversion.updated` event with `status: Settled` is emitted and `settledTimestamp` is set — at that point the bought funds are credited to the account's balance.

## Payouts

Upon settlement, the converted funds will reflect in the account's available balance.

- **Automatic payouts**: Ryft will trigger a payout within 24 hours of the conversion settling. No action is required.
- **Manual payouts**: For NonHosted accounts we recommend triggering a payout after receiving the `Conversion.updated` event where `status` is `Settled`.


See [Payouts](/documentation/overview/core_concepts/payouts) for more on payout configuration.

## Fees and FX margin

Ryft applies an FX fee to every conversion. The `rate` and all amounts are **gross** values (before fees).

The fee is charged on **exactly one side** of the conversion, so `sell.fees` and `buy.fees` are mutually exclusive — read whichever one is present. Which side it falls on determines the net amount credited to the account balance:

- **Buy side** (`buy.fees`) — the whole `sell.amount` is converted at `rate` to give `buy.amount`, and the fee is deducted from it afterwards: **Net Credit = `buy.amount` − Fee**.
- **Sell side** (`sell.fees`) — the fee is deducted from `sell.amount` **before** the conversion, so only the remainder is converted: **Net Credit = `buy.amount`** in full.


Fee amounts are in minor units of the currency of the side they are charged on.

Once calculated, the fee breakdown on that side contains either `ryft` or `platform`, depending on the account type:

- **[Main Accounts](/documentation/overview/core_concepts/ryft_entities)** — `ryft` shows Ryft's fee, which is deducted in full.
- **[Sub-Accounts](/documentation/overview/core_concepts/ryft_entities#sub-accounts)** — `platform` shows the total gross platform fee deducted. Ryft's fee (`platform.ryftFee`) is taken out of this amount — it is not an additional deduction. The platform retains the difference (`platform.amount` − `platform.ryftFee.amount`).


```json fees — Main Account
{
    "ryft": {"amount": 700}
}
```

*Total fee deducted: 700 (all retained by Ryft).*

```json fees — Sub-Account
{
    "platform": {
        "amount": 1000,
        "ryftFee": {"amount": 700}
    }
}
```

*Total fee deducted: 1000 (700 goes to Ryft, 300 retained by the platform).*

The platform fee is a single rate across all Sub-Accounts, including conversions Sub-Accounts create directly via the Ryft Portal. [Contact us](/documentation/overview/support) to configure it.

For Sub-Account conversions, the platform fee is settled against the **Main Account's** balance via a `ConversionPlatformFee` [balance transaction](/documentation/api/reference/openapi#operation/balanceTransactionsList).

- **With a configured platform fee** — the transaction's `net` value represents the platform's retained earnings (`platform.amount` − `platform.ryftFee.amount`).
- **Without a platform fee** — a `ConversionPlatformFee` transaction is still created to cover Ryft's FX fee, resulting in a negative `net` value (a debit) on the Main Account.


### Calculating the net amount and effective rate

Take the single populated fee — `ryft.amount` for a Main Account, or `platform.amount` for a Sub-Account — from whichever side carries `fees`, then apply the net credit above. The effective rate you received is the net amount credited ÷ `sell.amount`.

For a buy-side fee (`sell.amount` 100000, `buy.amount` 86000, Ryft fee 700):

```text
net amount     = 86000 − 700     = 85300
effective rate = 85300 ÷ 100000  = 0.853
```

For a sell-side fee (`sell.amount` 100000, Ryft fee 700, so 99300 converted at 0.86 to give `buy.amount` 85398):

```text
net amount     = 85398
effective rate = 85398 ÷ 100000  = 0.85398
```

Alternatively, once the conversion settles the credited funds appear as a [balance transaction](/documentation/api/reference/openapi#operation/balanceTransactionsList) of type `ConversionBuy`, whose `net` field is the net amount credited — so you can read it directly instead of computing it. The sold funds are debited from the sell-currency balance as a matching `ConversionSell` transaction.

## Example

The following walks through the full lifecycle of a conversion. In this scenario, a merchant collects payments in EUR but only holds a GBP bank account. They convert €1,000.00 of their EUR balance to GBP so it can be paid out.

### 1. Create the conversion

```json POST /v1/conversions — Request
{
    "sell": {"currency": "EUR", "amount": 100000},
    "buy": {"currency": "GBP"},
    "reason": "Monthly EUR settlement",
    "termAgreement": true
}
```

The response contains the new conversion. The rate and buy amount are not yet known:

```json POST /v1/conversions — Response
{
    "id": "cv_XXXXXXXX",
    "sell": {"amount": 100000, "currency": "EUR"},
    "buy": {"currency": "GBP"},
    "status": "InProgress",
    "reason": "Monthly EUR settlement",
    "createdTimestamp": 1750000000
}
```

### 2. Conversion.created

A `Conversion.created` webhook is dispatched immediately, carrying the same unpriced object.

### 3. Conversion.updated — pricing applied

Once the rate is determined, a `Conversion.updated` event is emitted. The `rate`, `buy.amount` (gross, before fees), `fees`, and `estimatedSettlementDate` are now populated. See [Calculating the net amount and effective rate](#calculating-the-net-amount-and-effective-rate) to derive what is credited to the balance:

```json Conversion object — pricing applied
{
    "id": "cv_XXXXXXXX",
    "sell": {"amount": 100000, "currency": "EUR"},
    "buy": {
        "amount": 86000,
        "currency": "GBP",
        "fees": {
            "ryft": {"amount": 700}
        }
    },
    "rate": 0.86,
    "status": "InProgress",
    "reason": "Monthly EUR settlement",
    "estimatedSettlementDate": "2026-06-20",
    "createdTimestamp": 1750000000
}
```

Where the fee is charged on the sell side instead, `fees` sits on `sell` and `buy` carries none — the fee came off the 100000 EUR before conversion, so 99300 was converted at `rate` and the full `buy.amount` is credited:

```json Conversion object — sell-side fee
{
    "id": "cv_XXXXXXXX",
    "sell": {
        "amount": 100000,
        "currency": "EUR",
        "fees": {
            "ryft": {"amount": 700}
        }
    },
    "buy": {"amount": 85398, "currency": "GBP"},
    "rate": 0.86,
    "status": "InProgress",
    "reason": "Monthly EUR settlement",
    "estimatedSettlementDate": "2026-06-20",
    "createdTimestamp": 1750000000
}
```

### 4. Conversion.updated — settled

When the conversion settles, a further `Conversion.updated` is emitted. The `status` is now `Settled`, `settledTimestamp` is set, and the GBP funds are available in the account's balance:

```json Conversion object — settled
{
    "id": "cv_XXXXXXXX",
    "sell": {"amount": 100000, "currency": "EUR"},
    "buy": {
        "amount": 86000,
        "currency": "GBP",
        "fees": {
            "ryft": {"amount": 700}
        }
    },
    "rate": 0.86,
    "status": "Settled",
    "reason": "Monthly EUR settlement",
    "estimatedSettlementDate": "2026-06-20",
    "settledTimestamp": 1750086400,
    "createdTimestamp": 1750000000
}
```

### 5. Payout

The GBP funds are now available in the account's balance. If the account is on **automatic payouts**, Ryft will trigger a payout within 24 hours — no action needed. If on **manual payouts**, trigger one after receiving the settled event above.

## Next Steps

- [API reference](/documentation/api/reference/openapi#tag/Conversions) — full endpoint documentation
- [Conversions Webhooks](/documentation/get_started/webhooks/conversions) — event payload reference
- [Payouts](/documentation/overview/core_concepts/payouts) — payout configuration