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

# Send a transfer

> Move USDC or USDT from one of your users' addresses to another of yours, on one chain.

Moves a balance from one of your users' addresses to another user's address on your account. Use it when your users settle with each other, split a bill, or pay one another inside your product.

<Warning>
  Backend only. Never call from frontend.
</Warning>

A transfer is an ordinary on-chain send between two addresses you created, made on your API instruction. Because the receiving address is provably yours, it doesn't need to be on your approved destinations list the way a [withdrawal](/api-reference/wallet-api/withdrawals) does.

## Endpoint

```text theme={null}
POST https://merchant.minisend.xyz/api/v1/transfers
```

```text theme={null}
Authorization: Bearer wsk_live_your_key_here
```

Requires the `transfer` scope. It defaults off on every key, including keys created before scopes existed. Contact us to turn it on for a key that should be able to move money between your users.

## Body

<ParamField body="from" type="string" required>
  The `walletRef` of the user paying.
</ParamField>

<ParamField body="to" type="string" required>
  The `walletRef` of the user being paid. Must be a different address from `from`.
</ParamField>

<ParamField body="chain" type="string" required>
  The chain to send on. Both addresses need to be live on it. Transfers stay on one chain; to consolidate balances across chains, use [settlement](/wallet-api/settlement).
</ParamField>

<ParamField body="token" type="string" required>
  `USDC` or `USDT`.
</ParamField>

<ParamField body="amount" type="number" required>
  A positive number, in the token's units, with at most 6 decimal places. The smallest transfer is 0.1 on most chains, 0.5 on Avalanche and 20 on Ethereum.
</ParamField>

<ParamField body="idempotency_key" type="string">
  Reusing the same key with the same request returns the original transfer instead of sending a second one. Reusing it with a different `from`, `to`, `chain`, `token` or `amount` is refused with `409`. Keys are shared with withdrawals, so a key you used for a withdrawal can't be reused for a transfer.
</ParamField>

## Example

```bash theme={null}
curl -X POST https://merchant.minisend.xyz/api/v1/transfers \
  -H "Authorization: Bearer wsk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "user-42",
    "to": "user-7",
    "chain": "BASE",
    "token": "USDC",
    "amount": 12.5,
    "idempotency_key": "split-8841"
  }'
```

## Response (202)

```json theme={null}
{
  "transfer": {
    "id": "8f21c3d0-...",
    "state": "queued",
    "from": "user-42",
    "to": "user-7",
    "chain": "BASE",
    "token": "USDC",
    "amount": "12.500000",
    "tx_hash": null,
    "created_at": "2026-10-01T09:00:00.000Z",
    "updated_at": "2026-10-01T09:00:00.000Z"
  }
}
```

`202` means the transfer is accepted and queued, and it goes out shortly after. Poll [get a transfer](#getting-one-transfer) or listen for [`wallet.transfer.completed`](/wallet-api/webhooks#wallet-transfer-completed-and-wallet-transfer-failed). Replaying an `idempotency_key` returns `200` with the original transfer.

<ResponseField name="state" type="string" required>
  `queued`, `sending`, `completed`, `failed`, or `under_review`. `failed` means nothing moved and the amount is still at the `from` address. `under_review` is rare: we couldn't confirm the outcome, so we're checking it. Don't treat it as either paid or failed yet.
</ResponseField>

<ResponseField name="tx_hash" type="string">
  The on-chain transaction, once it completes. `null` until then, and while `failed`.
</ResponseField>

<Note>
  The receiving address does not get a `wallet.deposit.received` for a transfer. Credit the receiver in your own records from `wallet.transfer.completed`, so the same money is never counted twice.
</Note>

## Errors

| Status | Meaning |
| - | - |
| `400` | The body is invalid, the amount is below the chain's minimum, either address isn't live on `chain`, or `from` doesn't hold enough of `token` on that chain. Money already committed to transfers or withdrawals still in progress counts as spent. |
| `403` | The key doesn't have the `transfer` scope. |
| `404` | No address with the reference in `from` or `to`. |
| `409` | One of the addresses is frozen, the `idempotency_key` was used for a different request, or transfers aren't enabled on your account yet. |

## Listing your transfers

```text theme={null}
GET https://merchant.minisend.xyz/api/v1/transfers
```

Needs `wallets:read`. Newest first.

<ParamField query="limit" default="20" type="number">
  Page size, max 100.
</ParamField>

<ParamField query="offset" default="0" type="number">
  Rows to skip.
</ParamField>

```json theme={null}
{
  "transfers": [
    {
      "id": "8f21c3d0-...",
      "state": "completed",
      "from": "user-42",
      "to": "user-7",
      "chain": "BASE",
      "token": "USDC",
      "amount": "12.500000",
      "tx_hash": "0x9b3e51c7a2d84f06e1b9c3a7d5f2e8b4c6a0d1f3e5b7c9a2d4f6e8b0c1a3d5f7",
      "created_at": "2026-10-01T09:00:00.000Z",
      "updated_at": "2026-10-01T09:00:41.000Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}
```

## Getting one transfer

```text theme={null}
GET https://merchant.minisend.xyz/api/v1/transfers/{transferId}
```

Needs `wallets:read`. Returns `{ "transfer": { ... } }` in the same shape as above, or `404` for an id that isn't yours.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.