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

# Refunds

> How to return the money of an on-ramp or off-ramp operation that could not be completed: when a refund can be requested, where the money goes, and how to follow its outcome.

When an operation cannot be completed — a buy that could not be executed, a deposit that never matched an order — the money the customer already sent has to go back. A refund is requested per operation and returns **the deposit**: fiat on an on-ramp, crypto on an off-ramp.

<Warning>
  Refunds are an **opt-in feature that must be enabled for your account by the Ripio team**, separately for each ramp. While a ramp is disabled, its refund endpoints return `403 Forbidden` — error code `20056` (`FiatRefundNotEnabledException`) for on-ramp, `20072` (`CryptoRefundNotEnabledException`) for off-ramp — and `refundable` is always `false`. [List Refundable Deposits](/ramps-api/refunds/list-refundable-deposits) is affected too: while on-ramp refunds are disabled it returns no claimable deposits, so an empty page there does not mean the customer has none.
</Warning>

## Deciding whether to offer a refund

Every on-ramp and off-ramp order carries two read-only fields that answer the question before you build the request:

| Field                       | Meaning                                                                                                                                                                                                                                                                             |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `refundable`                | Whether a refund can be requested for this operation **right now**. Computed per request, so use it to decide whether to call the endpoint instead of discovering it through a `400`.                                                                                               |
| `refundDestinationRequired` | Whether the request must name a destination. `true` → send `fiatAccountId` (on-ramp) or `refundAddress` (off-ramp). `false` → the money returns to where it came from and the field is rejected. `null` → the operation is not in refund territory, so the question does not apply. |

The destination rule depends on the payment provider and, for crypto, on your account's configuration:

| Ramp     | Country                   | Where the money goes                                          | `refundDestinationRequired`           |
| -------- | ------------------------- | ------------------------------------------------------------- | ------------------------------------- |
| On-ramp  | Argentina, Mexico, Brazil | The account the customer paid from                            | `false`                               |
| On-ramp  | Colombia                  | A destination account you name                                | `true`                                |
| Off-ramp | All                       | The deposit's on-chain origin address, or an address you name | Depends on your account's refund mode |

`refundDestinationRequired` is the field to branch on — it is answered per operation, so you never have to keep your own table of countries and providers.

## Requesting a refund

| What you are refunding       | Endpoint                                                                                                       |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------- |
| A cancelled on-ramp order    | [`POST /customers/{customerId}/transactions/onramp/{orderId}/refund/`](/ramps-api/refunds/request-fiat-refund) |
| A fiat deposit with no order | [`POST /customers/{customerId}/refundableDeposits/{depositUuid}/refund/`](/ramps-api/refunds/refund-deposit)   |
| A failed off-ramp order      | [`POST /offramp/{orderId}/refund/`](/ramps-api/refunds/request-crypto-refund)                                  |

<Note>
  A cancelled off-ramp order whose **trade already executed** is not one of these: the crypto was sold, so there is no deposit left to send back and `refundable` is `false`. What failed there is the fiat payout, and the way out is to retry it against another destination account — see [Change Fiat Account and Reprocess](/ramps-api/off-ramp/change-fiat-account). Refunding and reprocessing never apply to the same order.
</Note>

Requests are idempotent: `202` means the refund was registered, `200` means one already existed for that deposit and nothing new was created.

A fiat deposit that arrives but never matches an on-ramp order has no order to hang the refund off. Those are listed by [List Refundable Deposits](/ramps-api/refunds/list-refundable-deposits) and refunded by `depositUuid`.

That listing answers two different questions, and `includeRequested` picks which one:

| Call                                                                    | Answers                           | What comes back                                                                                    |
| ----------------------------------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------- |
| `GET /customers/{customerId}/refundableDeposits/`                       | "What can this customer claim?"   | Only the deposits a new refund can be requested for: no refund yet, or a rejected one.             |
| `GET /customers/{customerId}/refundableDeposits/?includeRequested=true` | "Where is this customer's money?" | The same deposits, plus the ones whose refund was already requested and has not been credited yet. |

Ask for the wider list when you show the customer their own payments. Approval is not instant, so with the default list a deposit disappears from the only place it was visible right after the customer asks for it back; with `includeRequested=true` it stays, and `latestRefund` says what is happening to it. A deposit leaves that list once its refund reaches `COMPLETED` — the money is back in the customer's account.

The listing has no `refundable` field, so in the wider mode a deposit being there no longer means it can be claimed. `latestRefund` is what tells them apart: `null` or `REJECTED` when a refund can still be requested, any other state when one is already under way.

The reverse also holds while on-ramp refunds are not enabled for your account: a deposit **not** being there does not mean it does not exist. Neither call offers a deposit you could not refund anyway, so the claimable half is left out of both. What `?includeRequested=true` still returns is any deposit whose refund Ripio support already started for the customer — that money is moving, and the customer's screen has to be able to say so.

## Following the outcome

The `latestRefund` block on the order (and on each refundable deposit) carries the state of its most recent refund, or `null` if there never was one:

| Status                 | Meaning                                                             |
| ---------------------- | ------------------------------------------------------------------- |
| `PENDING`              | Registered, not yet sent to the payment provider or the blockchain. |
| `PROCESSING`           | Sent, awaiting confirmation.                                        |
| `COMPLETED`            | The money reached its destination.                                  |
| `REJECTED`             | The request was declined. Nothing was sent — see `rejectionReason`. |
| `FAILED` / `CANCELLED` | The attempt did not go through. Contact Ripio support.              |

`rejectionReason` is empty unless the refund was `REJECTED`:

| Code                  | Meaning                                                      |
| --------------------- | ------------------------------------------------------------ |
| `DUPLICATE_REQUEST`   | The same refund was already requested.                       |
| `INVALID_DESTINATION` | The destination account or address cannot receive the money. |
| `ALREADY_RESOLVED`    | The operation was already resolved by other means.           |
| `NOT_ELIGIBLE`        | The operation does not qualify for a refund.                 |
| `CONTACT_SUPPORT`     | Contact Ripio's support team for the detail.                 |

A rejection is not final: nothing was paid out, so `refundable` stays `true` and the refund can be requested again.

`initiatedBy` tells you who originated it, which matters because **a refund is not necessarily one you asked for** — Ripio may return a deposit on its own after a failed order, or register one on the customer's behalf. On-ramp: `PARTNER`, `SYSTEM` or `OPS`. Off-ramp: `PARTNER`, `WIDGET` (the end user, from the widget) or `OPS`.

<Note>
  A refund emits an event only when it **completes**: `ON-RAMP.ORDER.REFUNDED` and `ON-RAMP.DEPOSIT.REFUNDED` on the on-ramp, [`OFF-RAMP.ORDER.REFUNDED`](/ramps-api/webhooks/off-ramp-events) on the off-ramp. Every other transition is silent — in particular a **rejection is never an event on either ramp**, so `latestRefund` is the only way to learn about one. Poll it if you need to know that a refund was declined.

  The off-ramp event carries no hash of the returned crypto: read `refundedTxnHash` on the order for that.
</Note>
