> 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

<figure><img src="/files/MQq54uAjmsMwG3Ym96fc" alt=""><figcaption></figcaption></figure>

Create a payout to send funds directly to a recipient’s wallet on the selected blockchain. PayRam validates the request, applies your project’s payout limits, and either queues the payout for sending or holds it for manual approval.

***

### Authentication

This endpoint is **project-API-key only**. Use the API key generated for the project you’re paying out from (Project → API Keys). The payout is created under that key’s project.

### Endpoint

| Item                                            | Value                                                 |
| ----------------------------------------------- | ----------------------------------------------------- |
| <mark style="color:$warning;">`BASE_URL`</mark> | Your PayRam server URL, e.g. `https://yourdomain.com` |
| Method                                          | <mark style="color:$warning;">`POST`</mark>           |

> BASE\_URL: use your plain HTTPS domain (<mark style="color:$warning;">`https://yourdomain.com`</mark>).

### Headers

| Header                                                 | Required | Example                                                                                                                                     |
| ------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:$warning;">`API-Key`</mark>         | Yes      | <mark style="color:$warning;">`be703fa47ebe07121102ee260fb3d5c0`</mark> (project key)                                                       |
| <mark style="color:$warning;">`Content-Type`</mark>    | Yes      | <mark style="color:$warning;">`application/json`</mark>                                                                                     |
| <mark style="color:$warning;">`Idempotency-Key`</mark> | No       | A unique value of your choosing, e.g. <mark style="color:$warning;">`payout-2026-08-13-00417`</mark>. See **Preventing duplicate payouts**. |

### Request Body

| Field                                                     | Type             | Required   | Description                                                                                                                                                                                                                                                                                                           |
| --------------------------------------------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:$warning;">`email`</mark>              | string           | ✅ Yes      | Recipient’s email address (must be a valid email).                                                                                                                                                                                                                                                                    |
| <mark style="color:$warning;">`blockchainCode`</mark>     | string           | ✅ Yes      | Blockchain network. One of <mark style="color:$warning;">`ETH`</mark>, <mark style="color:$warning;">`BASE`</mark>, <mark style="color:$warning;">`POLYGON`</mark>, <mark style="color:$warning;">`TRX`</mark>.                                                                                                       |
| <mark style="color:$warning;">`currencyCode`</mark>       | string           | ✅ Yes      | Token or native coin to send, e.g. <mark style="color:$warning;">`USDC`</mark>, <mark style="color:$warning;">`USDT`</mark>, <mark style="color:$warning;">`ETH`</mark>, <mark style="color:$warning;">`POL`</mark>, <mark style="color:$warning;">`TRX`</mark>. Must be enabled for the project on the chosen chain. |
| <mark style="color:$warning;">`amount`</mark>             | string (decimal) | ✅ Yes      | **Crypto amount** to send, in the currency’s own units — **not USD**. E.g. <mark style="color:$warning;">`"100"`</mark> = 100 USDC. See “Amounts are in crypto” below.                                                                                                                                                |
| <mark style="color:$warning;">`toAddress`</mark>          | string           | ✅ Yes      | Recipient wallet address; must be valid for <mark style="color:$warning;">`blockchainCode`</mark>.                                                                                                                                                                                                                    |
| <mark style="color:$warning;">`customerID`</mark>         | string           | ✅ Yes\*    | Your unique identifier for the recipient in your system. \*Technically optional in the schema, but a payout **cannot be created without it** — omitting it returns an error. Always send it.                                                                                                                          |
| <mark style="color:$warning;">`mobileNumber`</mark>       | string           | ❌ Optional | Recipient’s mobile number.                                                                                                                                                                                                                                                                                            |
| <mark style="color:$warning;">`residentialAddress`</mark> | string           | ❌ Optional | Recipient’s address.                                                                                                                                                                                                                                                                                                  |
| <mark style="color:$warning;">`idempotencyKey`</mark>     | string           | ❌ Optional | Alternative to the <mark style="color:$warning;">`Idempotency-Key`</mark> header. If both are sent, the header wins.                                                                                                                                                                                                  |

### Amounts are in Crypto (not USD)

<mark style="color:$warning;">`amount`</mark> is the exact on-chain amount in the currency’s units (e.g. <mark style="color:$warning;">`"100"`</mark> USDC sends 100 USDC; <mark style="color:$warning;">`"0.05"`</mark> ETH sends 0.05 ETH). PayRam computes the USD value internally for limit checks.

If your system works in fiat, convert USD → crypto **before** creating the payout using the public ticker endpoint, then send the resulting crypto amount:

1. <mark style="color:$warning;">`GET {BASE_URL}/api/v1/ticker`</mark> returns each currency’s live USD <mark style="color:$warning;">`price`</mark> and <mark style="color:$warning;">`walletPrecision`</mark>.
2. <mark style="color:$warning;">`cryptoAmount = usdAmount / price`</mark>, rounded to that currency’s <mark style="color:$warning;">`walletPrecision`</mark>. (Stablecoins have <mark style="color:$warning;">`price = "1.0"`</mark>, so the crypto amount equals the USD amount.)
3. Create the payout with that <mark style="color:$warning;">`amount`</mark>.

> **Native coins are supported.** You can pay out native <mark style="color:$warning;">`ETH`</mark>, <mark style="color:$warning;">`POL`</mark>, and <mark style="color:$warning;">`TRX`</mark> (not only tokens). **BTC is not supported for payouts.**

### Payout Limits and Approval

At creation, PayRam evaluates the payout (in USD) against your project’s limits, **per recipient member within the project**:

* **Auto-approve amount** — payouts at or under this are auto-approved and queued for sending (<mark style="color:$warning;">`status: "pending"`</mark>). Payouts above it require manual approval (<mark style="color:$warning;">`status: "pending-approval"`</mark>).
* **Hourly limit** / **Daily limit** — if the member’s cumulative payouts in this project for the current hour/day would exceed these, the payout is held for approval.
* **Minimum payout** — payouts below this are rejected outright.

When a payout is held, the response <mark style="color:$warning;">`status`</mark> is <mark style="color:$warning;">`pending-approval`</mark> and <mark style="color:$warning;">`attributes.approvalReason`</mark> explains why (<mark style="color:$warning;">`above_auto_approve`</mark>, <mark style="color:$warning;">`daily_limit_exceeded`</mark>, <mark style="color:$warning;">`hourly_limit_exceeded`</mark>, etc.). An admin then approves/rejects it from the dashboard.

> These thresholds are configured per installation and can be overridden per project in the dashboard (**Project → Payout Limits**; the global minimum lives under **Settings → Withdrawal Limits**). Contact PayRam support to change global defaults. Don’t hard-code specific limit values in your integration — read them from your dashboard.

### Idempotency key

{% hint style="warning" %} <mark style="color:$warning;">**Always send an idempotency key.**</mark> <mark style="color:$info;">A payout moves real money. If your request times out and you retry it without a key, PayRam cannot tell that retry apart from a deliberate second payment and will send the funds twice.</mark>
{% endhint %}

Generate a unique value per payout before your first attempt, store it alongside the payout in your own system, and send it as the <mark style="color:$warning;">`Idempotency-Key`</mark> header (or as <mark style="color:$warning;">`idempotencyKey`</mark> in the body). Use something you can look up again, such as an order ID — not a timestamp, or a retry would produce a different key.

Retry with the **same** key and PayRam returns the original payout instead of creating a second one. Keys are scoped per project and never expire.

If you create a payout **without** a key and it matches another from the last 5 minutes — same project, recipient address, amount, currency and chain — it is rejected with <mark style="color:$warning;">`409 PAYOUT_DUPLICATE_REQUEST`</mark>. Send a key if you genuinely need two identical payouts in that window.

{% hint style="info" %} <mark style="color:$primary;">**Note**</mark><mark style="color:$info;">: Idempotency keys require</mark> <mark style="color:$info;"></mark><mark style="color:$info;">**PayRam v3.6.0 or later**</mark><mark style="color:$info;">. On older versions the key is ignored.</mark>
{% endhint %}

***

### Example Request

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

### Example Response

<mark style="color:$warning;">`201 Created`</mark> — the created payout object:

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

<mark style="color:$warning;">`status`</mark> will be:

* <mark style="color:$warning;">**`pending`**</mark> — auto-approved (within limits); queued for sending.
* <mark style="color:$warning;">**`pending-approval`**</mark> — held for manual admin approval (see <mark style="color:$warning;">`attributes.approvalReason`</mark>).

> **The&#x20;**<mark style="color:$warning;">**`id`**</mark>**&#x20;field is important** — it uniquely identifies the payout. Store it; you’ll use it to track status via <mark style="color:$warning;">`GET /api/v1/withdrawal/{id}/merchant`</mark> (or find it in the list endpoint).

### Tracking Status

Track status either way:

* **Webhooks (recommended)** — get a <mark style="color:$warning;">`payout.<status>`</mark> event pushed to you on every change (see **Payout Webhooks**).
* **Polling** — <mark style="color:$warning;">`GET /api/v1/withdrawal/{id}/merchant`</mark> (single) or <mark style="color:$warning;">`GET /api/v1/withdrawal/merchant`</mark> (list).

The payout moves through: <mark style="color:$warning;">`pending-approval → pending → initiated → sent → processed`</mark> (<mark style="color:$warning;">`failed`</mark> / <mark style="color:$warning;">`rejected`</mark> are terminal). See the **Status lifecycle** in the Overview.

### Errors

| HTTP | When                                                                                                                                                                                                                                                                                                                                                                                                       |
| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400  | Missing/invalid required field (<mark style="color:$warning;">`email`</mark> not a valid email, missing <mark style="color:$warning;">`blockchainCode`</mark> / <mark style="color:$warning;">`currencyCode`</mark> / <mark style="color:$warning;">`amount`</mark> / <mark style="color:$warning;">`toAddress`</mark>, invalid wallet address for the chain, or invalid blockchain/currency combination). |
| 400  | <mark style="color:$warning;">`PAYOUT_AMOUNT_BELOW_MINIMUM`</mark> (amount below the project minimum), or <mark style="color:$warning;">`PAYOUT_CURRENCY_DISABLED`</mark> (currency not enabled for payouts).                                                                                                                                                                                              |
| 409  | <mark style="color:$warning;">`PAYOUT_DUPLICATE_REQUEST`</mark> — a matching payout was created in the last 5 minutes and no idempotency key was sent. See **Preventing duplicate payouts**.                                                                                                                                                                                                               |
| 401  | Missing/invalid <mark style="color:$warning;">`API-Key`</mark>, or a non-project key.                                                                                                                                                                                                                                                                                                                      |
| 500  | <mark style="color:$warning;">`customerID`</mark> omitted (“failed to create customer”), <mark style="color:$warning;">`BTC`</mark> selected (not supported for payouts), or an unexpected server error.                                                                                                                                                                                                   |
| 503  | <mark style="color:$warning;">`EXCHANGE_RATE_UNAVAILABLE`</mark> — a live exchange rate couldn’t be fetched for a non-stablecoin (retryable; try again shortly).                                                                                                                                                                                                                                           |

### Convert USD → Crypto (Ticker)

The Create Payout API takes a **crypto** `amount`, but many systems work in **fiat (USD)**. Use the ticker endpoint to fetch live USD prices, convert your USD amount to the crypto amount, then create the payout. This keeps your conversion aligned with the same pricing PayRam uses internally for its limit checks.

### Endpoint

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

**Public** — no <mark style="color:$warning;">`API-Key`</mark> required. Returns every currency configured on your server with its current USD price.

### Example Request

```bash
curl --location --request GET '${BASE_URL}/api/v1/ticker'
```

### Example Response

<mark style="color:$warning;">`200 OK`</mark> — an array, one entry per blockchain + 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                                                                                                                                         |
| ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:$warning;">`blockchainCode`</mark> / <mark style="color:$warning;">`currencyCode`</mark> | The chain + currency this price applies to.                                                                                                     |
| <mark style="color:$warning;">`price`</mark>                                                                | Current price of **1 unit of the currency in USD** (stablecoins are <mark style="color:$warning;">`"1.0"`</mark>).                              |
| <mark style="color:$warning;">`walletPrecision`</mark>                                                      | Decimal places to round the converted crypto amount to.                                                                                         |
| <mark style="color:$warning;">`tokenAddress`</mark>                                                         | Token contract (<mark style="color:$warning;">`0x000…000`</mark> for native coins).                                                             |
| <mark style="color:$warning;">`standard`</mark>                                                             | <mark style="color:$warning;">`native`</mark>, <mark style="color:$warning;">`ERC20`</mark>, <mark style="color:$warning;">`TRC20`</mark>, etc. |

### How to Convert

For the currency you’re paying out, find the matching row (by `blockchainCode` + `currencyCode`), then:

```
cryptoAmount = usdAmount / price          # rounded to walletPrecision
```

* **Stablecoins** (<mark style="color:$warning;">`price = "1.0"`</mark>) → <mark style="color:$warning;">`cryptoAmount = usdAmount`</mark>.
* **Other currencies** → divide by <mark style="color:$warning;">`price`</mark> and round to <mark style="color:$warning;">`walletPrecision`</mark>.

**Example:** pay out **$50 in ETH** when <mark style="color:$warning;">`price = "1659.57"`</mark>:\ <mark style="color:$warning;">`50 / 1659.57 = 0.030128…`</mark> → rounded to 18 dp → <mark style="color:$warning;">`amount: "0.030128..."`</mark>.

#### Recommended flow

1. Your back office validates the **USD** amount (your own rules).
2. <mark style="color:$warning;">`GET /api/v1/ticker`</mark> → look up the target currency’s <mark style="color:$warning;">`price`</mark> and <mark style="color:$warning;">`walletPrecision`</mark>.
3. Convert USD → crypto and round to <mark style="color:$warning;">`walletPrecision`</mark>.
4. <mark style="color:$warning;">`POST /api/v1/withdrawal/merchant`</mark> with the resulting crypto <mark style="color:$warning;">`amount`</mark>.

> Fetch the ticker **right before** creating the payout so the rate is current. Rounding to <mark style="color:$warning;">`walletPrecision`</mark> avoids “fractional base unit” rejections on create.
