> ## 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.

# Exchange Rates and Fees

> How PCX manages exchange rates, rate locking, spread configs, and fee handling for cross-border payments

PCX applies exchange rates to all cross-currency flows. Rates are fetched per-transaction, locked at initiation, and validated at submission. Organizations can have custom rate configurations that control the spread applied on top of the underlying market rate.

***

## Fetching a rate

Before initiating any cross-border payment or ramp, fetch the rate for the corridor:

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

**Response:**

```json theme={null}
{
  "org_rate_id": "258b95aa-a76f-4144-ade1-23814a32900d",
  "from_currency": "NGN",
  "to_currency": "KES",
  "rate": 0.09127444176,
  "expires_at": "2026-06-04T12:00:00.000Z"
}
```

Store both `org_rate_id` and `rate`. Both are required when initiating a cross-border payment or ramp.

For ramp flows, the corridor direction must match the ramp direction:

| Ramp direction             | `fromCurrency`                        | `toCurrency`                                   |
| -------------------------- | ------------------------------------- | ---------------------------------------------- |
| `on_ramp` (fiat → crypto)  | Fiat currency (e.g. `NGN`)            | Crypto's fiat equivalent (e.g. `USD` for USDC) |
| `off_ramp` (crypto → fiat) | Crypto's fiat equivalent (e.g. `USD`) | Fiat currency (e.g. `NGN`)                     |

<Warning>
  Fetching the rate in the wrong direction for a ramp request will cause a `RATE_STALE` error. Always align the rate fetch direction with the ramp direction.
</Warning>

***

## Rate locking

When `org_rate_id` is included in a payment or ramp initiation, PCX locks that rate for the transaction. The payer sees the rate you fetched, and PCX guarantees the conversion at that rate regardless of market movement between fetch and settlement.

Rates expire at `expires_at`. Initiating a payment after expiry will fail — re-fetch the rate and retry promptly.

***

## Spread validation (ramp flows)

For on-ramp and off-ramp flows, you also submit `client_rate` — the rate you displayed to the user. PCX validates it against the current live rate within a **0.5% tolerance**:

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

When you receive `RATE_STALE`:

1. Re-fetch the rate from `GET /organizations/admin/exchange-rate`
2. Present the updated rate to the user for re-confirmation
3. Resubmit the ramp with the new `client_rate` and `org_rate_id`

Never cache `org_rate_id` values across sessions or reuse them after the user has been shown the rate.

***

## Org rate configurations

Organizations can have custom spreads applied on top of market rates. These are managed by platform administrators:

| Operation                   | Endpoint                                         |
| --------------------------- | ------------------------------------------------ |
| Add a rate config           | `POST /organizations/admin/add-rate-config`      |
| Get rate configs for an org | `GET /organizations/admin/rate-configs/{org_id}` |
| Update a rate config        | `PUT /organizations/admin/rate-configs/{org_id}` |
| Get active rate configs     | `GET /organizations/admin/active-rate-configs`   |

A **rate config** defines the spread percentage for a given currency pair. When a rate is fetched for an org, the active rate config for that pair is applied to produce the `org_rate_id` rate.

***

## Manual rate management

Administrators can manually set or override rates for specific organizations and corridors:

| Operation                      | Endpoint                                                  |
| ------------------------------ | --------------------------------------------------------- |
| Add a manual rate              | `POST /organizations/admin/add-rate`                      |
| Add an active rate with config | `POST /organizations/admin/add-active-rate`               |
| List all org rates             | `GET /organizations/admin/rates`                          |
| Get active rates               | `GET /organizations/admin/active-rates`                   |
| Deactivate a rate              | `PUT /organizations/admin/rates/{org_rate_id}/deactivate` |
| Delete a rate                  | `DELETE /organizations/admin/rates/{org_rate_id}`         |

***

## Fees

PCX does not charge fees as a separate line item on payments. Fees are embedded in the spread applied via the org's rate configuration — the rate the user sees already includes PCX's margin. The `rate` returned by `GET /organizations/admin/exchange-rate` is the spread-adjusted rate.

For a breakdown of fees collected across virtual account operations:

```bash theme={null}
GET /virtual-accounts/admin/analytics/fee-analysis
```

This endpoint is available to internal admins and provides aggregate fee data across all accounts.
