# Create & manage transfers

> Create Moov transfers with the API or Dashboard, retry safely with idempotency keys, get transfer options, and paginate transfer history with cursors.

> AI agents: search and read all Moov docs with the docs MCP server at https://docs.moov.io/mcp (no API key). Setup: https://docs.moov.io/guides/developer-tools/mcp-ai/docs-mcp/index.md

Create transfers, retry them without charging anyone twice, and find them again later, with the API or in the Dashboard. For how a transfer moves once it's created, see [transfers](/guides/money-movement/index.md).

## Start a transfer

The source and destination payment methods you choose set the rail, and with it the cost, speed, and risk. See [choose a payment method](/guides/money-movement/payment-methods/index.md#choose-a-payment-method) to compare them.

Create a transfer with `POST /accounts/{accountID}/transfers` ([API reference](/api/money-movement/transfers/create/index.md)), where `accountID` is your partner account. Two rules apply to every transfer:

- **A `2xx` response means Moov accepted the transfer, not that money moved.** Don't ship goods or mark an invoice paid until the transfer reaches the status that matters for you. See [create transfer responses](/guides/money-movement/events-and-statuses/index.md#create-transfer-responses).
- **Send an `X-Idempotency-Key` header.** It's required, and it stops a retried request from creating a second transfer. See [retry safely](#retry-safely).

### Retry safely

An idempotency key makes a request safe to retry: sending it again returns the original result instead of charging someone twice. These endpoints require an `X-Idempotency-Key` header:

- `POST` [Create transfer](/api/money-movement/transfers/create/index.md)
- `POST` [Refund transfer](/api/money-movement/refunds/create/index.md)
- `POST` [Cancel or refund a card transfer](/api/money-movement/refunds/cancel/index.md)

Generate the key yourself and make it deterministic, so a retry of the same action sends the same key: a unique ID from your system of up to 36 characters, or a UUID built from the action's identifiers (Moov supports UUID v5). Keys don't expire. A second request with a key Moov has already seen returns a `409` with the original transfer in the body; use that transfer, and never retry with a new key.

### Get transfer options

Not every source and destination pair is valid, and what's available depends on each account's capabilities. The transfer options `POST` [endpoint](/api/money-movement/transfers/options/index.md) returns the valid combinations, accounting for network restrictions such as maximum amounts. Use it to show a payer or payee a menu of ways to move money.

- Send an `accountID` for the source or destination to list its valid payment methods.
- Add a `paymentMethodID` to narrow the results. For example, if you know the source is a card, the response lists the destinations that card can pay.

**cURL**

```zsh
curl -X POST "https://api.moov.io/accounts/{accountID}/transfer-options" \
  -H "Authorization: Bearer {token}" \
  -H "x-moov-version: v2026.07.00" \
  --data-raw '{
    "amount": {
      "value": 100,
      "currency": "USD"
    },
    "destination": {
      "accountID": "string"
    },
    "source": {
      "accountID": "string"
    }
  }'
```

**Go SDK**

```go
mc, _ := moov.NewClient()

// your partner accountID
var accountID string

mc.TransferOptions(ctx, accountID, moov.CreateTransferOptions{
  Source: moov.CreateTransferOptionsTarget{
    AccountID: "string",
  },
  Destination: moov.CreateTransferOptionsTarget{
    AccountID: "string",
  },
  Amount: moov.Amount{
    Currency: "USD",
    Value:    1,
  },
})
```

**TypeScript**

```typescript
const result = await moov.transfers.generateOptions({
  accountID: "deafe3cf-31d4-4dcc-8176-3d6bf8bb4f04",
  createTransferOptions: {
    source: {},
    destination: {},
    amount: {
      currency: "USD",
      value: 1204,
    },
  },
});
```

**Python**

```python
res = moov.transfers.generate_options(account_id="deafe3cf-31d4-4dcc-8176-3d6bf8bb4f04", source={}, destination={}, amount={
    "currency": "USD",
    "value": 1204,
})
```

**Ruby**

```ruby
res = s.transfers.generate_options(account_id: 'deafe3cf-31d4-4dcc-8176-3d6bf8bb4f04', create_transfer_options: Models::Components::CreateTransferOptions.new(
  source: Models::Components::SourceDestinationOptions.new,
  destination: Models::Components::SourceDestinationOptions.new,
  amount: Models::Components::Amount.new(
    currency: 'USD',
    value: 1204
  )
))
```

**Java**

```java
CreateTransferOptionsResponse res = sdk.transfers().generateOptions()
        .accountID("deafe3cf-31d4-4dcc-8176-3d6bf8bb4f04")
        .createTransferOptions(CreateTransferOptions.builder()
            .source(SourceDestinationOptions.builder()
                .build())
            .destination(SourceDestinationOptions.builder()
                .build())
            .amount(Amount.builder()
                .currency("USD")
                .value(1204L)
                .build())
            .build())
        .call();
```

**.NET**

```csharp
var res = await sdk.Transfers.GenerateOptionsAsync(
    accountID: "deafe3cf-31d4-4dcc-8176-3d6bf8bb4f04",
    body: new CreateTransferOptions() {
        Source = new SourceDestinationOptions() {},
        Destination = new SourceDestinationOptions() {},
        Amount = new Amount() {
            Currency = "USD",
            Value = 1204,
        },
    }
);
```

**PHP**

```php
$createTransferOptions = new Components\CreateTransferOptions(
    source: new Components\SourceDestinationOptions(),
    destination: new Components\SourceDestinationOptions(),
    amount: new Components\Amount(
        currency: 'USD',
        value: 1204,
    ),
);

$response = $sdk->transfers->generateOptions(
    accountID: 'deafe3cf-31d4-4dcc-8176-3d6bf8bb4f04',
    createTransferOptions: $createTransferOptions

);
```

See [payment methods](/guides/money-movement/payment-methods/index.md) for every valid combination and [transfer limits](/guides/money-movement/payment-methods/index.md#transfer-limits).

## List transfers

The list transfers [endpoint](/api/money-movement/transfers/list/index.md) returns up to `count` transfers per page, newest first. Offset pagination with `skip` is limited to a maximum `skip` of 200,000. To paginate through larger transfer histories, use a cursor instead.

Request the first page without a cursor:

```zsh
curl -i "https://api.moov.io/accounts/{accountID}/transfers?status=completed&count=200" \
  -H "Authorization: Bearer {token}" \
  -H "x-moov-version: v2026.07.00"
```

If more transfers are available, the response includes an `X-Next-Cursor` header:

```http
HTTP/1.1 200 OK
X-Next-Cursor: eyJ2IjoxLCJ0IjoiMjAyNi0wOS0yNVQxMjowMDowMFoiLCJpZCI6IjAwMDAwMDAwLTAwMDAtNDAwMC04MDAwLTAwMDAwMDAwMDAwMSJ9
```

To get the next page, pass that value as the `cursor` query parameter. Keep the other query parameters the same:

```zsh
curl -i "https://api.moov.io/accounts/{accountID}/transfers?status=completed&count=200&cursor=eyJ2IjoxLCJ0IjoiMjAyNi0wOS0yNVQxMjowMDowMFoiLCJpZCI6IjAwMDAwMDAwLTAwMDAtNDAwMC04MDAwLTAwMDAwMDAwMDAwMSJ9" \
  -H "Authorization: Bearer {token}" \
  -H "x-moov-version: v2026.07.00"
```

Repeat until a response has no `X-Next-Cursor` header, which means you've reached the last page.

- Use the same filters on every page. Changing filters between pages can skip or repeat transfers.
- Treat the cursor as opaque. Don't parse, build, or modify it.
- Don't combine `cursor` with `skip`. The request fails with a `422` error.
- Transfers created after you fetch the first page don't appear on later pages.

## In the Dashboard

The **Transfers** section of the Dashboard provides an overview of your account's transfer history. You can create and manage transfers, including [reversals](/guides/accept-payments/cards/reversals/index.md#in-the-dashboard), [refunds](/guides/accept-payments/cards/refunds/index.md#in-the-dashboard), and [cancellations](/guides/ach/cancellations/index.md#request-a-cancellation), and view and manage [disputes](/guides/accept-payments/cards/disputes/index.md#in-the-dashboard).

![Transfer list view in Moov Dashboard](../../shared-images/transfers-view.png)

### Peek preview

The peek preview shortcut gives you fast access to transfer status and details. Press the `space` key to toggle the preview on and off. You can move your cursor up and down the transfer list and the preview will update with the corresponding transfer details. The `esc` key will also dismiss the preview. Press `c` while the preview is open to copy the `transferID` to your clipboard.

[*Video: peek preview*](https://docs.moov.io/guides/money-movement/images/peek-preview.mp4)

### Create a transfer

Create a transfer between any two Moov accounts by clicking **New transfer** and choosing `Payout` or `Collect`. You can also create a scheduled transfer or recurring transfers. See [scheduling](/guides/money-movement/scheduling/index.md#in-the-dashboard) for more information.

You'll need to input the following information to create a transfer:

- A [product](/guides/account-tools/product-catalog/index.md) or an amount
- Source
- Destination

Optionally, you can enter:

- Tax amount
- Tip amount
- [Surcharge](/guides/accept-payments/cards/surcharges/index.md) amount (credit card transactions)
- [Payout fees](/guides/send-payments/payout-fees/index.md)
- Recipient fees (payouts)
- Description
- Metadata
- ACH addenda (ACH transfers)

![New transfer form in Moov Dashboard](../images/surcharge-transfer.png)

Any business that intends to add a surcharge to credit card transactions must notify Moov 30 days before implementing surcharges. See the [surcharges](/guides/accept-payments/cards/surcharges/index.md) guide for more information.

To pass a payout fee along to the recipient, look under **Advanced settings** when creating the transfer and find the **Payout fee funding** option. If not set, the source wallet pays the payout fee.

![Enable payout fees](../images/enable-payout-fees.png)

### Transfer details

Clicking any transfer will bring you to the **Transfer details** page, where you can manage and review information about a transfer and its current [status](/guides/money-movement/events-and-statuses/index.md).

![Transfer detail view in Moov Dashboard](../images/transfer-details.png)

The transfer timeline shows information on each leg of the transfer to give transparency on where the funds are in the process. For example, if a transfer from a bank account to a Moov wallet is created, you'll see the ACH debit information followed by the funds availability in the wallet.

[*Video: transfer timeline*](https://docs.moov.io/guides/money-movement/images/transfer-timeline.mp4)

