How to build a remittance app on an on/off-ramp API: funding, quotes, payouts, and disclosures

The build plan for a US-to-LatAm remittance app on stablecoin rails: fund the send, quote the receive amount, pay out over Pix or SPEI, and meet Reg E.

A remittance app on an on/off-ramp API has four moving parts: the sender funds in dollars, a binding quote converts that into the recipient's local amount, a payout lands in the recipient's bank over Pix, SPEI, or another local rail, and your app adds the disclosures US consumer law requires. You own the sender.

The cost case for stablecoin remittances is covered in the five-part cost check. This page is the build: which API calls, in what order, and which obligations sit with you rather than the provider. How ramp providers get paid for each leg is in how on/off-ramp providers make money.

Key takeaways

  • Every sender is a customer. Onboard each one with KYC, because moving money for parties the provider can't see is nesting.
  • Fund with a US virtual account, a memo-coded ACH or wire payin, or ACH pull from a Plaid-connected account.
  • Quote the payout right before the confirm screen. BlindPay payout quotes hold for 5 minutes.
  • Pix and SPEI pay out in minutes. The receipt's delivery date comes from the recipient's rail.
  • Regulation E subpart B disclosures, cancellation, and error resolution are the provider's job, which usually means your app.

What does the architecture of a remittance app look like?

Three layers. Your app holds the sender relationship: sign-up, the amount screen, disclosures, receipts, and support. The ramp API holds the money movement: KYC records, payins, conversion, and payouts. Webhooks connect them, so your app updates the transfer status without polling.

StepYour appThe ramp API
Onboard the senderCollects identity data, shows termsRuns KYC, returns kyc_status
Add the recipientCollects bank details (Pix key, CLABE, CBU)Validates and stores the bank account
FundShows deposit instructions or a connect buttonCreates the payin, converts to stablecoin
QuoteShows send amount, rate, fees, receive amount, expiryReturns a binding payout quote
Pay outShows the receipt with a delivery dateExecutes the payout on the local rail
TrackNotifies the senderFires payout webhooks with each status

The split matters for compliance too. Your app can't hand KYC to the provider and forget about it, and the provider can't write your Reg E disclosures. Who owns what in on-ramp compliance goes deeper on the line.

How does the sender fund the transfer?

With a payin that turns dollars into stablecoins in a wallet the transfer can draw from. BlindPay's payment methods reference lists three ways US dollars come in:

  1. A virtual account in the sender's name. The customer gets its own routing and account number. Each ACH, wire, or SWIFT deposit creates a payin, and BlindPay sends USDC or USDT to the linked wallet (virtual accounts). This suits repeat senders who send every payday.
  2. Bank details plus a memo code. The sender pushes ACH or wire to BlindPay's details with the memo_code from the payin, so the deposit gets matched (payin quotes).
  3. ACH pull. The sender connects a bank account through Plaid and the payin pulls the funds directly, with no manual transfer.

The funding wallet is a product decision. A customer-controlled blockchain wallet keeps the payout flow non-custodial: BlindPay operates as a non-custodial payment processor for payout flows from customer-controlled wallets, where you authorize the exact amount and BlindPay executes that single transaction. A BlindPay-managed wallet (beta) is simpler to run and is custodial when you choose that path.

How do you quote the amount the recipient gets?

Create a payout quote against the recipient's bank account the moment the sender reaches the confirm screen, not while they type. The quote returns sender_amount, receiver_amount, commercial_quotation, blindpay_quotation, the fee lines, and expires_at in epoch milliseconds (payout quotes).

Two fields decide what the sender sees:

  • currency_type. sender fixes the stablecoin amount and returns the local amount. receiver fixes the local amount, which fits a sender who promised exactly R$ 2,000 to family.
  • cover_fees. true adds fees on top of the stablecoin sent, so the recipient gets the full amount. false deducts them from what the recipient receives.

Illustrative example. A sender in Texas wants to send $500 to a Pix key in Brazil. The app requests a quote with currency_type: sender and cover_fees: false:

Line on the screenValue
You send500.00 USDC, from the balance an earlier ACH deposit funded
Exchange rate after fee5.38 BRL per USDC
Transfer feeShown from the quote's fee lines
They receive500 x 5.38 = R$ 2,690.00
Quote valid until5 minutes from creation
ArrivesMinutes, over Pix

If the sender walks away and returns after 5 minutes, the app requests a new quote and shows the new number. It never executes the old one. Partner fees on live quotes covers how to add your own margin as a visible line instead of hiding it in the rate.

How does the payout reach the recipient?

Over the recipient country's own rail. BlindPay's cut-off times list Pix at up to 5 minutes, running 24/7, and SPEI at up to 10 minutes. Transfers 3.0 in Argentina settles in up to 10 minutes, and ACH Colombia takes about 1 business day.

Recipient details are where remittances fail most. Validate the Pix key or CLABE before the first transfer, and store the recipient as a saved bank account so the second transfer is one tap. Pix keys, CPF, and CLABE lists the fields per country.

If a payout can't be delivered after the stablecoins are collected, BlindPay refunds them to the quote's refund wallet, or to the sending wallet when none is set. On Pix and other non-USD local rails the refund is automatic. Your app should turn that status into a plain message and a retry button.

Which compliance obligations sit with the app?

More than most teams expect. Three layers apply.

Customer visibility. BlindPay's nested payments policy says you may not move payments for any party whose identity or activity BlindPay can't see. A remittance app that pools every sender's money into one company account is the textbook case. Onboard each sender as a customer in your instance instead. Money services are a high-risk category that is allowed with disclosure and enhanced due diligence, per BlindPay's prohibited and high-risk activities list.

Consumer remittance rules. The CFPB's Regulation E subpart B, "Requirements for Remittance Transfers," runs from 12 CFR 1005.30 to 1005.36: definitions, disclosures, estimates, error resolution, cancellation and refund, acts of agents, and scheduled transfers. The provider owes a pre-payment disclosure and a receipt. How to show a cross-border quote maps each required field to the quote.

Cancellation timing. Under 12 CFR 1005.34, a sender can cancel if the request arrives "no later than 30 minutes after the sender makes payment," but only if the funds "have not been picked up by the designated recipient or deposited into an account of the designated recipient." With a Pix payout that lands in minutes, that window can close fast. Decide your execution timing with counsel before launch.

How should the app handle status and support?

Treat every webhook as a sender notification waiting to happen. Map each payout status to one line of copy: processing, completed, failed, or refunded. Show the delivery date on the receipt, and keep the quote ID, the rate, and the receive amount on the transfer record so support can answer "why did my mother get R$ 2,690 and not R$ 2,700?" in one look.

Test the unhappy paths before launch. On a development instance, a payout request_amount of 66600 fails and 77700 is refunded, so you can rehearse both screens without real money.

What to do next

Create a development instance, onboard one test sender, add one Pix recipient, and run the full loop: payin, quote, payout, webhook. The payout quotes reference has every field your confirm screen needs.

FAQ