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

# Create Transfer

> Move funds between the trading account and one of the account's virtual accounts, or between two virtual accounts, in the same currency

A transfer is a same-currency book transfer inside one account. At least one leg must be a virtual account: trading account → virtual account, virtual account → trading account, or virtual account → virtual account. Converting between currencies is done with [FX](/fx/api-reference/fx/create-fx-quote), not with a transfer.

Transfers settle synchronously: the source is debited and the destination credited in the same request, and the returned transfer is already `completed`.

Requires an API key with `write` access on the `payments` scope.

## Path Parameters

<ParamField path="account_id" type="string" required>
  The account's external ID (starts with `acct_`)
</ParamField>

## Headers

<ParamField header="Idempotency-Key" type="string" required>
  Key for this transfer: any string up to 255 characters, a UUID recommended but not enforced. Scoped to the account: replaying it with the same payload returns the original transfer with `200`, and with a different payload `409` `IDEMPOTENCY_CONFLICT`. See [Idempotency](/fx/api-reference/idempotency).
</ParamField>

## Request Body

<ParamField body="source" type="object" required>
  Balance location to debit. Either `{"type": "trading_account"}` or `{"type": "virtual_account", "virtual_account_id": "va_…"}`. The virtual account must belong to this account, be `active`, and hold the transfer currency.
</ParamField>

<ParamField body="destination" type="object" required>
  Balance location to credit, same shape as `source`. Trading account → trading account is rejected, and the two legs cannot be the same virtual account.
</ParamField>

<ParamField body="amount" type="string" required>
  Transfer amount as a decimal string, greater than 0. The available balance of the source leg must cover it.
</ParamField>

<ParamField body="currency" type="string" required>
  Currency code, for example `USD`. Both legs settle in this currency; a trading-account leg uses the trading account's balance in this currency.
</ParamField>

<ParamField body="reference" type="string">
  Optional reference shown on the transfer (max 255 characters)
</ParamField>

<ParamField body="internal_note" type="string">
  Optional internal note, never shown to counterparties (max 500 characters)
</ParamField>

## Response

Returns `201` with the new transfer, or `200` with the existing transfer when the `Idempotency-Key` was already used with the same payload.

<ResponseField name="id" type="string">
  Transfer identifier (starts with `trf_`)
</ResponseField>

<ResponseField name="object" type="string">
  Always returns `"transfer"`
</ResponseField>

<ResponseField name="account_id" type="string">
  Account ID
</ResponseField>

<ResponseField name="source" type="object">
  Balance location debited: `{"type": "trading_account"}` or `{"type": "virtual_account", "virtual_account_id": "va_…"}`
</ResponseField>

<ResponseField name="destination" type="object">
  Balance location credited, same shape as `source`
</ResponseField>

<ResponseField name="amount" type="string">
  Transfer amount (decimal)
</ResponseField>

<ResponseField name="currency" type="string">
  Transfer currency code
</ResponseField>

<ResponseField name="status" type="string">
  Transfer status. Transfers settle synchronously, so a successful response is always `completed`; `failed` is reserved for transfers that could not be posted.
</ResponseField>

<ResponseField name="reference" type="string">
  Reference, or `null`
</ResponseField>

<ResponseField name="internal_note" type="string">
  Internal note, or `null`
</ResponseField>

<ResponseField name="created" type="string">
  ISO 8601 timestamp when created
</ResponseField>

<ResponseField name="updated" type="string">
  ISO 8601 timestamp when last updated
</ResponseField>

<ResponseField name="created_by" type="string">
  ID of the API key that created the transfer (starts with `ak_`)
</ResponseField>

<ResponseField name="updated_by" type="string">
  Actor that last updated the transfer, or `null`
</ResponseField>

## Errors

| Status | `code` | When |
| - | - | - |
| `404` | `RESOURCE_NOT_FOUND` | A `virtual_account_id` does not belong to this account |
| `409` | `IDEMPOTENCY_CONFLICT` | The `Idempotency-Key` was already used with a different payload; `details.transfer_id` is the existing transfer |
| `422` | `TRANSFER_TRADING_TO_TRADING` | Both legs are `trading_account` |
| `422` | `TRANSFER_SAME_LOCATION` | Both legs are the same virtual account |
| `422` | `VIRTUAL_ACCOUNT_INACTIVE` | A virtual account leg is not `active` |
| `422` | `CURRENCY_MISMATCH` | A virtual account leg is not denominated in `currency` |
| `422` | `INSUFFICIENT_FUNDS` | The source leg's available balance is below `amount`; `details.available` carries the current available balance |

## Request Example

```json theme={null}
{
  "source": {"type": "trading_account"},
  "destination": {"type": "virtual_account", "virtual_account_id": "va_7f3k2m9p4q1w8e5r3t6y0u2i"},
  "amount": "250.00",
  "currency": "USD",
  "reference": "Fund operating account"
}
```

## Response Example

```json theme={null}
{
  "id": "trf_6h2k9m4p1w8e5r3t6y0u2i4a",
  "object": "transfer",
  "account_id": "acct_9f2a7c1e6a2b4f3c9b8d2e5a",
  "source": {"type": "trading_account"},
  "destination": {"type": "virtual_account", "virtual_account_id": "va_7f3k2m9p4q1w8e5r3t6y0u2i"},
  "amount": "250.00",
  "currency": "USD",
  "status": "completed",
  "reference": "Fund operating account",
  "internal_note": null,
  "created": "2026-01-15T10:00:00Z",
  "updated": "2026-01-15T10:00:00Z",
  "created_by": "ak_9a3yfd2ro3jeoqqalbx8jew1",
  "updated_by": null
}
```
