Stablecoin to fiat payout (crypto off-ramp)
Payout API endpointsCopied!
Here are the endpoints you'll need to complete or cancel a payout transaction:
-
POST /quote
: Create a stablecoin/fiat quote -
POST /payout
: Initiate stablecoin to fiat payout:-
When the wallet sender address is known
-
When the wallet sender address is unknown
-
-
GET /transactions/{id}
: View transaction details -
PUT /transactions/{id}
: Cancel a transaction (only available for transactions withawaiting_deposit
status)
How Funds Flow?Copied!
Here's how funds flow through UnblockPay's payout process:
-
First, the customer requests a quote.
-
Next, the customer (or partner acting on their behalf) sends stablecoins to UnblockPay's designated deposit wallet address.
-
Once received, UnblockPay converts stablecoins to fiat currency.
-
Finally, we send the converted fiat amount directly to the recipient's bank account via local transfer.
1. Create a quote: POST /quoteCopied!
This endpoint returns a fresh exchange rate for converting stablecoins (e.g. USDC or USDT) to fiat money (e.g. BRL). The quotation is valid for 5 minutes and includes a unique ID that you'll need to reference when initiating the payout.
Here's an request example:
{
"symbol": "USDC/BRL"
}
Here's an example response:
{
"id": "bfc36f00-ed1f-11ef-9fe4-378e29e10831",
"quotation": "5.6766",
"symbol": "USDC/BRL",
"expires_at": 1739790908
}
2. Initiate stablecoin to fiat payout: POST /payout: Copied!
a) When the wallet sender address is knownCopied!
This endpoint initiates the payout process by creating a transaction that will convert stablecoins into fiat money and send the funds via local rails like Pix and SPEI®. You'll need to provide the payout amount
in stablecoins, the quote_id
from the previous step, along with sender
and receiver informations.
The sender
will include:
-
currency: Blockchain token you want to convert into fiat (e.g.
USDC
orUSDT
). -
payment_rail: Blockchain network where the transaction will occur (i.e.
solana
orethereum
). -
address: The wallet address from which you are sending your tokens.
The receiver
will include:
-
currency: The fiat currency you want to receive the payout (e.g.
BRL
orMXN
). -
payment_rail: Local payment network through which the payout will be sent (e.g.
pix
orspei
). -
pix_key: Unique identifier used for Pix transfers in Brazil's banking system, which can be a phone number, tax ID (CPF or CNPJ), email address or random key (e.g.
21981419836
,14965065700
,email@gmail.com
ordcta478j-196l-03fm-t6gh-4298er7845m2
). -
document: the receiver tax ID (e.g.
14965065700
for individuals or58444469000108
for business).
Here's an example request:
{
"amount": 5.00,
"quote_id": "bfc36f00-ed1f-11ef-9fe4-378e29e10831",
"sender": {
"currency": "USDC",
"payment_rail": "solana",
"address": "3fFnisw8zsFhyQopBH1hLLshJEPs8tCysESSLTvTGjWk"
},
"receiver": {
"currency": "BRL",
"payment_rail": "pix",
"pix_key": "21999887766",
"document": "12345678900"
}
}
The transaction will be created in an awaiting_deposit
status, and you'll receive deposit instructions for sending USDCs or USDTs.
The sender_deposit_instructions
will include:
-
amount: The exact amount of tokens you need to send to the
deposit_address
. -
currency: Blockchain token you want to convert into fiat (e.g.
USDC
orUSDT
). -
payment_rail: The blockchain network name (e.g.
solana
orethereum
). -
deposit_address: UnblockPay's wallet address where you should send stablecoins.
Send the exact stablecoin amount specified to ensure proper processing.
Once we detect the USDC deposit, we'll automatically process the conversion and initiate the Pix transfer to the receiver.
Here's an example response:
{
"id": "0d053970-ed1f-11ef-80a0-81ef33c9f312",
"status": "awaiting_deposit",
"type": "off_ramp",
"partner_id": "7bcc2530-86a4-4cee-9382-820e12d2d7ea",
"quotation": "5.6755",
"sender_deposit_instructions": {
"amount": 5,
"currency": "USDC",
"payment_rail": "solana",
"deposit_address": "7PWWVJ3qLTbct4ucxpKXrfoZt39thmbzrnKqA9NnoGn5"
},
"sender": {
"currency": "USDC",
"payment_rail": "solana",
"address": "3fFnisw8zsFhyQopBH1hLLshJEPs8tCysESSLTvTGjWk"
},
"receiver": {
"amount": 28.34,
"currency": "BRL",
"payment_rail": "pix",
"pix_key": "21981419836",
"pix_key_type": "PHONE",
"pix_end_to_end_id": "E3513612020250217110500000886939",
"bank_account": {
"bank_name": "NU PAGAMENTOS - IP",
"bank_code": null,
"bank_account_number": null,
"beneficiary": {
"document": "12345678900",
"name": "Satoshi Nakamoto"
}
}
},
"receipt": {
"initial_crypto_amount": 5,
"final_fiat_amount": 28.34,
"unblockpay_fee": 0.15
},
"created_at": "2025-02-17T11:05:09.000Z",
"updated_at": "2025-02-17T11:05:09.000Z",
"finished_at": null
}
The finished_at
field is only populated when the payout reaches a final status.
If you want to understand our fee structure, please check the Fee section.

After initiating a payout, we will send a webhook with the event type payout.created
.
Pool/jumbo wallet scenario
The scenario below only applies when deposits come from the same wallet addres (pool/jumbo wallet). For deposits from different wallets, this scenario does not apply.
When sending 2 or more deposits of the same amount from the same address, payouts will be processed in chronological order based on the oldest transaction.
Only call the payout route when you are certain you will execute the USDC or USDT deposit. Cancel the transaction immediately if the deposit will not be made.
Example: Customer 1 initiates a payout. Shortly after, Customer 2 initiates a payout with the same transaction amount as Customer 1. If Customer 2 makes the deposit before Customer 1, the payout amount will be deposited for the receiver specified by Customer 1.
b) When the wallet sender address is unknownCopied!
When the sender address is unknown, you'll need to confirm the stablecoin deposit after it's completed. The POST/payout
request remains similar, except you won't include the sender
address.
To confirm the stablecoin deposit, you will need to call POST/payout/confirm
, including the transaction_id
and the transaction_hash
.
-
transaction_id
: A unique ID that identifies each UnblockPay payout transaction. -
transaction_hash
: The blockchain transaction ID that identifies the stablecoin transfer to UnblockPay's wallet.
This endpoint should only be called after sending the tokens to the deposit_address
provided in the sender_deposit_instructions
.
Here's an example request:
{
"transaction_id": "246c6260-faa7-11ef-b04c-d5117df2fce9",
"transaction_hash": "3uRi73QxDrNmhAz9Mkxke7H8Xws2ZJmwUTUXWbgM2CHoejefWY32nXTq1vitximhRWX25vuRASWRC3iXChVvXiro"
}
After calling this endpoint, the transaction status changes to processing
.
Here's an example response:
{
"id": "0d053970-ed1f-11ef-80a0-81ef33c9f312",
"status": "processing",
"type": "off_ramp",
"partner_id": "7bcc2530-86a4-4cee-9382-820e12d2d7ea",
"quotation": "5.6755",
"sender": {
"currency": "USDC",
"payment_rail": "solana",
"address": "3fFnisw8zsFhyQopBH1hLLshJEPs8tCysESSLTvTGjWk"
},
"receiver": {
"amount": 28.34,
"currency": "BRL",
"payment_rail": "pix",
"pix_key": "21981419836",
"pix_key_type": "PHONE",
"pix_end_to_end_id": "E3513612020250217110500000886939",
"bank_account": {
"bank_name": "NU PAGAMENTOS - IP",
"bank_code": null,
"bank_account_number": null,
"beneficiary": {
"document": "12345678900",
"name": "Satoshi Nakamoto"
}
}
},
"receipt": {
"initial_crypto_amount": 5,
"final_fiat_amount": 28.34,
"unblockpay_fee": 0.15
},
"created_at": "2025-02-17T11:05:09.000Z",
"updated_at": "2025-02-17T11:05:09.000Z",
"finished_at": null
}

Once we confirm the stablecoin deposit, we will send a webhook with the event type payout.processing
.
3. View transaction details: GET /transactions/{id}Copied!
This endpoint allows you to check the status and details of a transaction at any time. You'll need the transaction ID that was returned when you initiated the payout.
Here's an example response showing a completed
payout:
{
"id": "b11f8920-ece3-11ef-8e88-2d5fe9bab89b",
"status": "completed",
"type": "off_ramp",
"partner_id": "7bcc2530-86a4-4cee-9382-820e12d2d7ea",
"quotation": "5.6846",
"sender": {
"amount": 5,
"currency": "USDC",
"payment_rail": "solana",
"address": "3fFnisw8zsFhyQopBH1hLLshJEPs8tCysESSLTvTGjWk",
"transaction_hash": "2FzBAQickeHCL9nS3qcCyfZok7xJtuoqpg73hJqdkNdCTratjNqnc1jET3h7kApz7ayaHTqeVWyTi9AzD1aoCFfu"
},
"receiver": {
"amount": 28.34,
"currency": "BRL",
"payment_rail": "pix",
"pix_key": "21981419836",
"pix_key_type": "PHONE",
"pix_end_to_end_id": "E3513612020250217040000000886541",
"bank_account": {
"bank_name": "ITAÚ UNIBANCO S.A.",
"bank_code": "60701190",
"bank_account_number": "00076210",
"beneficiary": {
"name": "Satoshi Nakamoto",
"document": "12345678900"
}
}
},
"receipt": {
"initial_crypto_amount": 5,
"final_fiat_amount": 28.34,
"unblockpay_fee": 0.15
},
"created_at": "2025-02-17T04:00:14.514Z",
"updated_at": "2025-02-17T04:02:09.695Z",
"finished_at": "2025-02-17T04:02:09.676Z"
}
Here's an example response showing a processing
payout:
{
"id": "11111111-1111-1111-1111-111111111111",
"status": "processing",
"type": "off_ramp",
"partner_id": "7bcc2530-86a4-4cee-9382-820e12d2d7ea",
"quotation": "5.6764",
"sender": {
"amount": 5,
"currency": "USDC",
"payment_rail": "solana",
"address": "8rdeWPM7n9bDzpaQnQou8PjAsCpYXVJ6HArLBEYjjG7A",
"transaction_hash": "5ciPwdnthPNjZVVejMRXtLmFFD4qbNzRn1zTVe5LZar4M7fyXtQH6yRbXHdmPKiVsr9jNGdWeDFAhBCK1JhjqCvU"
},
"receiver": {
"amount": 28.34,
"currency": "BRL",
"payment_rail": "pix",
"pix_key": "21972419836",
"pix_key_type": "PHONE",
"pix_end_to_end_id": "E3513612020250217120400000887098",
"bank_account": {
"bank_name": null,
"bank_code": null,
"bank_account_number": "00086310",
"beneficiary": {
"name": "Satoshi Nakamoto",
"document": "12345678900"
}
}
},
"receipt": {
"initial_crypto_amount": 5,
"final_fiat_amount": 28.34,
"unblockpay_fee": 0.15
},
"created_at": "2025-01-15T12:04:22.010Z",
"updated_at": "2025-01-15T12:06:31.120Z",
"finished_at": null
}
4. Cancel a transaction: PUT /transactions/{id}Copied!
This endpoint allows you to cancel a transaction that is still in the awaiting_deposit
status. You cannot cancel payouts that are in other statuses. Once cancelled, the transaction cannot be resumed and you'll need to create a new one if needed.
Here's an example response showing a cancelled
payout:
{
"id": "4d7196d0-ed6e-11ef-8fc7-7b1bb8f06792",
"status": "cancelled",
"type": "off_ramp",
"partner_id": "7bcc2530-86a4-4cee-9382-820e12d2d7ea",
"quotation": "5.6896",
"sender": {
"amount": 5,
"currency": "USDC",
"payment_rail": "solana",
"address": "FvjyJUVeahfEbprqsDAaAvbJ6riFcgoh2v8qqjLJbJU5",
"transaction_hash": null
},
"receiver": {
"amount": 28.34,
"currency": "BRL",
"payment_rail": "pix",
"pix_key": "11122233344",
"pix_key_type": "CPF",
"pix_end_to_end_id": "E3513612020250217162700000888283",
"bank_account": {
"bank_name": "TESTE BANCO S.A",
"bank_code": null,
"bank_account_number": null,
"beneficiary": {
"name": "Testando Teste",
"document": "11122233344"
}
}
},
"receipt": {
"initial_crypto_amount": 5,
"final_fiat_amount": 28.34,
"unblockpay_fee": 0.15
},
"created_at": "2025-02-17T20:32:27.325Z",
"updated_at": "2025-02-17T20:54:45.578Z",
"finished_at": null
}

After canceling a payout, we will send a webhook with the event type payout.cancelled
.