# Transfers

> Instant transfers from your app balance to PS Wallet users by Telegram ID: the transfer and getTransfers methods.

The `transfer` method sends money from your app balance to a PS Wallet user by their Telegram ID. The transfer is instant and free of charge: the money arrives in the user's account in the same coin and network. It's convenient for paying out winnings, cashback, refunds, and rewards.

## Before you start

1. Enable the method in your app dashboard: "Security" → "Transfers to users via API" (you'll need your PIN). The method is disabled by default, and a request will return the error `403 METHOD_DISABLED`.
2. Top up your app balance: paid invoices are credited to it automatically, and you can also top it up from your own account in the dashboard — free of charge.

> [!WARNING]
> Anyone with your app token will be able to withdraw money from the balance via `transfer`. Enable the method only if you really need it, and keep only the necessary amount on the balance. For more details, see the [Security](https://help.pswallet.org/en/pay-api/security) section.

## transfer

Sends money from the app balance to a PS Wallet user. `POST` only. Returns [Transfer](https://help.pswallet.org/en/pay-api/types#transfer).

| Parameter | Type | Description |
|---|---|---|
| `user_id` | Number | Required. Recipient's Telegram ID. They must have opened PS Wallet at least once. |
| `asset` | String | Required. Coin: `USDT`, `USDT-TRC20`, `USDT-BEP20`, `USDT-ERC20`, `USDT-TON`, `XAUT`, `TRX`, `BNB`, `ETH`, `GRAM`. Rubles can't currently be sent to other users. |
| `amount` | String | Required. Amount. |
| `spend_id` | String | Required. Unique transfer identifier, up to 64 characters. Protects against double sending: a transfer with an already used `spend_id` will not be executed (error `SPEND_ID_USED`). |
| `comment` | String | Optional. A comment the recipient will see in the notification and history if the amount is not below the threshold (default $1). Links and @mentions are removed. Up to 1024 characters. |
| `disable_send_notification` | Boolean | Optional. `true` — don't send the recipient a notification in the bot. Default is `false`. |

If you specify `asset=USDT`, the money is debited from the first network where the app balance has enough funds: TRC20 → BEP20 → TON → ERC20. The transfer network is returned in the `asset` field of the response.

### Request example

```bash title="cURL"
curl https://pay.pswallet.org/api/transfer \
  -H "PSWallet-Pay-API-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 123456789,
    "asset": "USDT-TRC20",
    "amount": "5",
    "spend_id": "prize-2026-11-09-123456789",
    "comment": "Выигрыш в розыгрыше"
  }'
```

```js title="Node.js"
// Node.js 18+ (built-in fetch)
// Generate spend_id once and save it before sending the request
const spendId = `prize-${drawId}-${userId}`;

const response = await fetch("https://pay.pswallet.org/api/transfer", {
  method: "POST",
  headers: {
    "PSWallet-Pay-API-Token": process.env.PSWALLET_PAY_TOKEN,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    user_id: userId,
    asset: "USDT-TRC20",
    amount: "5",
    spend_id: spendId,
    comment: "Выигрыш в розыгрыше",
  }),
});
const data = await response.json();
if (!data.ok) throw new Error(data.error.name);
console.log(data.result.transfer_id);
```

```python title="Python"
import os
import requests

# Generate spend_id once and save it before sending the request
spend_id = f"prize-{draw_id}-{user_id}"

response = requests.post(
    "https://pay.pswallet.org/api/transfer",
    headers={"PSWallet-Pay-API-Token": os.environ["PSWALLET_PAY_TOKEN"]},
    json={
        "user_id": user_id,
        "asset": "USDT-TRC20",
        "amount": "5",
        "spend_id": spend_id,
        "comment": "Выигрыш в розыгрыше",
    },
    timeout=15,
)
data = response.json()
if not data["ok"]:
    raise RuntimeError(data["error"]["name"])
print(data["result"]["transfer_id"])
```

Response:

```json
{
  "ok": true,
  "result": {
    "transfer_id": 311,
    "spend_id": "prize-2026-11-09-123456789",
    "user_id": "123456789",
    "asset": "USDT-TRC20",
    "amount": "5",
    "status": "completed",
    "completed_at": "2026-11-09T12:00:00.000+00:00",
    "comment": "Выигрыш в розыгрыше"
  }
}
```

### Double sending and timeouts

`spend_id` is your transfer key. Generate it once for each payout (for example, from the order or payout number) and save it before sending the request. A repeated request with the same `spend_id` will not transfer the money a second time and will return the error `SPEND_ID_USED`.

> [!WARNING]
> If a request timed out and you don't know whether the transfer went through, check it via `getTransfers` with the same `spend_id` instead of sending it again with a new one. The same applies to the error `500 INTERNAL_ERROR`.

### Comment and notification

- The recipient receives a notification in the PS Wallet bot unless `disable_send_notification=true` is passed.
- The recipient will see the comment in the notification and history only if the transfer amount is not below the threshold (default $1). Below the threshold, the transfer goes through without your text.
- Links and @mentions in the comment the recipient sees are replaced with `[…]`. In the API response and in `getTransfers`, the comment is returned exactly as you sent it.

### transfer errors

The response code is 400 unless stated otherwise.

- `403 METHOD_DISABLED` — the method is not enabled in the "Security" section.
- `403 TRANSFERS_DISABLED` — transfers are restricted for the app owner.
- `403 APP_FROZEN` — the app is frozen.
- `PARAM_USER_ID_REQUIRED`, `PARAM_USER_ID_INVALID` — the Telegram ID is missing or invalid.
- `PARAM_SPEND_ID_REQUIRED`, `PARAM_SPEND_ID_INVALID` — `spend_id` is missing or longer than 64 characters.
- `PARAM_COMMENT_INVALID` — the comment is longer than 1024 characters.
- `PARAM_ASSET_REQUIRED`, `PARAM_ASSET_INVALID` — the coin is missing, unknown, or currently unavailable.
- `PARAM_AMOUNT_REQUIRED`, `PARAM_AMOUNT_INVALID` — the amount is missing, not greater than zero, or has more decimal places than the coin supports.
- `403 TEMPORARILY_UNAVAILABLE` — transfers in this coin are currently unavailable (for example, rubles).
- `404 USER_NOT_FOUND` — the recipient was not found: they have never opened PS Wallet.
- `SPEND_ID_USED` — a transfer with this `spend_id` has already been made.
- `INSUFFICIENT_FUNDS` — there isn't enough money on the app balance.

## getTransfers

A list of the app's transfers, newest first. Returns `{ "items": [Transfer, …] }`.

| Parameter | Type | Description |
|---|---|---|
| `asset` | String | Optional. Only transfers in this coin (`USDT` — across all networks). |
| `transfer_ids` | String | Optional. Comma-separated transfer IDs (the first 1000 are taken into account). |
| `spend_id` | String | Optional. The transfer with this `spend_id`. |
| `offset` | Number | Optional. How many transfers to skip. Default is 0. |
| `count` | Number | Optional. How many to return, from 1 to 1000. Default is 100. |

```bash title="cURL"
curl "https://pay.pswallet.org/api/getTransfers?spend_id=prize-2026-11-09-123456789" \
  -H "PSWallet-Pay-API-Token: $TOKEN"
```

Response:

```json
{
  "ok": true,
  "result": {
    "items": [
      {
        "transfer_id": 311,
        "spend_id": "prize-2026-11-09-123456789",
        "user_id": "123456789",
        "asset": "USDT-TRC20",
        "amount": "5",
        "status": "completed",
        "completed_at": "2026-11-09T12:00:00.000+00:00",
        "comment": "Выигрыш в розыгрыше"
      }
    ]
  }
}
```

An empty `items` when searching by `spend_id` means the transfer didn't happen — you can send it again with the same `spend_id`.

Errors (code 400): `PARAM_ASSET_INVALID`, `PARAM_TRANSFER_IDS_INVALID`, `PARAM_OFFSET_INVALID`, `PARAM_COUNT_INVALID`.

---
Source: https://help.pswallet.org/en/pay-api/transfers
