Skip to main content

Overview

Offramps with transfers let your users convert stablecoin to international fiat currencies through a quote-based flow. Unlike payment routes, which create persistent pipes for USD offramps, transfers are one-time operations with locked exchange rates and fees. Use transfers for international payouts to currencies like COP, MXN, and BRL. The process involves three steps:
  1. Create a payment account: register the user’s bank account details as a payment account.
  2. Create a quote: get a quote with the exchange rate, fees, and destination amount.
  3. Create a transfer: execute the transfer using the quote before it expires.

Before you begin

Make sure you have:
  • API access to the payment accounts, quotes, and transfers endpoints.
  • User bank account details: the destination bank account information (details vary by country and rail).
  • A stablecoin source: users need stablecoin on a supported chain to transfer.

Key concepts

Payment accounts

A payment account is a stored representation of a user’s bank account or other fiat destination. See Payment Accounts for the general concept and the US-specific fields; this page covers the additional fields required for international destinations. For international transfers, you’ll create an externalFiatAccount type payment account containing the user’s bank details for the destination country.

Quotes

A quote locks in the exact exchange rate, fees, and destination amount for a transfer, unlike an exchange rate preview, which is indicative only and doesn’t lock anything in. Quotes have an expiration time, so you must create the transfer before the quote expires. Key information in a quote includes:
  • Exchange rate: the rate used for currency conversion, excluding fees.
  • Fees: breakdown of Rain fees and any sender fees.
  • Destination amount: the exact amount the user receives in fiat.
  • Expiration: when the quote expires and is no longer valid.

Transfers

A transfer executes the movement of funds from crypto to fiat using a locked quote. When you create a transfer, Rain generates a depositAddress where the user sends their stablecoin. Once Rain receives the funds, it automatically converts and sends them to the destination bank account.

API flow

Action / API callExternal / networkWebhook Rain sends you
UserProvides bank details
1YouPOST /payment-accounts
2RainReturns payment account
3YouPOST /quotes
4RainReturns quote with rate & fees
5YouDisplays quote details
6YouPOST /transfers
7RainReturns transfer with depositAddress
8YouProvides deposit address
9UserSends stablecoin to deposit address
10RainConverts to fiat
11BankReceives fiat transfer
12WebhooktransactionTransfer
The steps above, in text:

Step 1: create a payment account

Before creating a quote, you must register the user’s bank account as a payment account. The required fields vary by destination country.

Request: Colombian peso (COP)

Colombia supports multiple rails: co_ach for bank transfers, co_bre_b for Bancolombia transfers, and co_mobile_wallet for mobile wallet payments.

Request: Mexican peso (MXN)

Request: Brazilian real (BRL)

Brazil supports br_pix for instant PIX payments and br_ted for traditional bank transfers.
First-party and third-party transfersRain supports both first-party and third-party transfer destinations:
  • First-party (thirdParty: false): the bank account belongs to the same user initiating the transfer.
  • Third-party (thirdParty: true): the bank account belongs to a different party, for example a vendor or another individual.

Response

Required fields by rail

Colombia (COP)

Mexico (MXN)

Brazil (BRL)

Additional fields may be required depending on the transfer amount or specific compliance requirements. Contact your Rain representative for detailed requirements.

Step 2: create a quote

Once you have a payment account, request a quote to see the exchange rate, fees, and destination amount.

Request

You can optionally specify a source address if you know which wallet will be sending the funds. Some rails may require this information.

Response

Quote fields

Quotes expire after a short period (typically 15 minutes). If the quote expires, you must create a new quote before creating a transfer.

Step 3: create a transfer

If the user is happy with the exchange rate and fees, create a transfer using the quote.

Request

Request parameters

Headers

Reusing an idempotency-key with a different request body returns a 409 conflict. Generate a new key for each distinct transfer.

Response

The response includes the transfer details along with a depositAddress where the user sends their stablecoin:

Response fields

The depositAddress is unique to this transfer. Only send the exact source amount of the specified currency (for example, 100 USDC on Base) to this address. Sending other tokens or incorrect amounts may result in failed transfers or loss of funds.

Alternative: fund a transfer from fiat

Instead of funding a transfer with crypto, you can quote a fiat source such as wire or ach. The transfer then comes back with a fiat depositAddress containing bank deposit instructions, and you send the source amount there instead of to an onchain address.

Quote request (fiat source)

Quote response (fiat source)

Transfer request (fiat source)

Transfer response (fiat source)

A fiat-source transfer returns a depositAddress with type: "fiat": the bank deposit instructions to fund it. Send exactly the source amount, and include the transferMessage reference with your payment so Rain can match the funds to the transfer. A fiat source doesn’t need a refundAddress.

What happens when crypto is deposited

When a user sends stablecoin to the deposit address:
  1. Crypto received: Rain detects the incoming stablecoin transfer.
  2. Transfer created: a transaction of type transfer is created, and a transactionTransfer webhook with action created is sent.
  3. Validation: the amount and currency are validated against the transfer.
  4. Conversion: stablecoin is converted to the destination fiat currency.
  5. Status updates: as the transfer progresses, transactionTransfer webhooks with action updated are sent for status changes.
  6. Fiat transfer: funds are sent to the user’s bank account via the specified rail.
  7. Transfer completed: the transfer settles, and a transactionTransfer webhook with action completed is sent.

Webhooks

Rain sends webhooks during the transfer lifecycle to keep you informed of status changes: See the transfer object reference for the full webhook payload schema.

Transfer statuses

Query transfers

You can query all transfer transactions via the Transactions API. Filter by type=transfer to see transfer transactions:
Or get a specific transaction by ID:

Cancel a transfer

To cancel a transfer that hasn’t completed, call DELETE /v1/transfers/{transactionId} with the transaction ID the create call returned:
A successful cancel returns 200 with the transfer, its status now cancelled. Once a transfer passes the point where it can be stopped, the call returns 409 with Transfer can no longer be cancelled.

Supported configurations

Source (crypto)

Source (fiat)

Destination (fiat)

Additional currencies and rails are being added regularly. Contact your Rain representative for the latest supported configurations.

Error handling

Quote errors

Transfer errors

If you receive a 503 response with TransferPersistenceError, your transfer has already been initiated with the payment provider. Rain automatically reconciles the transfer asynchronously. Don’t retry the request, since this could result in duplicate transfers. Use the idempotency-key header to safely handle retries in case of network timeouts.

Best practices

  1. Use idempotency keys: always include a unique idempotency-key header with each transfer request to prevent duplicate transfers on retries.
  2. Display quote details: show users the exchange rate, fees, and destination amount before they confirm the transfer.
  3. Handle expiration: implement logic to refresh quotes if the user takes too long to confirm.
  4. Monitor webhooks: set up webhook handlers to track transfer status in real time.
  5. Validate bank details: create payment accounts during user onboarding to catch errors early.
  6. Provide clear instructions: make sure users understand they must send the exact amount to the deposit address.
  7. Handle 503 responses: if you receive a 503 error, don’t retry immediately. The transfer may have been initiated and is reconciled automatically.

What’s next

Set up onramps

Enable fiat deposits into crypto.

Set up offramps

Enable fiat withdrawals from crypto via payment routes.

Payment accounts

See the full request, response, and field reference for registering a bank account.

Set up webhooks

Receive transfer notifications as they happen.