> For the complete documentation index, see [llms.txt](https://docs.payram.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payram.com/api-integration/payouts-apis/create-payouts.md).

# Create Payouts

In this section, you’ll learn how to create a payout in PayRam to send funds directly to a recipient’s wallet on the selected blockchain.

<figure><img src="https://content.gitbook.com/content/wm1DIvbGMREWT1TdLPtz/blobs/O2IPnGNAlwUzG7PQqKNQ/payram-create-payouts.png" alt=""><figcaption></figcaption></figure>

Create an [API payout](/api-integration/payouts-apis/overview.md#two-kinds-of-merchant-payout). PayRam checks it against your project’s limits and either queues it to send (`pending`) or holds it for approval (`pending-approval`).

**Before you start:** the project needs a hot wallet assigned for the payout’s network ([Hot Wallet Setup](/onboarding-guide/hot-wallet-setup.md)), otherwise the request fails with `422 PAYOUT_HOT_WALLET_NOT_ASSIGNED` (PayRam Core v3.9.0 and later).

***

### Endpoint

```
POST {BASE_URL}/api/v1/withdrawal/merchant
```

| Header            | Required | Value                                                                       |
| ----------------- | -------- | --------------------------------------------------------------------------- |
| `API-Key`         | Yes      | Your project API key                                                        |
| `Content-Type`    | Yes      | `application/json`                                                          |
| `Idempotency-Key` | No       | Up to 255 characters, e.g. `payout-00417`. See [Idempotency](#idempotency). |

### Request body

| Field                | Type   | Required | Description                                                                                                                                             |
| -------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`              | string | Yes      | Recipient’s email (must be a valid email).                                                                                                              |
| `blockchainCode`     | string | Yes      | `ETH`, `BASE`, `POLYGON` or `TRX`.                                                                                                                      |
| `currencyCode`       | string | Yes      | Token or native coin, e.g. `USDC`, `USDT`, `ETH`, `POL`, `TRX`. See [Supported networks](/api-integration/payouts-apis/overview.md#supported-networks). |
| `amount`             | string | Yes      | Amount **in the token, not USD** — e.g. `"100"` USDC or `"0.05"` ETH. See [Amounts](#amounts).                                                          |
| `toAddress`          | string | Yes      | Recipient wallet; must be valid for `blockchainCode`.                                                                                                   |
| `customerID`         | string | Yes      | Your identifier for the recipient. A payout cannot be created without it.                                                                               |
| `mobileNumber`       | string | No       | Recipient’s mobile number.                                                                                                                              |
| `residentialAddress` | string | No       | Recipient’s address.                                                                                                                                    |
| `idempotencyKey`     | string | No       | Same as the `Idempotency-Key` header; the header wins if both are sent.                                                                                 |

### Amounts

* `amount` is in the token’s own units. To pay a USD value, convert it first with the [ticker](#convert-usd-to-crypto-ticker).
* PayRam truncates the amount to the token’s precision (`walletPrecision`) — never rounds up and never rejects for extra decimals. An amount that truncates to zero is rejected. **Round it yourself** so your records match what is sent.
* Bitcoin payouts are not supported: they are refused at creation with `400 PAYOUT_CURRENCY_DISABLED` (PayRam Core v3.9.0 and later).

### Limits and approval

Limits are evaluated in **USD** over rolling windows: the last 60 minutes and the last 24 hours. The totals count what the same account has paid out in the project: the API key’s account for API payouts. Payouts held for approval count toward those totals; failed, rejected and cancelled ones do not.

| Outcome                                       | Result                                                                     |
| --------------------------------------------- | -------------------------------------------------------------------------- |
| Within limits                                 | `201`, `status: "pending"` — queued to send.                               |
| Over the auto-approve amount, or over a limit | `201`, `status: "pending-approval"` with `approvalReason` in `attributes`. |
| Below the minimum                             | `400 PAYOUT_AMOUNT_BELOW_MINIMUM` — no payout is created.                  |

Limits are set in the dashboard — see [Editing Payout Limits](/api-integration/payouts-apis/editing-payout-limits.md). Don’t hard-code them.

### Idempotency

{% hint style="warning" %}
**Always send an idempotency key.** Without one, PayRam cannot tell a retry after a timeout from a deliberate second payout.
{% endhint %}

* Generate one key per payout before the first attempt and store it with the payout (an order ID works; a timestamp does not).
* Retrying with the same key returns the **original** payout with `201` and the header `Idempotent-Replayed: true` (PayRam Core v3.9.0 and later). Keys are per project and do not expire.
* A reused key with a different body still returns the original payout — **never reuse a key for a different payout.**
* **Without a key**, an identical payout (same project, address, token, network and amount) within 5 minutes is refused with `409 PAYOUT_DUPLICATE_REQUEST`.

***

### Example request

```bash
curl --location '${BASE_URL}/api/v1/withdrawal/merchant' \
--header 'API-Key: <API_KEY>' \
--header 'Idempotency-Key: payout-00417' \
--header 'Content-Type: application/json' \
--data-raw '{
  "email": "test@test.com",
  "blockchainCode": "ETH",
  "currencyCode": "USDC",
  "amount": "100",
  "toAddress": "0x291b68732f14F47Fd21bE81ec5Cf1bcfC0DB14Ea",
  "customerID": "414817384"
}'
```

### Example response

`201 Created`:

```json
{
  "id": 120,
  "createdAt": "2026-06-19T13:14:41.806Z",
  "blockchainCode": "ETH",
  "currencyCode": "USDC",
  "currencyType": "token",
  "amount": "100",
  "priceInUSD": "1",
  "amountInUSD": "100",
  "toAddress": "0x291b68732f14F47Fd21bE81ec5Cf1bcfC0DB14Ea",
  "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "recipientEmail": "test@test.com",
  "txHash": null,
  "status": "pending-approval",
  "type": "payout_merchant",
  "attributes": "{\"approvalReason\":\"above_auto_approve\"}",
  "createdBy": "user"
}
```

Store the `id` — you need it to [look up the payout](/api-integration/payouts-apis/get-single-payout.md). Field meanings are in [GET All Payouts](/api-integration/payouts-apis/get-all-payouts.md#response-fields).

### Errors

API errors use this shape:

```json
{"error": {"code": "PAYOUT_DUPLICATE_REQUEST", "message": "...", "details": "..."}}
```

Only an error worth retrying (for example `EXCHANGE_RATE_UNAVAILABLE`) also carries `"retryable": true`; otherwise the field is absent.

A body that fails validation (missing field, invalid email) returns `400` with a different shape; `fields` maps each field to its problem. Malformed JSON (including a non-numeric `amount`) returns `400` with `"error": "Invalid JSON"` and no `fields`.

```json
{"error": "Validation failed", "message": "Invalid request data", "fields": {"Email": "Email must be a valid email address"}}
```

| HTTP  | `code`                           | When                                                                                                                                                                                                                              |
| ----- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | `BAD_REQUEST`                    | Amount zero, negative, too large, or below the token’s smallest unit; address invalid for the network; unknown network or token code; missing `customerID` (PayRam Core v3.9.0 and later). `Idempotency-Key` over 255 characters. |
| `400` | `PAYOUT_CURRENCY_DISABLED`       | Payouts are turned off for this token, or it is Bitcoin. (PayRam Core v3.9.0 and later)                                                                                                                                           |
| `400` | `PAYOUT_AMOUNT_BELOW_MINIMUM`    | Amount is below the project minimum.                                                                                                                                                                                              |
| `401` | `UNAUTHORIZED`                   | Missing or invalid `API-Key`, or not a project key.                                                                                                                                                                               |
| `403` | `INSUFFICIENT_PERMISSIONS`       | The key lacks payout permission.                                                                                                                                                                                                  |
| `404` | `NOT_FOUND`                      | This network + token pair is not configured on the server.                                                                                                                                                                        |
| `409` | `PAYOUT_DUPLICATE_REQUEST`       | Identical payout within 5 minutes and no idempotency key.                                                                                                                                                                         |
| `422` | `PAYOUT_HOT_WALLET_NOT_ASSIGNED` | No hot wallet assigned to the project for this network. (PayRam Core v3.9.0 and later)                                                                                                                                            |
| `503` | `EXCHANGE_RATE_UNAVAILABLE`      | No live price for a non-stablecoin. Retryable; sent with `Retry-After: 30`.                                                                                                                                                       |
| `500` | `INTERNAL_SERVER_ERROR`          | Anything else.                                                                                                                                                                                                                    |

**Retry rule:** treat `4xx` as permanent — fix the request, don’t retry it. Retry `5xx` (including `EXCHANGE_RATE_UNAVAILABLE`), always with the same idempotency key.

***

### Convert USD to crypto (ticker)

```
GET {BASE_URL}/api/v1/ticker
```

Public — no `API-Key`. Returns one entry per network + currency:

```json
[
  {
    "blockchainCode": "ETH",
    "currencyCode": "ETH",
    "tokenAddress": "0x0000000000000000000000000000000000000000",
    "standard": "native",
    "walletPrecision": 18,
    "family": "ETH_Family",
    "price": "1659.57"
  },
  {
    "blockchainCode": "BASE",
    "currencyCode": "USDC",
    "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "standard": "ERC20",
    "walletPrecision": 6,
    "family": "ETH_Family",
    "price": "1.0"
  }
]
```

| Field                             | Meaning                                            |
| --------------------------------- | -------------------------------------------------- |
| `blockchainCode` / `currencyCode` | The network + currency the price applies to.       |
| `price`                           | USD price of 1 unit (stablecoins are `"1.0"`).     |
| `walletPrecision`                 | Decimal places the token supports — round to this. |
| `tokenAddress`                    | Token contract (`0x000…000` for native coins).     |
| `standard`                        | `native`, `ERC20`, `TRC20`, etc.                   |

**Convert:** `amount = usdAmount / price`, rounded down to `walletPrecision`. Example: $50 in ETH at `price = "1659.57"` → `0.030128…`. Fetch the ticker right before creating the payout so the rate is current.
