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

# Crypto Payouts

> Withdraw stablecoins from virtual accounts or settle fiat to a beneficiary via off-ramp

PCX supports two crypto payout flows:

1. **Virtual account crypto withdrawal** — withdraw the balance of a PCX fiat virtual account as USDC, USDT, or another supported asset to an external wallet.
2. **Off-ramp (crypto → fiat)** — the customer sends crypto to a PCX-generated wallet address; PCX converts it and disburses fiat to a registered beneficiary.

***

## 1. Virtual account crypto withdrawal

Withdraw from a PCX virtual account balance to an external crypto wallet.

```bash theme={null}
curl -X POST https://prod-api.pcxpay.com/v1/virtual-accounts/withdraw/crypto \
  -H "X-Api-Key: your_api_key" \
  -H "Authorization: NONE" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "va_xxxx",
    "amount": 100.00,
    "crypto_currency": "USDC",
    "crypto_network": "polygon",
    "wallet_address": "0xDestinationWalletAddress"
  }'
```

PCX debits `amount` from the virtual account's available balance and initiates the on-chain transfer to `wallet_address`. A `virtual_account.withdrawal` webhook fires once the transfer is submitted.

### Supported assets and networks

| Asset | Networks                |
| ----- | ----------------------- |
| USDC  | Ethereum, Polygon, Tron |
| USDT  | Ethereum, Polygon, Tron |
| ETH   | Ethereum                |
| BTC   | Bitcoin                 |

<Note>
  Choose Polygon or Tron for lower network fees on smaller USDC/USDT amounts. Ethereum offers the widest wallet compatibility but carries higher gas costs.
</Note>

***

## 2. Off-ramp (crypto → fiat)

The customer sends crypto to a PCX-generated deposit address. Once the on-chain transfer confirms, PCX converts the crypto to fiat and disburses to the beneficiary's bank or mobile money account.

### Step 1 — Fetch the exchange rate

Fetch the rate in the off-ramp direction. The `fromCurrency` is the fiat equivalent of the crypto asset (USDC → USD):

```bash theme={null}
GET /organizations/admin/exchange-rate
  ?orgId=org_xxxx
  &fromCurrency=USD
  &toCurrency=NGN
```

**Response:**

```json theme={null}
{
  "org_rate_id": "258b95aa-...",
  "from_currency": "USD",
  "to_currency": "NGN",
  "rate": 1620.5,
  "expires_at": "2026-06-04T12:00:00.000Z"
}
```

Store both `org_rate_id` and `rate` (the value you will pass as `client_rate`).

### Step 2 — Initiate the off-ramp

```bash theme={null}
curl -X POST https://prod-api.pcxpay.com/v1/payments-init/ramp \
  -H "X-Api-Key: your_api_key" \
  -H "Authorization: NONE" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "off_ramp",
    "user_id": "user_xxxx",
    "org_id": "org_xxxx",
    "fiat_currency": "NGN",
    "fiat_amount": 162050.00,
    "client_rate": 1620.5,
    "crypto_currency": "USDC",
    "crypto_network": "ethereum",
    "payment_method": "bank_transfer",
    "country": "NG",
    "payer_details": {
      "name": "Customer Name",
      "email": "customer@example.com"
    },
    "beneficiary_id": "ben_xxxx",
    "beneficiary_name": "Customer Name",
    "destination": {
      "accountNumber": "0123456789",
      "bankCode": "058",
      "bankName": "GTBank"
    }
  }'
```

### Step 3 — Handle the response

```json theme={null}
{
  "response": {
    "next_action": "send_crypto",
    "payment_instructions": {
      "type": "crypto_transfer",
      "wallet_address": "0xPCXDepositAddress",
      "crypto_amount": 100.00,
      "crypto_currency": "USDC",
      "crypto_network": "ethereum",
      "expires_at": "2026-06-04T11:34:34.128120Z"
    }
  }
}
```

Display the `wallet_address`, `crypto_amount`, and `crypto_network` to the customer. Once they send the exact amount to that address on-chain, PCX initiates the NGN bank transfer to the beneficiary automatically.

<Warning>
  The customer must send exactly `crypto_amount` of `crypto_currency` on `crypto_network`. Sending a different amount, wrong asset, or wrong network will delay or prevent the fiat payout to the beneficiary.
</Warning>

***

## Rate validation

PCX validates `client_rate` against the current live rate. If the rate has drifted more than **0.5%** since you fetched it, the request returns:

```json theme={null}
{
  "statusCode": 422,
  "error": "RATE_STALE",
  "message": "Rate has drifted. Please re-fetch the exchange rate."
}
```

Re-fetch the rate, present the updated value to the customer, and resubmit with the new `client_rate` and `org_rate_id`. Do not reuse `org_rate_id` values across sessions. See [Exchange Rates and Fees](/guides/settlement/exchange-rates-and-fees).

***

## Track status

```bash theme={null}
GET /payments/{payment_id}
```

Terminal statuses: `completed` (fiat successfully paid to beneficiary), `failed`, `canceled`.

Use [webhooks](/essentials/webhooks) to receive `payment.completed` in real time instead of polling.
