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

# Example gateway

# Gateway API — Flow Examples

All endpoints require authentication via **API key and signature**.

> **Migrating from a previous version?** See the
> [Migration Guide](https://statics.ripio.com/docs/api/migration-guide.html) for a
> breakdown of what changed (rails/transaction-type vocabulary, renamed paths, response
> shapes) and the deadlines for deprecated endpoints.

***

## Response structure

All endpoints return the same flat, client-friendly envelope:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {}
}
```

* On **success**: `error_code` and `message` are `null`; `data` holds the payload.
* On **error**: `error_code` and `message` are populated; `data` is `null` (or contains field-level details when applicable).

List endpoints embed `pagination` inside `data` alongside `results` (or a custom wrapper key):

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "results": [ ... ],
    "pagination": {
      "count": 94,
      "next": "https://api.ripio.com/wallet/transactions/?page=2",
      "previous": null,
      "per_page": 20,
      "total_pages": 5
    }
  }
}
```

Cursor-paginated endpoints (see high-volume flows) return `nc` / `pc` instead of `pagination`:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "results": [ ... ],
    "nc": "cD0yMDI2LTAyLTI0KzAw...",
    "pc": null
  }
}
```

> `nc` = next cursor, `pc` = previous cursor. These are opaque base64 tokens — the client must pass them back verbatim via `?cursor=<token>` without parsing them.

***

## Flow 1: Crypto Send

To perform a crypto send you need the `network` and the `network_fee` for the currency being sent. Both are retrieved from the networks endpoint.

### Step 1 — Fetch available networks for the currency

**Required permission:** `general_data_read`

```
GET /network/currency-networks/USDT/
```

Response:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": [
    {
      "currency": "USDT",
      "currency_balance_id": 14,
      "network": {
        "id": 3,
        "code": "ethereum",
        "name": "Ethereum",
        "status_tag": null,
        "deliver_time": "~10 min",
        "enabled": true,
        "use_memo": false
      },
      "native_network": false,
      "standard": "ERC20",
      "network_standard": "ERC20",
      "fee": "2.50",
      "fee_tag": null,
      "send": true,
      "receive": true,
      "order": 1,
      "enabled": true,
      "min_amount": "10.00",
      "max_amount": null,
      "is_partial_disabled_send": false,
      "is_partial_disabled_receive": false,
      "messages": []
    },
    {
      "currency": "USDT",
      "currency_balance_id": 14,
      "network": {
        "id": 7,
        "code": "tron",
        "name": "Tron (TRC20)",
        "status_tag": null,
        "deliver_time": "~5 min",
        "enabled": true,
        "use_memo": false
      },
      "native_network": false,
      "standard": "TRC20",
      "network_standard": "TRC20",
      "fee": "1.00",
      "fee_tag": null,
      "send": true,
      "receive": true,
      "order": 2,
      "enabled": true,
      "min_amount": "5.00",
      "max_amount": null,
      "is_partial_disabled_send": false,
      "is_partial_disabled_receive": false,
      "messages": []
    }
  ]
}
```

Relevant fields to extract:

* `network.code` → value to use in `gateway_data.network` of the send request
* `fee` → value to use in `network_fee` of the send request
* `send: true` → confirms that sending is enabled for that network
* `network.use_memo` → indicates whether a `memo_id` must be included

***

### Step 2 (optional) — Verify the crypto rail is enabled

**Required permission:** `general_data_read`

```
GET /transactions/rails/?rail=crypto&transaction_type=withdrawal
```

> The legacy `GET /transactions/gateways/` still works as a deprecated alias returning the same response. `swap` is now grouped under `rail: "ripio"`.

Response:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": [
    {
      "rail": "crypto",
      "name": "Crypto",
      "order": 1,
      "transaction_types": [
        {
          "transaction_type": "withdrawal",
          "enabled": true,
          "account_enabled": true,
          "fee": "0.00",
          "fixed_fee": "0.00",
          "custom_fee_currencies": {},
          "taxes": [],
          "deliver_time": "~10 min",
          "blocked": false
        }
      ]
    }
  ]
}
```

Verify that `enabled: true`, `account_enabled: true`, and `blocked: false`.

***

### Step 3 — Execute the send

**Required permission:** `withdrawal_write`

```
POST /transactions/crypto/withdrawal/
```

> The legacy `POST /transactions/crypto/send/` still works as a deprecated alias until August 15, 2026.

Body (using data from Step 1 — Tron network):

```json theme={null}
{
  "amount": "50.00",
  "currency": "USDT",
  "address_to": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
  "gateway": "crypto",
  "network_fee": "1.00",
  "gateway_data": {
    "network": "tron",
    "memo_id": null
  },
  "otp_code": "123456"
}
```

> **Note on OTP code**: `otp_code` parameter is NOT needed for send to a whitelisted address. Addresses can be whitelisted from Ripio App. Public endpoint to whitelist addressess and bank accounts is coming soon.

> **Note on network fee**: Although the backend validates `network_fee`, the client must send it explicitly (retrieved from the network endpoint). This allows knowing the total cost of the operation (`amount` + `network_fee`) upfront before confirming it. Without this value, the backend would deduct the fee from the sent amount and the recipient would receive less than expected. The `network_fee` value is honored as long as the transaction is accepted.

Response (201 Created):

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "id": 7823941,
    "external_id": null,
    "transaction_type": "withdrawal",
    "rail": "crypto",
    "status": "PEN",
    "gateway_details": "tron",
    "transaction_hash": null,
    "from_currency": "USDT",
    "to_currency": "USDT",
    "fiat_currency": "ARS",
    "fee_currency": "USDT",
    "fee": "1.00",
    "fee_fiat": "1050.00",
    "rate": "1.00",
    "rate_usd": "1.00",
    "amount_from": "50.00",
    "amount_to": "50.00",
    "amount_fiat": "52500.00",
    "amount_usd": "50.00",
    "origin": null,
    "destination": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
    "tax": "0.00",
    "tax_currency": null,
    "created_at": "2026-04-16T14:32:00Z",
    "updated_at": "2026-04-16T14:32:00Z",
    "extra_data": {}
  }
}
```

> `transaction_hash` is `null` until the withdrawal is broadcast on-chain; once broadcast it holds the on-chain hash.

> **Note on memo networks:** For networks that use a memo such as XRP (`use_memo: true`), `memo_id` must be included in `gateway_data`.

**Business rule errors:**

| `error_code` | Condition                                                                     |
| ------------ | ----------------------------------------------------------------------------- |
| `GTW_0404`   | Gateway `crypto` is disabled or not enabled for the account                   |
| `CUR_0403`   | Currency is disabled or not available for sending                             |
| `NET_0400`   | `gateway_data.network` value is unknown                                       |
| `NET_0401`   | Network exists but send is currently disabled for that currency               |
| `ADR_0400`   | Address is invalid for the selected network (includes sending to own address) |
| `ADR_0401`   | Destination address is blacklisted                                            |
| `ADR_0402`   | Address flagged as high risk by compliance                                    |
| `FEE_0401`   | `network_fee` is below the accepted minimum for the current conditions        |
| `TXN_0401`   | Amount is below the network minimum or above the network maximum              |
| `OTP_0401`   | OTP code required but not provided (send above amount threshold)              |
| `OTP_0400`   | OTP code is incorrect                                                         |

> When there are errors in **multiple fields**, `error_code`/`message` surface the first one (useful for a toast notification) and `data` preserves the full structure for mapping errors to each form input.

***

## Flow 2: CVU Withdrawal (Argentina)

Only available for accounts with `country: AR` and an assigned CVU. Withdraws ARS to a previously verified CVU/CBU bank account.

### Step 1 — Fetch gateway limits

**Required permission:** `general_data_read`

```
GET /transactions/limits/?rail=bank&transaction_type=withdrawal
```

Response:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": [
    {
      "rail": "bank",
      "transaction_types": [
        {
          "transaction_type": "withdrawal",
          "currency": "ARS",
          "currency_balance_id": 1,
          "limits": {
            "min_amount": "500.00",
            "max_amount": "500000.00",
            "daily_amount": "500000.00",
            "daily_qty": 5,
            "monthly_amount": "5000000.00",
            "monthly_qty": 50,
            "annual_amount": null,
            "annual_qty": null
          },
          "remaining": {
            "daily_amount": "350000.00",
            "daily_qty": 4,
            "monthly_amount": "4500000.00",
            "monthly_qty": 48,
            "annual_amount": null,
            "annual_qty": null
          }
        }
      ]
    }
  ]
}
```

> An unlimited period is returned as `null` (e.g. `annual_amount` above), never the internal `-1`. `currency` is never `null`: it is the user's fiat (or `USDT` for `swap`).

Verify that the amount to withdraw is between `min_amount` and `max_amount`, and that `remaining.daily_amount` is sufficient.

### Step 2 — Execute the CVU withdrawal

**Required permission:** `withdrawal_write`

```
POST /transactions/bank/withdrawal/
```

> The legacy `POST /transactions/bank-transfer-cvu/withdrawal/` still works as a deprecated alias until August 15, 2026.

Body:

```json theme={null}
{
  "amount": "10000.00",
  "currency": "ARS",
  "gateway_data": {
    "bank_account": "un.alias.ejemplo"
  },
  "otp_code": "123456"
}
```

> `bank_account` can be an alias, CBU, or external CVU as the destination.

> **Note on OTP code**: `otp_code` parameter is only needed for transfers to third party accounts. Endpoint to whitelist (no need for OTP) bank accounts coming soon.

Response (201 Created):

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "id": 7823950,
    "external_id": null,
    "transaction_type": "withdrawal",
    "rail": "bank",
    "status": "PEN",
    "gateway_details": null,
    "transaction_hash": null,
    "from_currency": "ARS",
    "to_currency": "ARS",
    "fiat_currency": "ARS",
    "fee_currency": "ARS",
    "fee": "0.00",
    "fee_fiat": "0.00",
    "rate": "1.00",
    "rate_usd": "0.00095",
    "amount_from": "10000.00",
    "amount_to": "10000.00",
    "amount_fiat": "10000.00",
    "amount_usd": "9.50",
    "origin": null,
    "destination": "0720461088000000012345",
    "tax": "0.00",
    "tax_currency": null,
    "created_at": "2026-04-16T15:00:00Z",
    "updated_at": "2026-04-16T15:00:00Z",
    "extra_data": {
      "provider_reference": "AB12CD34EF56GH78IJ90KL",
      "bank_account": "0720461088000000012345",
      "bank_name": "Example Bank"
    }
  }
}
```

> `extra_data` only exposes operational keys (`provider_reference`, `bank_account`, `bank_name`). No personal data is included.

**Business rule errors:**

| `error_code` | Condition                                                                   |
| ------------ | --------------------------------------------------------------------------- |
| `TXN_0500`   | Account country is not `AR`                                                 |
| `GTW_0404`   | CVU gateway is disabled                                                     |
| `TXN_0450`   | `bank_account` not provided in `gateway_data`                               |
| `CVU_1000`   | Account does not have a Ripio CVU                                           |
| `CVU_1006`   | Account's CVU is temporarily blocked                                        |
| `CVU_1005`   | `bank_account` format is invalid, or destination account is not in ARS      |
| `OTP_0401`   | OTP required but not provided (third-party transfer above amount threshold) |
| `OTP_0400`   | OTP code is incorrect                                                       |

***

## Flow 3: PIX Withdrawal (Brazil)

Only available for accounts with `country: BR`. Withdraws BRL using a destination PIX key.

### Step 1 — Fetch gateway limits

**Required permission:** `general_data_read`

```
GET /transactions/limits/?rail=pix&transaction_type=withdrawal
```

Response:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": [
    {
      "rail": "pix",
      "transaction_types": [
        {
          "transaction_type": "withdrawal",
          "currency": "BRL",
          "currency_balance_id": 2,
          "limits": {
            "min_amount": "10.00",
            "max_amount": "20000.00",
            "daily_amount": "20000.00",
            "daily_qty": 10,
            "monthly_amount": "100000.00",
            "monthly_qty": 100,
            "annual_amount": null,
            "annual_qty": null
          },
          "remaining": {
            "daily_amount": "18500.00",
            "daily_qty": 9,
            "monthly_amount": "95000.00",
            "monthly_qty": 98,
            "annual_amount": null,
            "annual_qty": null
          }
        }
      ]
    }
  ]
}
```

### Step 2 — Execute the PIX withdrawal

**Required permission:** `withdrawal_write`

```
POST /transactions/pix/withdrawal/
```

Body:

```json theme={null}
{
  "amount": "500.00",
  "currency": "BRL",
  "gateway_data": {
    "pix_key": "joao.silva@email.com"
  },
  "otp_code": 123456
}
```

> **Note on OTP code**: `otp_code` is only required for transfers to third-party PIX keys above the configured amount threshold. For PIX keys registered to the user's own account, it can be omitted.

Valid `pix_key` examples:

* CPF: `"123.456.789-00"` or `"12345678900"`
* Email: `"joao.silva@email.com"`
* Phone: `"+5511999990000"`
* Random key (EVP): `"e5f3b2a1-4c6d-11ed-bdc3-0242ac120002"`

Response (201 Created):

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "id": 7823960,
    "external_id": null,
    "transaction_type": "withdrawal",
    "rail": "pix",
    "status": "PEN",
    "gateway_details": null,
    "transaction_hash": null,
    "from_currency": "BRL",
    "to_currency": "BRL",
    "fiat_currency": "BRL",
    "fee_currency": "BRL",
    "fee": "0.00",
    "fee_fiat": "0.00",
    "rate": "1.00",
    "rate_usd": "0.19",
    "amount_from": "500.00",
    "amount_to": "500.00",
    "amount_fiat": "500.00",
    "amount_usd": "95.00",
    "origin": null,
    "destination": "joao.silva@email.com",
    "tax": "0.00",
    "tax_currency": null,
    "created_at": "2026-04-16T15:10:00Z",
    "updated_at": "2026-04-16T15:10:00Z",
    "extra_data": {}
  }
}
```

**Business rule errors:**

| `error_code` | Condition                                                    |
| ------------ | ------------------------------------------------------------ |
| `TXN_0500`   | Account country is not `BR`                                  |
| `GTW_0404`   | PIX gateway is disabled                                      |
| `TXN_0450`   | `pix_key` not provided in `gateway_data`                     |
| `PIX_0400`   | PIX key is registered in the account but is disabled         |
| `PIX_0401`   | PIX recipient bank account is inactive                       |
| `PIX_0402`   | PIX key ownership data does not match the expected recipient |
| `PIX_0500`   | External PIX provider returned an error                      |

***

## Flow 4: Transaction queries

### List transactions with filters

**Required permission:** `general_data_read`

```
GET /transactions/?rail=crypto&currency=USDT&ordering=-date_created
```

> Each item is the full transaction body (same shape as the create response and the
> detail endpoint). Fields are abbreviated below for readability.

Response (cursor pagination):

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "results": [
      {
        "id": 7823941,
        "external_id": null,
        "transaction_type": "withdrawal",
        "rail": "crypto",
        "status": "PEN",
        "gateway_details": "tron",
        "transaction_hash": null,
        "from_currency": "USDT",
        "amount_from": "50.00",
        "amount_to": "50.00",
        "created_at": "2026-04-16T14:32:00Z",
        "updated_at": "2026-04-16T14:32:00Z"
      }
    ],
    "nc": "cD0yMDI2LTA0LTE2KzE0JTNBMzIlM0EwMC4wMDAwMDAlMkIwMCUzQTAw",
    "pc": null
  }
}
```

> The `nc` and `pc` tokens are **opaque** (URL-safe base64). The client passes them back verbatim via `?cursor=<token>`. Do not attempt to parse or modify them.

Next page:

```
GET /transactions/?cursor=cD0yMDI2LTA0LTE2KzE0JTNBMzIlM0EwMC4wMDAwMDAlMkIwMCUzQTAw
```

Available query parameters:

| Parameter           | Type   | Description                                                                   |
| ------------------- | ------ | ----------------------------------------------------------------------------- |
| `rail`              | enum   | Filter by rail: `bank`, `pix`, `crypto`, `ripio` (`ripio` also returns swaps) |
| `transaction_type`  | enum   | Filter by type: `deposit`, `withdrawal`, `swap`                               |
| `currency`          | string | Filter by currency (matches both `currency` and `crypto_currency`)            |
| `payment_method_id` | int    | Filter by payment method ID                                                   |
| `ordering`          | enum   | `date_created` or `-date_created` (default)                                   |
| `cursor`            | string | Opaque token returned in `nc`/`pc` from the previous response                 |

***

### Fetch a transaction detail

```
GET /transactions/7823941/
```

Response (same rich body as the create response):

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": {
    "id": 7823941,
    "external_id": null,
    "transaction_type": "withdrawal",
    "rail": "crypto",
    "status": "PEN",
    "gateway_details": "tron",
    "transaction_hash": null,
    "...": "..."
  }
}
```

When the transaction does not exist:

```json theme={null}
{ "error_code": "ERR_0004", "message": "Not found", "data": null }
```

### Possible transaction statuses

| Status | Description |
| ------ | ----------- |
| `PEN`  | Pending     |
| `PRO`  | Processing  |
| `COM`  | Completed   |
| `ERR`  | Error       |
| `WTG`  | Waiting     |
| `CAN`  | Cancelled   |
| `REF`  | Refunded    |
| `REJ`  | Rejected    |

***

## Flow 5: Balance check before operating

Before executing any operation, it is recommended to verify the available balance.

**Required permissions:** `general_data_read`, `balance_read`

```
GET /balance/wallet
```

Response:

```json theme={null}
{
  "error_code": null,
  "message": null,
  "data": [
    {
      "currency": "USDT",
      "currency_internal_id": 14,
      "amount": "250.00",
      "locked_amount": "0.00"
    },
    {
      "currency": "BTC",
      "currency_internal_id": 5,
      "amount": "0.00451230",
      "locked_amount": "0.00000000"
    },
    {
      "currency": "ARS",
      "currency_internal_id": 1,
      "amount": "125000.00",
      "locked_amount": "0.00"
    }
  ]
}
```

* `amount`: available balance (the amount that can be used in operations)
* `locked_amount`: funds reserved by open orders

***

## Generic error codes

When the upstream service does not return a specific `{code, detail}` atom (e.g. authentication errors or DRF-level errors), the gateway falls back to generic codes mapped by HTTP status:

| `error_code` | HTTP | Meaning             |
| ------------ | ---- | ------------------- |
| `ERR_0001`   | 500  | Internal error      |
| `ERR_0003`   | 400  | Bad request         |
| `ERR_0004`   | 404  | Not found           |
| `ERR_0005`   | 409  | Conflict            |
| `ERR_0006`   | 503  | Service unavailable |
| `ERR_0008`   | 401  | Unauthorized        |
| `ERR_0009`   | 403  | Forbidden           |
| `ERR_0010`   | 405  | Method not allowed  |
| `ERR_0011`   | —    | Generic / unmapped  |
| `ERR_0012`   | 429  | Too many requests   |

Example response when no valid credentials are provided:

```json theme={null}
{
  "error_code": "ERR_0008",
  "message": "Authentication credentials were not provided.",
  "data": null
}
```

> Domain-specific codes (`TXN_0420`, `CURR_0002`, `CVU_1000`, etc.) are **passed through unchanged** from the upstream microservice. `ERR_XXXX` codes only appear when no specific code is found.

> For more information on error codes, refer to the Errors page.

***

## Quick endpoint reference

| Endpoint                                       | Method | Permission                          | Description                                   |
| ---------------------------------------------- | ------ | ----------------------------------- | --------------------------------------------- |
| `/health/`                                     | GET    | —                                   | Health check                                  |
| `/account/`                                    | GET    | `general_data_read`                 | Account profile                               |
| `/balance/`                                    | GET    | `general_data_read`, `balance_read` | All balances                                  |
| `/balance/wallet`                              | GET    | `general_data_read`, `balance_read` | Wallet balances (excluding DEX)               |
| `/balance/viral`                               | GET    | `general_data_read`, `balance_read` | DEX balances (memecoins)                      |
| `/currencies/`                                 | GET    | `general_data_read`                 | Available currencies                          |
| `/rates/`                                      | GET    | `general_data_read`                 | Exchange rates                                |
| `/addresses/`                                  | GET    | `general_data_read`                 | Account crypto addresses                      |
| `/network/currency-networks/{currency}/`       | GET    | `general_data_read`                 | Available networks for a currency             |
| `/transactions/`                               | GET    | `general_data_read`                 | Transaction list (cursor-paginated)           |
| `/transactions/{id}/`                          | GET    | `general_data_read`                 | Transaction detail                            |
| `/transactions/rails/`                         | GET    | `general_data_read`                 | Rails and their enabled transaction types     |
| `/transactions/limits/`                        | GET    | `general_data_read`                 | Operational limits grouped by rail            |
| `/transactions/crypto/withdrawal/`             | POST   | `withdrawal_write`                  | Send crypto to an external address            |
| `/transactions/crypto/deposit/reprocess-hash/` | POST   | `withdrawal_write`                  | Reprocess a crypto deposit by hash            |
| `/transactions/bank/withdrawal/`               | POST   | `withdrawal_write`                  | Withdrawal to CVU/CBU (AR only)               |
| `/transactions/pix/withdrawal/`                | POST   | `withdrawal_write`                  | Withdrawal via PIX (BR only)                  |
| `/transactions/ripio/withdrawal/`              | POST   | `internal_balance_transfer_write`   | Send balance to another Ripio account         |
| `/transactions/swap/`                          | POST   | `trading_write`                     | Swap between currencies                       |
| `/transactions/quotes/`                        | POST   | `trading_write`                     | Request a swap quote                          |
| `/banking/deposit-accounts/`                   | GET    | `general_data_read`                 | Ripio deposit accounts (CVU / PIX)            |
| `/banking/accounts/`                           | GET    | `general_data_read`                 | List the user's own bank accounts             |
| `/banking/accounts/`                           | POST   | `general_data_read`                 | Register a bank account                       |
| `/banking/accounts/{rail_type}/{id}/`          | GET    | `general_data_read`                 | Own bank account detail                       |
| `/banking/accounts/{rail_type}/{id}/`          | DELETE | `general_data_read`                 | Delete an own bank account                    |
| `/contacts/`                                   | GET    | `contacts_read`                     | Saved contacts (`crypto` / `bank` / `rp_tag`) |
| `/contacts/`                                   | POST   | `contacts_write`                    | Add a contact                                 |
| `/contacts/{type}/{id}/`                       | DELETE | `contacts_write`                    | Delete a contact by `(type, id)`              |

> **Account state:** write endpoints (and the banking/contacts resources) additionally
> require a fully operative account; country-restricted endpoints (CVU = AR, PIX = BR)
> enforce the account's country.

> **Legacy paths (removed August 15, 2026):** the write/quote paths and
> `banking/deposit-accounts/` replace their legacy equivalents — `crypto/send/`,
> `bank-transfer-cvu/withdrawal/`, `balance/send/`, `balance/swap/`, `promises/`,
> `transactions/gateways/`, `banking/cvu/`, `banking/pix/` — which keep working (with a
> `Deprecation` header) until that date.
