How to add a "Send to Brazil by Pix" feature to your neobank app

Build a Send to Brazil by Pix feature: Pix key entry, recipient check, live quote screen, payout, webhooks, receipts, and the mistakes that cause failures.

To add a "Send to Brazil by Pix" feature, your app collects the recipient's Pix key, your backend saves it as a Pix bank account, requests a live quote, shows the BRL amount, and executes a payout. BlindPay handles the conversion and the Pix delivery through one API, and webhooks tell you when the reais land.

This guide is about the product feature inside a neobank: the screens, the states, and the messages. For the payout mechanics from a business wallet, see how to send USDC to a bank account in Brazil. For where this feature sits in the whole build, start with how to build a neobank.

Key takeaways

  • Ask for the Pix key type before the key. Each type has its own format and validation.
  • Lock the price with a quote and show the BRL amount the recipient gets, not only the USD you send.
  • Send an idempotency key on every payout so a timeout never doubles a transfer.
  • Pix runs 24/7, so your support and notifications should too.
  • Store the payout ID and rail reference on every transfer. Support needs them for every "where is my money" ticket.

What is Pix, and why do users in Brazil expect it?

Pix is Brazil's instant payment scheme, created by the central bank. According to Banco Central do Brasil, it lets people, companies, and government entities send or receive transfers in a few seconds at any time, including non-business days.

That sets the bar. A Brazilian recipient compares your transfer to the Pix they get from friends every day. If your feature takes two days, it feels broken, even if it is cheaper than a wire.

What do you need before the first Pix transfer?

You need a verified sender, a recipient's Pix details, and a funding source for the payout.

RequirementWho provides itWhere it lives in BlindPay
Terms of service acceptanceYour user, on a hosted pagetos_id passed when creating the customer
KYC on the senderYour userCustomer object, status approved
Pix key and recipient nameYour user, about the recipientBank account of type pix
Bank and branch details (no key)Your user, about the recipientBank account of type pix_safe or ted
Funds to sendYour treasury or the user's balanceUSDC or USDT in the funding wallet

The onboarding guide for neobanks covers the KYC side. The bank accounts docs list the exact fields per rail.

How do you build the feature step by step?

Six steps, from the sender's first tap to the receipt.

  1. Verify the sender. The user accepts terms of service and passes KYC once. After that, every transfer reuses the same customer.
  2. Collect the Pix key. Show a picker first: CPF, CNPJ, phone, email, or random key. Then show one input with the right keyboard and mask for that type. Save it as a pix bank account for the customer.
  3. Request a live quote. Your backend creates a payout quote for the amount. The user can type USD to send or BRL to arrive. The quote locks the rate and fees for 5 minutes. See the payout quotes docs.
  4. Show the confirmation screen. Recipient name, Pix key (partly masked), BRL amount received, fee, rate, and a countdown.
  5. Execute the payout. On confirm, create the payout with the quote ID and an Idempotency-Key header (idempotency docs). A retry after a timeout then returns the same payout instead of a second one.
  6. Track and notify. Listen for payout.new, payout.update, and payout.complete (webhook events). Update the transfer row, send a push notification, and email a receipt.

How should the Pix key screen work?

Validate early and show who the money goes to before the user commits.

  • Type picker first. "Which Pix key does your recipient use?" removes most format errors.
  • Format checks on the device. A CPF has 11 digits, a CNPJ has 14. A phone needs the country code. An email needs an @. A random key is long and gets pasted, so offer a paste button.
  • Show the recipient name. Ask the user to type the recipient's full name and show it again on the confirmation screen. People catch wrong keys when they read a name they don't recognize. The guide on verification of payee in instant payments explains how rails check recipients.
  • Save favorites. Most remittance users send to the same two or three people. Let them pick a saved recipient instead of retyping a key.

What should the confirmation screen show?

Everything the user needs to say yes, and nothing they need to calculate.

FieldExample (illustrative)Why it matters
RecipientAna SouzaCatches a wrong key
Arrives asR$ 2,710.00The number the recipient will see
You sendUS$ 500.00The number leaving the user's balance
FeeUS$ 4.50Shown separately, never buried in the rate
Rate1 USD = 5.47 BRLLets users compare
Price locked for4:32The quote expiry, counting down
ArrivesUsually within minutesSets expectations

The numbers above are invented. In production, every one of them comes from the quote response. Some users check your rate against the central bank's PTAX reference, which Banco Central do Brasil publishes as the average of four daily dealer consultations. A quote includes spread and fees, so it won't match PTAX exactly. Show both the rate and the fee so the comparison is fair. The quote screen and fee disclosure guide covers the layout in more detail.

What does one transfer look like end to end?

Illustrative example. Maria lives in Miami and uses a neobank app. She sends money to her sister Ana in Belo Horizonte.

  1. Maria taps "Send to Brazil" and picks Ana from saved recipients (Pix key type: phone).
  2. She types US$ 500. The app requests a quote and shows R$ 2,710.00 arriving, a US$ 4.50 fee, and a 5-minute countdown.
  3. She confirms at 4:32 remaining. The backend creates the payout with an idempotency key.
  4. The app shows "Sending". A payout.new webhook arrives, then payout.complete.
  5. Maria gets a push: "R$ 2,710.00 arrived in Ana's account." The receipt includes the rate, fee, time, and a reference number.

Total time on Maria's side: under a minute. That is the experience Pix users expect.

What are the common Pix mistakes?

  • One free-text field for every key type. Users paste a phone without the country code or a CPF with dots in the wrong place. Use a type picker and masks.
  • Showing only the USD amount. The recipient cares about reais. Show the BRL amount from the quote.
  • Executing an expired quote. The 5-minute window passes while the user reads. Requote and show the new numbers.
  • No idempotency key. A flaky mobile connection plus a retry can send money twice.
  • Treating "processing" as "failed". Give the payout time and rely on webhooks instead of polling every second.
  • Silent failures. If a payout fails or is refunded, say so in the app with a reason and a "fix and resend" button.
  • No reference on the receipt. Store the payout ID and the rail reference. Without them, support can't trace anything.

How should you handle failures and refunds?

Show the status the payout reports, explain it in plain words, and give one next step.

  • processing: "Sending. Usually takes a few minutes."
  • on_hold: "We're checking this transfer. We'll update you." Compliance reviews can happen on any rail.
  • completed: "Arrived." Show the receipt.
  • failed or refunded: "This transfer didn't go through. Your money is back." Then offer to fix the Pix key.

On Pix and other non-USD local rails, if BlindPay can't deliver a payout after the stablecoins are collected, it refunds them automatically to the quote's refund wallet, or to the sending wallet when none is set. Your ledger should then return the amount to the user's balance. The payout statuses explainer covers each state.

Pix also has a special return mechanism for fraud cases, run by the recipient's and payer's banks. The central bank's guide to Pix returns and the MED sets the rules, including an 80-day window from the original transaction to open a recovery.

How do you test the feature before launch?

Run every branch on a development instance before real money moves.

  1. Create a customer and add a Pix bank account with test details.
  2. Request a quote and let it expire. Confirm your app requotes.
  3. Execute a payout and confirm your webhook handler updates the transfer.
  4. Send the amount 66600 to force a failed payout, and 77700 to force a refunded one. Check the messages users see.
  5. Retry a payout call with the same idempotency key and confirm no second payout appears.
  6. Kill the app mid-transfer and reopen it. The status should come from your backend, not from the screen.

How BlindPay fits

BlindPay gives a neobank one API for Pix payouts. Your backend creates the customer after terms of service and KYC, adds the recipient as a pix bank account (or pix_safe and ted when the user has bank details instead of a key), requests a quote that locks the rate and fees for 5 minutes, and executes the payout. BlindPay converts USDC or USDT from the funding wallet into BRL and pays over Pix, which BlindPay lists as instant.

You don't pre-fund a Brazilian account or open a local entity to send. Webhooks, signed with Svix headers, report each status change. If you charge your own fee per transfer, partner fees add it to the quote so users see one price. The same flow then extends to SPEI and other rails, as covered in which payout rails a neobank should launch with.

What to do next

Sketch the three screens (recipient, confirm, receipt), then wire them to a development instance with the payout quickstart. Send one test transfer that completes, one that fails, and one that is refunded before you show it to users.

FAQ