---
title: "How to build a remittance app on an on/off-ramp API: funding, quotes, payouts, and disclosures"
seoTitle: "How to build a remittance app on an on/off-ramp API"
description: "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."
date: "2026-10-11"
updated: "2026-10-11"
category: "payments"
author: "BlindPay Team"
howto:
  name: "How to build a remittance app on an on/off-ramp API"
  steps:
    - name: "Onboard each sender as a customer"
      text: "Collect identity data in your app and create a customer for every sender, so KYC and screening sit with the real owner of the funds."
    - name: "Save the recipient's bank account"
      text: "Collect the Pix key, CLABE, or CBU once, validate it, and store it as a bank account under the sender."
    - name: "Fund the transfer in dollars"
      text: "Take the sender's dollars as a payin through a US virtual account, a memo-coded ACH or wire, or ACH pull from a Plaid-connected account."
    - name: "Quote the payout on the confirm screen"
      text: "Create a payout quote when the sender reaches confirm, show the send amount, rate, fees, receive amount, and a countdown from expires_at."
    - name: "Disclose, then execute"
      text: "Show the Regulation E pre-payment disclosure, execute the payout before the quote expires, and issue the receipt with the delivery date."
    - name: "Track the payout with webhooks"
      text: "Map each payout status to a sender notification and keep the quote ID, rate, and receive amount on the transfer record for support."
faq:
  - q: "What do I need to build a remittance app on an on/off-ramp API?"
    a: "Five pieces: a way for the sender to fund the transfer in dollars, a quote that shows the exact amount the recipient gets, a payout to the recipient's local bank account, status tracking through webhooks, and the consumer disclosures your license requires. The ramp API covers funding, conversion, and payout. Your app owns the sender experience, the disclosures, and the money transmitter licensing."
  - q: "How does the sender fund a remittance with BlindPay?"
    a: "The sender's dollars arrive as a payin. A KYC-approved customer can get a US virtual account with its own routing and account number, so every ACH, wire, or SWIFT deposit converts to USDC or USDT in a linked wallet. Customers without a virtual account can send ACH or wire to BlindPay's bank details with a memo code, or fund through ACH pull from a Plaid-connected account."
  - q: "Who is the remittance transfer provider under Regulation E?"
    a: "Usually the company that offers the service to the consumer, which is your app. Regulation E subpart B puts the pre-payment disclosure, the receipt, the 30-minute cancellation right, and error resolution on the remittance transfer provider. A provider making 500 or fewer remittance transfers in both the prior and current calendar year falls outside the normal course of business test. Confirm your status with counsel."
  - q: "Can every sender use my company's BlindPay account?"
    a: "Only if each sender is visible to BlindPay. Moving money for parties BlindPay has not onboarded is nesting, and nested activity can be delayed, frozen, or terminated. A remittance app onboards each sender as a customer inside its instance, so KYC, screening, and the payout record all sit with the real owner of the funds. Money services also need disclosure and enhanced due diligence at onboarding."
  - q: "How fast does the recipient get paid?"
    a: "It depends on the rail. In BlindPay's cut-off reference, Pix payouts in Brazil settle in up to 5 minutes and SPEI payouts in Mexico in up to 10 minutes, and Pix runs 24/7. ACH Colombia takes about 1 business day. The stablecoin leg doesn't wait for banking hours, so the recipient's rail decides what delivery date your receipt shows."
  - q: "Should the app quote the amount sent or the amount received?"
    a: "Both work. Set currency_type to sender when the user types dollars, so the quote returns the local amount received. Set it to receiver when the user types the local amount, for example R$ 2,000 for rent, so the quote returns the stablecoin cost. Either way, the confirm screen shows the quote's numbers and a countdown built from expires_at."
---

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](/resources/more/stablecoin-remittances-cost-vs-traditional). 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](/resources/more/how-onramp-offramp-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.

| Step | Your app | The ramp API |
| --- | --- | --- |
| Onboard the sender | Collects identity data, shows terms | Runs KYC, returns `kyc_status` |
| Add the recipient | Collects bank details (Pix key, CLABE, CBU) | Validates and stores the bank account |
| Fund | Shows deposit instructions or a connect button | Creates the payin, converts to stablecoin |
| Quote | Shows send amount, rate, fees, receive amount, expiry | Returns a binding payout quote |
| Pay out | Shows the receipt with a delivery date | Executes the payout on the local rail |
| Track | Notifies the sender | Fires 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](/resources/more/crypto-on-ramp-compliance-who-owns-what) 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](/docs/kb/payment-methods) 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](/docs/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](/docs/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](/docs/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 screen | Value |
| --- | --- |
| You send | 500.00 USDC, from the balance an earlier ACH deposit funded |
| Exchange rate after fee | 5.38 BRL per USDC |
| Transfer fee | Shown from the quote's fee lines |
| They receive | 500 x 5.38 = R$ 2,690.00 |
| Quote valid until | 5 minutes from creation |
| Arrives | Minutes, 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](/resources/more/partner-fees-markup-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](/docs/kb/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](/resources/more/pix-key-cpf-clabe-payout-details) 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](/docs/kb/nested-payments) 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](/docs/kb/prohibited-activities) list.

**Consumer remittance rules.** The CFPB's [Regulation E subpart B](https://www.consumerfinance.gov/rules-policy/regulations/1005/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](/resources/more/cross-border-quote-screen-fee-disclosure) maps each required field to the quote.

**Cancellation timing.** Under [12 CFR 1005.34](https://www.law.cornell.edu/cfr/text/12/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](/docs/payout-quotes) has every field your confirm screen needs.
