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

# Transfers

> Move value from crypto wallets or virtual accounts to external addresses and bank accounts.

## Create a transfer

Every transfer requires a `walletId` — the wallet you're sending funds from. This can be a crypto wallet or a virtual account's wallet.

### From a crypto wallet

```bash theme={null}
curl -X POST https://api.ledger.so/v1/transfers \
  -H "Authorization: Bearer $LEDGER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: transfer-1" \
  -d '{
    "customerId": "cus_...",
    "walletId": "wal_...",
    "amount": "10.00",
    "destinationCurrency": "usdc",
    "destinationPaymentRail": "solana",
    "destinationAddress": "7nwiVgP8..."
  }'
```

Response:

```json theme={null}
{
  "ok": true,
  "data": {
    "id": "txn_...",
    "customerId": "cus_...",
    "type": "transfer",
    "status": "in_review",
    "amount": "10.00",
    "fee": "0.03",
    "netAmount": "9.97",
    "sourceCurrency": "usdc",
    "destinationCurrency": "usdc",
    "destinationAddress": "7nwiVgP8...",
    "finalAmount": null,
    "destinationTxHash": null,
    "gasFee": null,
    "exchangeFee": null,
    "createdAt": 1710000000000,
    "updatedAt": 1710000000000
  }
}
```

### From a virtual account wallet

When you create a virtual account, the response includes a `walletId`. Fiat deposited into the virtual account converts to stablecoins in that wallet. Use that `walletId` to transfer funds out.

```bash theme={null}
curl -X POST https://api.ledger.so/v1/transfers \
  -H "Authorization: Bearer $LEDGER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: transfer-2" \
  -d '{
    "customerId": "cus_...",
    "walletId": "wal_...",
    "amount": "5.00",
    "destinationCurrency": "gbp",
    "destinationPaymentRail": "faster_payments",
    "destinationAddress": "pa_..."
  }'
```

Response:

```json theme={null}
{
  "ok": true,
  "data": {
    "id": "txn_...",
    "customerId": "cus_...",
    "type": "transfer",
    "status": "funds_received",
    "amount": "5.00",
    "fee": "0.15",
    "netAmount": "4.85",
    "sourceCurrency": "usdc",
    "destinationCurrency": "gbp",
    "payoutAccountId": "pa_...",
    "finalAmount": null,
    "exchangeFee": null,
    "createdAt": 1710000000000,
    "updatedAt": 1710000000000
  }
}
```

Virtual account wallets support both crypto and fiat destinations.

## Request fields

| Field                    | Type             | Required | Description                                                          |
| ------------------------ | ---------------- | -------- | -------------------------------------------------------------------- |
| `customerId`             | string           | Yes      | Ledger customer ID (`cus_...`)                                       |
| `walletId`               | string           | Yes      | Wallet to transfer from (`wal_...`)                                  |
| `amount`                 | string or number | Yes      | Amount to send                                                       |
| `destinationCurrency`    | string           | Yes      | Currency to deliver (e.g. `usdc`, `gbp`, `eur`, `usd`)               |
| `destinationPaymentRail` | string           | Yes      | Delivery rail (e.g. `solana`, `ach`, `faster_payments`, `sepa`)      |
| `destinationAddress`     | string           | Yes      | Onchain address or payout account ID (`pa_...`)                      |
| `reference`              | string           | No       | Payment reference included with the fiat transfer (see limits below) |
| `clientReferenceId`      | string           | No       | Your own reference ID (not sent to the recipient)                    |

### Reference limits by rail

The `reference` field is sent to the recipient's bank. Character limits vary by rail:

| Rail              | Max Length | Allowed Characters    |
| ----------------- | ---------- | --------------------- |
| `ach`             | 10         | A-Z, a-z, 0-9, spaces |
| `wire`            | 140        | —                     |
| `sepa`            | 140        | —                     |
| `faster_payments` | —          | —                     |
| `spei`            | —          | —                     |
| `pix`             | —          | —                     |

## Response fields

Every transfer response includes:

| Field       | Description                          |
| ----------- | ------------------------------------ |
| `amount`    | Original amount requested            |
| `fee`       | Ledger fee deducted                  |
| `netAmount` | Amount sent to destination after fee |

Additional fields vary by transfer type:

| Field                | Crypto | Fiat | Description                                  |
| -------------------- | ------ | ---- | -------------------------------------------- |
| `destinationAddress` | Yes    | No   | Onchain recipient address                    |
| `payoutAccountId`    | No     | Yes  | Payout account receiving funds               |
| `destinationTxHash`  | Yes    | No   | Onchain settlement transaction hash          |
| `gasFee`             | Yes    | No   | Network gas fee                              |
| `finalAmount`        | Yes    | Yes  | Final amount delivered after fees/conversion |
| `exchangeFee`        | Yes    | Yes  | Exchange or conversion fee                   |

## Fees

Ledger charges a flat fee on all transfers, deducted from the source amount before sending:

| Transfer type                                             | Fee  |
| --------------------------------------------------------- | ---- |
| FX transfers (e.g. USDC → GBP, USDC → EUR)                | 3%   |
| Stablecoin / same-currency (e.g. USDC → USD, USDC → USDC) | 0.3% |

## Onchain vs fiat transfers

| Destination Rail                                                        | Type             | KYC Required |
| ----------------------------------------------------------------------- | ---------------- | ------------ |
| `solana`, `ethereum`, `polygon`, `arbitrum`, `base`, `optimism`, `tron` | Onchain transfer | No           |
| `ach`, `wire`, `sepa`, `faster_payments`, `spei`, `pix`                 | Fiat transfer    | Yes          |

For fiat transfers, the customer must have completed KYC verification and have a payout account created first.

## Tracking transfers

After creating a transfer, track its progress via:

* `GET /v1/transactions/{id}` to poll the status
* Subscribe to `transaction.updated` webhooks for real-time updates

For crypto transfers, the `destinationTxHash` field populates once settled onchain.
