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

# Off-Ramp Webhook Events

> Detailed information on webhook events for Off-Ramp transactions.

## Introduction to Off-Ramp Webhooks

Ripio provides webhook notifications to keep your application informed about real-time status updates for Off-Ramp transactions. These events cover the process of converting cryptocurrency back into fiat currency and its subsequent withdrawal.

Similar to On-Ramp events, Off-Ramp webhooks are sent as POST requests with a JSON payload to your configured endpoint. It is crucial to validate the signature of every incoming webhook request as detailed in the [Webhooks Introduction](./introduction).

## Event Payload Structure

Each Off-Ramp event notification message contains a JSON-formatted payload with the following general structure:

```json theme={null}
{
  "eventType": "EVENT_TYPE_HERE",
  "issueDatetime": "YYYY-MM-DDTHH:MM:SS.ffffffZ",
  "transactionObject": {
    // Fields specific to the transaction and event type
  }
}
```

* `eventType` (String): The specific type of Off-Ramp event that occurred.
* `issueDatetime` (String): The Coordinated Universal Time (UTC) timestamp indicating when the event was triggered (e.g., `"2024-04-25T18:22:37Z"`).
* `transactionObject` (Object): An object containing details of the Off-Ramp transaction associated with the event.

## Off-Ramp Event Types

The following event types are supported for Off-Ramp transactions:

### `OFF-RAMP.DEPOSIT.RECEIVED`

* **Description:** Sent after Ripio successfully confirms a cryptocurrency deposit to the address associated with the user's active Off-Ramp session.
* **`transactionObject` typically includes:** Off-Ramp transaction fields (see below), including details of the crypto deposit.

### `OFF-RAMP.TRADE.COMPLETED`

* **Description:** Sent after Ripio successfully completes the trade operation, converting the deposited cryptocurrency into fiat currency.
* **`transactionObject` typically includes:** Off-Ramp transaction fields.

### `OFF-RAMP.WITHDRAWAL.PROCESSING`

* **Description:** Sent after Ripio initiates the fiat withdrawal process to the customer's designated fiat account linked to the active Off-Ramp session.
* **`transactionObject` typically includes:** Off-Ramp transaction fields.

### `OFF-RAMP.WITHDRAWAL.COMPLETED`

* **Description:** Sent upon successful completion and confirmation that the fiat withdrawal to the customer's account has been finalized.
* **`transactionObject` typically includes:** Off-Ramp transaction fields.

### `OFF-RAMP.ORDER.CANCELLED`

* **Description:** Sent if Ripio cancels the Off-Ramp order. This might occur if a customer breaches predefined transactional or periodic limits, or if the fiat payout could not be delivered to the destination account. `failureReason` says why it was cancelled, and `refundable` on the order says how it can be resolved:
  * `refundable: true` — the trade never executed, so the crypto is still there and can be returned by requesting a [refund](/ramps-api/refunds/overview) for the order. A completed refund arrives as `OFF-RAMP.ORDER.REFUNDED`; a rejected one emits nothing, so read the order's `latestRefund` for that.
  * `refundable: false` — the crypto was already sold and what failed was the fiat payout, so there is nothing to send back. Retry the payout against another destination account with [Change Fiat Account and Reprocess](/ramps-api/off-ramp/change-fiat-account). `failureReason: INVALID_DESTINATION` is the usual cause.
* **`transactionObject` typically includes:** Off-Ramp transaction fields.

### `OFF-RAMP.ORDER.REFUNDED`

* **Description:** Sent when a [crypto refund](/ramps-api/refunds/request-crypto-refund) of the order's deposit **completes** on-chain and the order moves to `REFUNDED`. It is the order-rooted refund event; a refund of a deposit that never had an order arrives as `OFF-RAMP.DEPOSIT.REFUNDED` instead. Either way, only the completion is announced: a refund that is registered, approved or **rejected** emits nothing, so poll `latestRefund` to follow those. The refund is not necessarily one you requested — `latestRefund.initiatedBy` says whether it came from you, from the end user in the widget, or from Ripio's support team.
* **`transactionObject` typically includes:** Off-Ramp transaction fields, with `status` as `REFUNDED` and `failureReason` / `failureDetail` carrying why the order failed in the first place. **The hash of the returned crypto is not in the payload** — read `refundedTxnHash` on the order.

### `OFF-RAMP.DEPOSIT.REJECTED`

* **Description:** Sent when a crypto deposit reaches one of your customers' deposit addresses but matches no off-ramp order, so no operation can be built from it. This only happens on the one-off order flow, where the deposit has to match an order exactly (address, amount, network, and an order still awaiting funds); a deposit into an off-ramp session's address always finds its order and never produces this event. The money is owed back to the customer, and once off-ramp refunds are enabled for your account you can return it yourself: the deposit shows up on [List Refundable Crypto Deposits](/ramps-api/refunds/list-refundable-crypto-deposits) — the only listing it appears on — and is refunded by `depositId`. Until then, and at any time, Ripio's support team can do it on the customer's behalf. Note this event is sent either way. This event is what tells you the money is there at all, so that you learn about it from your own integration rather than from the customer.
* **`transactionObject` typically includes:** deposit fields rather than order fields, because there is no order: `customerId`, `depositId`, `order` (`null`) and a `metadata` object with the deposit as the blockchain reported it — amount, currency, network and transaction hash.

### `OFF-RAMP.DEPOSIT.REFUNDED`

* **Description:** Sent when the [refund of an unmatched crypto deposit](/ramps-api/refunds/refund-crypto-deposit) **completes** on-chain. It is the deposit-rooted twin of `OFF-RAMP.ORDER.REFUNDED`, and it exists because this money never had an order to announce as `REFUNDED` — it follows a deposit that arrived as `OFF-RAMP.DEPOSIT.REJECTED`. Like the order-rooted event it reports only the completion: registering the refund, its approval and a **rejection** are silent, so read `latestRefund` on the deposit for those.
* **`transactionObject` typically includes:** the same deposit fields as `OFF-RAMP.DEPOSIT.REJECTED` — `customerId`, `depositId`, `order` (`null`) and `metadata`. Note that the hash in `metadata` is the one of the deposit that came **in**, not of the transfer that went back: **the hash of the returned crypto is not in this payload**, read `refundedTxnHash` on the deposit for it.

## Transaction Object Fields for Off-Ramp Events

The `transactionObject` for the order-rooted Off-Ramp events generally includes the following fields. The two deposit-rooted events — `OFF-RAMP.DEPOSIT.REJECTED` and `OFF-RAMP.DEPOSIT.REFUNDED` — carry a different shape, described with each of them above, because there is no order to report.

* `transactionId` (String): Unique identifier for the Off-Ramp transaction (often corresponds to the session ID).
* `createdAt` (String): Date and time the Off-Ramp session/transaction was created (UTC format).
* `fiatAccountId` (String): Unique identifier for the customer's fiat account associated with the session.
* `fromCurrency` (String): The source cryptocurrency being off-ramped (e.g., "USDC").
* `toCurrency` (String): The target fiat currency to be received (e.g., "ARS", "MXN").
* `amount` (String): The amount of the source cryptocurrency deposited by the customer.
* `chain` (String): The blockchain network used for the cryptocurrency deposit (e.g., "ETHEREUM").
* `address` (String): The cryptocurrency deposit address used by the customer for this Off-Ramp transaction.
* `status` (String): The current status of the Off-Ramp transaction.
* `txnHash` (String, optional): The transaction hash for the on-chain cryptocurrency deposit. This is typically present in `OFF-RAMP.DEPOSIT.RECEIVED` and subsequent events.
* `customerId` (String, optional): Unique identifier of the customer associated with the transaction.
* `failureReason` (String | null): Machine-readable reason the order failed or was cancelled. One of `INVALID_DESTINATION`, `EXPIRED_QUOTE`, `PROVIDER_REJECTED`, `PROVIDER_ERROR`, `REFUND_BY_PROVIDER`, `USER_BANNED`, `AMOUNT_LIMIT_EXCEEDED`, `KYC_NOT_APPROVED`, or `INTERNAL_ERROR`. Populated in the `OFF-RAMP.ORDER.CANCELLED` and `OFF-RAMP.ORDER.REFUNDED` events; null in all other events.
* `failureDetail` (String | null): Human-readable message that expands on `failureReason`, suitable for display to end customers. Null whenever `failureReason` is null.

*Note: The exact fields and their presence might vary slightly. Always inspect the payload of received webhooks.*

## Off-Ramp Event Flows

The following sequences illustrate the expected order of events in common Off-Ramp scenarios. As with On-Ramp events, your system should be prepared to handle webhooks that might arrive out of the typical order.

1. **Standard Successful Off-Ramp Transaction Flow:**
   `OFF-RAMP.DEPOSIT.RECEIVED` → `OFF-RAMP.TRADE.COMPLETED` → `OFF-RAMP.WITHDRAWAL.PROCESSING` → `OFF-RAMP.WITHDRAWAL.COMPLETED`
   * *Description:* This is the usual flow for a successful Off-Ramp transaction, starting from the confirmation of a cryptocurrency deposit, followed by its conversion to fiat, initiation of the fiat withdrawal, and finally, the completion of the withdrawal.

2. **Order Cancellation Flow (e.g., Due to Limit Breach):**
   `OFF-RAMP.DEPOSIT.RECEIVED` → `OFF-RAMP.ORDER.CANCELLED`
   * *Description:* This flow occurs when a cryptocurrency deposit is received, but the transaction is subsequently cancelled, for instance, if the customer exceeds transactional limits. The crypto can then be returned by requesting a [refund](/ramps-api/refunds/overview) for the order.

3. **Failed Payout and Reprocessing Flow:**
   `OFF-RAMP.DEPOSIT.RECEIVED` → `OFF-RAMP.TRADE.COMPLETED` → `OFF-RAMP.WITHDRAWAL.PROCESSING` → `OFF-RAMP.ORDER.CANCELLED` → *(you call [Change Fiat Account and Reprocess](/ramps-api/off-ramp/change-fiat-account))* → `OFF-RAMP.WITHDRAWAL.PROCESSING` → `OFF-RAMP.WITHDRAWAL.COMPLETED`
   * *Description:* The trade went through but the fiat could not be delivered, so the order was cancelled with `failureReason: INVALID_DESTINATION`. Pointing it at another destination account resumes it from the payout: the deposit and the trade are not replayed, so **no second `OFF-RAMP.DEPOSIT.RECEIVED` or `OFF-RAMP.TRADE.COMPLETED` arrives**. If the new destination fails as well, `OFF-RAMP.ORDER.CANCELLED` is sent again and the order can be reprocessed once more.

4. **Refund Flow:**
   `OFF-RAMP.DEPOSIT.RECEIVED` → `OFF-RAMP.ORDER.CANCELLED` → *(a refund is requested and approved)* → `OFF-RAMP.ORDER.REFUNDED`
   * *Description:* The order was cancelled before the trade, so the crypto is still there and can be returned. Nothing is emitted between the two events: registering the refund, its approval and a **rejection** are all silent, and the gap can last as long as the review takes — `latestRefund` on the order is what reports progress. `OFF-RAMP.ORDER.REFUNDED` means the crypto is back on-chain.

5. **Unmatched Deposit, returned:**
   `OFF-RAMP.DEPOSIT.REJECTED` → *(a refund is requested and approved)* → `OFF-RAMP.DEPOSIT.REFUNDED`
   * *Description:* Crypto arrived at one of your customers' deposit addresses with no order to apply it to. No order event can follow — there is no order to have a lifecycle — so the money is tracked by the deposit instead: it appears on [List Refundable Crypto Deposits](/ramps-api/refunds/list-refundable-crypto-deposits), you request its refund by `depositId`, and the completion arrives as `OFF-RAMP.DEPOSIT.REFUNDED`. Nothing is emitted in between, and the gap can last as long as the review takes — `latestRefund` on the deposit is what reports progress, including a rejection. Reachable only on the one-off order flow. If you do not act on it — or off-ramp refunds are not enabled for your account — Ripio's support team returns the money, and the same event announces it.

## Important Considerations

* **Webhook Delivery Order:** While these flows outline the logical progression, network latency or server processing can cause webhooks to arrive out of sequence. Design your webhook handlers to accommodate this.
* **Idempotency:** Ensure that processing the same webhook event multiple times does not lead to unintended side effects or data inconsistencies.
* **Error Management:** Implement robust error handling and logging in your webhook consumer.
* **Configuration:** For details on setting up your webhook endpoints within the Ripio partner system, please consult with the Ripio technical team.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.