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

# Brazil

> On-Ramp and Off-Ramp payment methods, currency and requirements for Brazil (BRL).

**Currency:** BRL · **On-Ramp:** ✅ · **Off-Ramp:** ✅

## Payment methods

### On-Ramp (fiat → crypto)

| Method | How the user pays |
| - | - |
| **PIX** | The user pays a **BR Code** generated for that specific order — by scanning the QR or pasting the *copia e cola* string. |

### Off-Ramp (crypto → fiat)

| Method | Where the user receives funds |
| - | - |
| **PIX** | Payout to a registered destination **PIX key**. |

<Note>
  Brazil has **no reusable deposit account**. Each on-ramp order returns its own payment instructions synchronously in `fiatPaymentInstructions`, so there is nothing to create beforehand and nothing to wait for.
</Note>

## On-Ramp payment instructions

[Create On-Ramp Order](/ramps-api/on-ramp/create-on-ramp-order) returns the PIX payment instructions in `fiatPaymentInstructions`:

| Field | What it is |
| - | - |
| `brCode` | The payable **BR Code EMV** (*copia e cola*). Render it as a QR or offer it for copy-paste. |
| `paymentUrl` | A hosted payment page, if you prefer to redirect the user. |
| `expiresAt` | When the payment instructions expire. After that, create a new order. |

<Warning>
  The payment instructions are bound to the customer's **own** verified identity: only the CPF/CNPJ from their approved KYC is allowed to pay them. A transfer attempted by anyone else will not go through, so never reuse one customer's BR Code for another.
</Warning>

Because the payable object belongs to a single order, `pix` is **not available through an [On-Ramp Session](/ramps-api/on-ramp/create-on-ramp-session)** — use [Create On-Ramp Order](/ramps-api/on-ramp/create-on-ramp-order).

## Requirements

* **Customer & KYC:** the customer must complete [identity verification](/ramps-api/kyc/overview) (`COMPLETED`) before operating. Brazil KYC requires `first_name`, `last_name`, `id_number`, `id_number_type` and `net_income` in addition to location and contact fields — see [the fields Brazil requires](/ramps-api/kyc/kyc-fields-by-country#brazil). The approved CPF/CNPJ and customer name bind on-ramp payment instructions to the verified customer, and the document is also used to verify ownership of PIX destination accounts.
* **Deposit account (On-Ramp):** not required. [Create Deposit Account](/ramps-api/customers/create-deposit-account) is a no-op for `pix` and returns `200 OK`; you can skip it entirely.
* **Off-Ramp destination account:** register the user's destination **PIX key** as an [external fiat account](/ramps-api/fiat-accounts/create-fiat-account) — a single `pix_key` field, which may be a CPF, CNPJ, email, phone or EVP (random) key (see [PIX key formats](#pix-key-formats) below). The key is resolved to its account holder while the request is served, and the holder's document must match the customer's own CPF/CNPJ. On a match the account is created as `unconfirmed` and must be [confirmed](/ramps-api/fiat-accounts/confirm-fiat-account) before it can receive a payout — see [What the key resolves to](#what-the-key-resolves-to) for the other endings. Use [Retrieve Fiat Account Requirements](/ramps-api/fiat-accounts/retrieve-fiat-account-requirements) to get the exact fields.
* **Terms & Conditions:** accept the applicable [T\&C](/ramps-api/terms-and-conditions/introduction) right after creating the customer — a required step before operating.
* **Investor profile:** after KYC, the customer answers a five-question suitability questionnaire required by Resolução BCB nº 520/2025, valid for one year. From October 30, 2026, a customer without a valid profile cannot create orders, sessions or destination accounts (`20100`). The widget asks for it on its own; via the API you collect it — see [Investor Profile (Brazil)](/ramps-api/investor-profile/introduction).

## PIX key formats

`pix_key` is a **single free-text field with no type selector**: the type is inferred from the value and the account holder is resolved from the key itself. So the key has to arrive in the same canonical format BACEN registered it in — a key that is merely *recognisable* to a human is not resolvable.

| Type | Format | Example |
| - | - | - |
| **CPF** | 11 bare digits, no dots or dash | `12345678909` |
| **CNPJ** | 14 bare digits, no punctuation | `12345678000199` |
| **Email** | plain address | `maria@example.com` |
| **Phone** | international format: `+`, country code, DDD, number | `+5511987654321` |
| **EVP** (random) | UUID as returned by the user's bank | `123e4567-e89b-42d3-a456-426614174000` |

Maximum length is **77 characters** (the EMV/BACEN limit).

<Warning>
  **A phone key must carry the `+` and the country code.** Without it the key is not the one registered at BACEN, so sending `5511987654321` or `11987654321` is rejected with a `400` naming the expected format. Send `+5511987654321`.

  This is also why the other types must be sent bare: an 11-digit key with no `+` is read as a CPF, which is exactly what a Brazilian mobile number looks like when the prefix is dropped.

  The rejection is a plain validation error, raised before the key is looked up:

  ```json theme={null}
  {
    "code": 20000,
    "type": "ValidationError",
    "detail": {
      "non_field_errors": [
        {
          "message": "Invalid accountFields '{'pix_key': '5511987654321'}': Invalid pix_key: a phone PIX key must be sent in international format, starting with '+' and the country code (e.g. '+5511987654321').",
          "code": "invalid"
        }
      ]
    },
    "status": 400
  }
  ```
</Warning>

## What the key resolves to

There is nothing to poll: the key is looked up while [Create Fiat Account](/ramps-api/fiat-accounts/create-fiat-account) is being served, so the answer is in that response. It has four endings, and they are not interchangeable — three of them are the customer's to fix, and each one needs different words.

| Ending | Response | What it means |
| - | - | - |
| **The key is the customer's** | `201`, `status: "unconfirmed"` | Resolved, and the holder's document matches the customer's KYC. Confirm it and it becomes `enabled`. |
| **The key is somebody else's** | `201`, `status: "disabled"` | Resolved, but to a different person. The account is created and cannot be confirmed — payouts only go to accounts in the customer's own name. |
| **No such key** | `400`, code `20079` | No bank has it registered. Nothing is created; the value has to be corrected, and retrying the same one cannot succeed. |
| **The key is not active yet** | `400`, code `20080` | It resolved, so it is a real key — but it is not in a state that can receive a transfer: still being claimed at BACEN, or cancelled. The customer fixes this at their bank, not by typing something else. |

A malformed value never reaches the lookup: it is a `400` with code `20000`, like the phone-prefix case above.

<Note>
  **The resolved holder comes back in `metadata`, in both `201` endings** — `name`, the masked `tax_id` and `bank_name`. That is what lets you show the customer whose account they are about to be paid into before confirming, and, when the account comes back `disabled`, explain *why* instead of showing a generic failure.

  ```json theme={null}
  {
    "fiatAccountId": "1a8a331f-9886-4a6e-a230-1222431897ed",
    "fiatAccountFields": { "pix_key": "maria@example.com" },
    "status": "disabled",
    "customerId": "c3e9bb2a-da78-442d-8aad-e0b05dd2a8e7",
    "metadata": {
      "name": "Maria Souza",
      "tax_id": "***.456.789-**",
      "bank_name": "Banco Exemplo S.A.",
      "type": "email",
      "status": "registered"
    }
  }
  ```

  BACEN masks the document, so you get enough for the customer to recognise the holder and not enough to identify a stranger.
</Note>

## Refunds

On-ramp deposits in Brazil are refunded by **reversing the customer's own PIX transfer**, so the money goes back to the account it came from and you never name a destination: `refundDestinationRequired` is `false`. See [Refunds](/ramps-api/refunds/overview).

## Next steps

* [On-Ramp integration walkthrough](/ramps-api/guides/on-ramp-integration)
* [Off-Ramp integration walkthrough](/ramps-api/guides/off-ramp-integration)
* [Widget integration](/ramps-api/widget/overview)


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