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

# Travel Rule (Mexico)

> The counterparty declaration Mexican partner accounts must send with every on-ramp and off-ramp from November 30, 2026: what to ask the customer, where to send it, and what happens when it is missing.

## Overview

**This page applies only to partners whose Ripio account is in Mexico.** What decides it is your account's country, not the customer's. Accounts in Argentina, Brazil and Colombia are not affected by anything described here.

Article 17, section XVI of the LFPIORPI and **article 24 Bis 2 of Acuerdo 115/2026** require Ripio to record, for **every** operation with virtual assets and with no minimum amount, who is on the other side of it: the name and country of residence of the owner of the external wallet. Ripio keeps that record for ten years and hands it to the authorities (UIF/SAT) on request.

The customer is the one who knows whose wallet it is, so from **November 30, 2026** every on-ramp and off-ramp of a Mexican account has to carry a **counterparty declaration**: the `travelRule` object described below. Without it, the operation is not created, or the money does not move.

> [!NOTE]
>
> Partners on the [Ramps widget](https://docs.ripio.com/ramps-api/widget/overview.md) do not implement any of this: the widget asks the customer itself. See [Travel Rule (Mexico)](https://docs.ripio.com/ramps-api/widget/flows/travel-rule.md) in the widget docs.

## When it applies

Travel Rule is **enforced from November 30, 2026 at 00:00 Mexico City time** (`2026-11-30T06:00:00Z`) on every Mexican account, with no transition period.

**Until then, nothing is blocked**, but `travelRule` is already accepted and stored. Start sending it now: a declaration you send today still counts after November 30, so your existing customers do not hit an error on that day. This matters most for fixed CLABEs and fixed deposit addresses — see [Existing sessions](#existing-sessions).

## What to ask the customer

One question, about the wallet on the other side of the operation:

| Operation | The counterparty is the owner of… | Wallet |
| - | - | - |
| On-ramp (buy) | the wallet that **receives** the crypto | `depositAddress` |
| Off-ramp (sell) | the wallet the crypto is **sent from** | the customer's own external wallet |

* **"Is this wallet yours?" — yes.** Nothing else to ask. Ripio takes the name and country of residence from the customer's KYC.
* **No.** Ask for the owner's **full name** — a person or a company, in a single field — and their **country of residence**.

Ripio only checks the format: a name that is not empty and a valid country code. **The declaration is not verified**: you collect it from the customer in good faith, and you do not need to send any KYC data of the counterparty. Ripio does not exchange data with the counterparty's exchange or wallet provider either.

> [!TIP]
>
> Most customers send to and from their own wallet. Showing "the wallet is mine" **ticked by default**, and asking for name and country only when the customer unticks it, keeps the question to a single tap.

## The `travelRule` object

| Field | Type | Required | Description |
| - | - | :-: | - |
| `counterpartyIsSelf` | boolean | ✅ | `true` when the wallet belongs to the customer. |
| `fullName` | string, up to 255 characters | When `counterpartyIsSelf` is `false` | Full name of the wallet's owner. |
| `country` | string | When `counterpartyIsSelf` is `false` | Owner's country of residence, as an [ISO-3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code (`MX`, `US`, `AR`…). |

```json The wallet is the customer's theme={null}
{
  "travelRule": {
    "counterpartyIsSelf": true
  }
}
```

```json The wallet belongs to someone else theme={null}
{
  "travelRule": {
    "counterpartyIsSelf": false,
    "fullName": "María Pérez López",
    "country": "MX"
  }
}
```

With `counterpartyIsSelf: true`, `fullName` and `country` are not stored, but they are still validated if you send them: leave them out. `country` is case-insensitive and trimmed. A malformed `travelRule` is rejected with `20000` on any account, even where Travel Rule does not apply:

```json theme={null}
{
  "code": 20000,
  "type": "ValidationError",
  "detail": {
    "travelRule": {
      "country": [
        {
          "message": "'MEX' is not a valid country. Use the ISO-3166-1 alpha-2 code (e.g. MX).",
          "code": "invalid_choice"
        }
      ]
    }
  },
  "status": 400
}
```

## Where to send it

`travelRule` goes in the body of the four creation endpoints. From November 30, each one requires it as follows:

| Endpoint | When `travelRule` is required |
| - | - |
| [Create On-Ramp Order](https://docs.ripio.com/ramps-api/on-ramp/create-on-ramp-order.md) `POST /api/v1/onramp/` | Unless the customer already has a current declaration for that same `depositAddress`. |
| [Create On-Ramp Session](https://docs.ripio.com/ramps-api/on-ramp/create-on-ramp-session.md) `POST /api/v1/onrampSession/` | Unless the customer already has a current declaration for that same `depositAddress`. |
| [Create Off-Ramp Order](https://docs.ripio.com/ramps-api/off-ramp/create-off-ramp-order.md) `POST /api/v1/offramp/` | **Always.** Every off-ramp order carries its own. |
| [Create Off-Ramp Session](https://docs.ripio.com/ramps-api/off-ramp/create-off-ramp-session.md) `POST /api/v1/offrampSession/` | Unless the customer's session already has a current declaration. |

Sending it when it is not required is never an error: it replaces the current declaration if it changed, and does nothing if it is the same. **When in doubt, send it.**

### How long a declaration lasts

A declaration covers every later operation with the same key, until the customer declares something else:

* **On-ramp:** the customer **and the wallet**. A declaration made for `0xAbc…` — through an order or a session — covers every later purchase to `0xAbc…`, including every deposit into a fixed CLABE pointing to it. A different `depositAddress` needs its own. EVM addresses are compared case-insensitively.
* **Off-ramp session:** the session. One declaration covers every deposit into the customer's fixed deposit addresses.
* **Off-ramp order:** that order only.

### Adding or changing it on a session

There is no separate endpoint: **post the session again with `travelRule`**. A customer has one session per direction, so posting it again updates that same session: the CLABE and the deposit addresses stay the same and operations already in progress are not touched.

Send the same body you used to create it, plus `travelRule`. The request goes through the same checks as a new session, and whatever you send replaces the session's parameters: a different `depositAddress`, for example, points the session — and the declaration — at the new wallet.

A new declaration applies to the operations that come **after** it. Each completed operation keeps the declaration it was made under, so changing it later does not rewrite history.

## When it is missing

> [!WARNING]
>
> **If you integrate only through the API, Ripio does not contact your customers.** When a declaration is missing, the `20108` and the [webhooks](#webhooks) below are the only notice: asking the customer and sending `travelRule` is up to you.

### At creation: `20108`

When Travel Rule is enforced and the request has no `travelRule` and no current declaration to fall back on, the four endpoints above answer `400` with code `20108`:

```json theme={null}
{
  "code": 20108,
  "type": "TravelRuleRequiredException",
  "detail": {
    "message": "This operation requires a Travel Rule declaration: send travelRule."
  },
  "status": 400
}
```

Ask the customer who owns the wallet, add `travelRule` and retry the same request. When a customer is missing more than one step, Terms & Conditions (`20039`) and KYC are reported first. The opposite does not hold: `20108` is checked before the deposit account, the destination account and the payment method, so the retry can still fail on one of those.

### On a fixed CLABE: the deposit is returned

A fixed CLABE takes deposits without asking anything first, so a deposit can arrive for a wallet nobody declared — typically on a session created before November 30. **That purchase is not made**: the order is cancelled and the MXN are returned to the account they came from. It happens on **every** such deposit, from the first one, until the customer declares the wallet.

You receive:

`ON-RAMP.DEPOSIT.RECEIVED` → `ON-RAMP.TRAVEL_RULE.REQUIRED` → `ON-RAMP.ORDER.CANCELLED` (`failureReason: TRAVEL_RULE_REQUIRED`) → `ON-RAMP.ORDER.REFUNDED`

Post the session again with `travelRule` and the next deposits go through. A purchase that was already cancelled is not resumed: the customer has to transfer again.

The same happens to an on-ramp **order** created before November 30 without `travelRule` and paid after it. Any declaration for that customer and `depositAddress` covers it — posting the session again, or a new order to the same wallet — as long as it arrives before the payment.

> [!NOTE]
>
> The return follows the usual limits on automatic refunds. One that cannot be made automatically is handled by Ripio's operations team: it takes longer, and `ON-RAMP.ORDER.REFUNDED` arrives when it completes.

### On a fixed deposit address: the sale is held

A sale is never returned automatically: the crypto is **held**. The order stays `PENDING`, nothing is sold and nothing is paid out.

You receive **only** `OFF-RAMP.TRAVEL_RULE.REQUIRED`, once. `OFF-RAMP.DEPOSIT.RECEIVED` is not sent until the sale is released, and nothing on the order marks it as held: it reads `PENDING` with a null `failureReason`. React to the webhook.

To release it, **post the session again with `travelRule`**. The held sales of that session resume right away: they are sold at the market price at that moment, as any session sale, and the usual events follow (`OFF-RAMP.DEPOSIT.RECEIVED` → `OFF-RAMP.TRADE.COMPLETED` …).

> [!WARNING]
>
> **While a customer has a held sale, every new operation that does not bring its own `travelRule` is rejected with `20108`** — buys and sells, orders and sessions, even when a declaration is already on file. Releasing the held sale clears it.

An off-ramp order created through the API always carries its own declaration from November 30, so it is never held. The exception is an order created **before** that date without `travelRule` whose crypto arrives after it: there is no API call to declare on an existing order, so contact Ripio support to resolve it. Until then, the warning above applies: every new operation of that customer needs its own `travelRule`.

## Existing sessions

Fixed CLABEs and fixed deposit addresses created before November 30 have no declaration unless you sent one. **Before November 30, post every active session again with `travelRule`**: it takes one request per customer, does not change the CLABE or the addresses, and avoids returned deposits and held sales from day one.

* **On-ramp sessions** need the declaration for the session's `depositAddress`. If the customer already declared that wallet on an earlier order, it is already covered.
* **Off-ramp sessions** need one declaration per session.

You can check what is on file before posting: see [Reading the declaration](#reading-the-declaration).

## Reading the declaration

The **current** declaration for an operation's key — the wallet on an on-ramp, the session or the order on an off-ramp — is returned as `travelRule`, `null` when there is none. It is what covers the next operations, not necessarily the one in force when an older order was processed. It is returned by:

* [Create On-Ramp Order](https://docs.ripio.com/ramps-api/on-ramp/create-on-ramp-order.md) (inside `transaction`) and [Retrieve an On-Ramp Order](https://docs.ripio.com/ramps-api/on-ramp/retrieve-an-on-ramp-order.md)
* [Create On-Ramp Session](https://docs.ripio.com/ramps-api/on-ramp/create-on-ramp-session.md) and [Retrieve an On-Ramp Session](https://docs.ripio.com/ramps-api/on-ramp/retrieve-an-on-ramp-session.md)
* [Retrieve a Customer's On-Ramp Session](https://docs.ripio.com/ramps-api/on-ramp/retrieve-a-customer-on-ramp-session.md)
* [Create Off-Ramp Session](https://docs.ripio.com/ramps-api/off-ramp/create-off-ramp-session.md) and [Retrieve an Off-Ramp Session](https://docs.ripio.com/ramps-api/off-ramp/retrieve-an-off-ramp-session.md)
* [Create Off-Ramp Order](https://docs.ripio.com/ramps-api/off-ramp/create-off-ramp-order.md) and [Retrieve an Off-Ramp Order](https://docs.ripio.com/ramps-api/off-ramp/retrieve-an-off-ramp-order.md)

```json theme={null}
"travelRule": {
  "counterpartyIsSelf": false,
  "fullName": "María Pérez López",
  "country": "MX"
}
```

With `counterpartyIsSelf: true`, `fullName` and `country` are `null`. List endpoints do not include it.

## Webhooks

| Event | When | What to do |
| - | - | - |
| `ON-RAMP.TRAVEL_RULE.REQUIRED` | A deposit arrived on a fixed CLABE for an undeclared wallet. `ON-RAMP.ORDER.CANCELLED` and the refund follow. | Ask the customer and post the session again with `travelRule`. |
| `OFF-RAMP.TRAVEL_RULE.REQUIRED` | A sale is held for its declaration. | Ask the customer and post the session again with `travelRule`. |

Both carry the standard transaction object of their [on-ramp](https://docs.ripio.com/ramps-api/webhooks/on-ramp-events.md) or [off-ramp](https://docs.ripio.com/ramps-api/webhooks/off-ramp-events.md) events and are sent once per order. A purchase cancelled for this reason has `failureReason: TRAVEL_RULE_REQUIRED` in its webhooks and in its detail. A held sale has no such field: the webhook is the only signal.

## Testing in sandbox

Ask the Ripio team to enforce Travel Rule on your **sandbox** account ahead of the date. Until then, `travelRule` is accepted in sandbox but nothing is blocked.

Once enforced, you can go through every case on this page: `20108` on a request without a declaration, a session posted again with `travelRule`, a deposit on an undeclared fixed CLABE that is returned, and a held sale released by its declaration.

## Errors

| Code | Type | Status | Where | Meaning |
| - | - | :-: | - | - |
| `20108` | `TravelRuleRequiredException` | 400 | Create order / session | No declaration of the counterparty, or the customer has a held sale. Send `travelRule` and retry. |
| `20000` | `ValidationError` | 400 | Create order / session | `travelRule` is malformed: see `detail.travelRule`. |


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