Stablecoin to Fiat (Payout)
Understanding a PayoutCopied!
UnblockPay's API allows you to seamlessly convert stablecoins into fiat currencies and send them via local payment rails, like Pix in Brazil and SPEI® in Mexico. We call this process a Payout (also known as off-ramp).
At a high level, executing a payout involves the following steps:
-
First, the customer or partner requests a quote.
-
Next, the customer (or partner acting on their behalf) sends stablecoins to UnblockPay's designated wallet address.
-
Once received, UnblockPay converts stablecoins to fiat money.
-
Finally, we send the converted fiat amount directly to the recipient's bank account via local transfer.
To create a payout, first you need to create a Customer and an External Account.
Payout API endpointsCopied!
Here are the endpoints you'll need to create, list or cancel a payout transaction:
-
POST /quote
: Create a stablecoin/fiat quote -
POST /payout
: Initiate stablecoin to fiat payout transaction:-
When the wallet sender address is known
-
POST/payout/confirm
When the wallet sender address is unknown
-
-
GET /transactions/{id}
: View details of a specific transaction -
PUT /transactions/{id}
: Cancel a transaction (only available for transactions withawaiting_deposit
status)
Watch this video to quickly understand how our payout API works.
How to create or manage a Payout?Copied!
Step 1: Create a quoteCopied!
POST /quote
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:
curl https://api.unblockpay.com/quote \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: YOUR_SECRET_TOKEN' \
--data '{
"symbol": "USDC/BRL"
}'
Here's an example response:
{
"id": "bfc36f00-ed1f-11ef-9fe4-378e29e10831",
"quotation": "5.6766",
"symbol": "USDC/BRL",
"expires_at": 1739790908
}
Step 2: Initiate stablecoin to fiat payoutCopied!
POST /payout
Option 1: When the wallet sender address is known
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.
For sending money through Pix, creating an External Account is not mandatory.
You can simply pass the receiver pix_key
and document
the on the payout request.For all other fiat currencies, you must create an External Account before initiating payout.
For example, when a customer wants to send USDCs to a bank account in Mexico, they must first register that account through our External Accounts endpoint. After registration, they simply include the External Account ID when initiating a payout.
Here's an example request sending external_account_id
:
curl https://api.unblockpay.com/payout \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: YOUR_SECRET_TOKEN' \
--data '{
"amount": 5.00,
"quote_id": "bfc36f00-ed1f-11ef-9fe4-378e29e10831",
"sender": {
"currency": "USDC",
"payment_rail": "solana",
"address": "3fFnisw8zsFhyQopBH1hLLshJEPs8tCysESSLTvTGjWk"
},
"receiver": {
"external_account_id": "b11f8920-ece3-11ef-8e88-2d5fe9bab89b"
}
}'
Here's an example request passing pix_key
and document
(only valid for Pix in Brazil):
curl https://api.unblockpay.com/payout \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: YOUR_SECRET_TOKEN' \
--data '{
"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"
}
}'

After initiating a payout, we will send a webhook with the event type payout.created
.
The sender
object will include:
Parameter |
Definition |
Example |
---|---|---|
currency |
Blockchain token you want to convert into fiat. |
|
payment_rail |
Blockchain network where the transaction will occur. |
|
address |
The wallet address from which you are sending your tokens. |
|
The receiver
object will include:
Parameter |
Definition |
Example |
---|---|---|
currency |
The fiat currency you want to receive the payout. |
|
payment_rail |
Local payment network through which the payout will be sent. |
|
external_account_id |
The external account unique identifier associated with a customer. |
|
pix_key (only for Pix) |
Unique identifier used for Pix transfers in Brazil's banking system, which can be a:
|
|
document |
The tax ID or government-issued number from the receiver. |
|
For payouts, if you are not initiating the payout from an UnblockPay's wallet, we cannot pull funds directly from the sender wallet, so we provide sender_deposit_instructions
containing deposit addresses where your customer (or you acting on their behalf) can initiate the transaction. Once your customer (or you acting on their behalf) completes the stablecoin deposit, fiat funds automatically move to the specified receiver without needing additional API calls.
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 transaction will be created in an awaiting_deposit
status, and you'll receive deposit instructions for sending stablecoins.
The sender_deposit_instructions
will include:
Parameter |
Definition |
Example |
---|---|---|
amount |
The exact amount of stablecoins you need to send to the |
|
currency |
Blockchain token you want to convert into fiat. |
|
payment_rail |
The blockchain network for initiating the payout. |
|
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 stablecoin deposit, we'll automatically process the conversion and initiate the fiat transfer to the receiver.
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.
Pool/Jumbo Wallet scenario
The scenario below only applies when deposits come from the same wallet address (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.
Option 2: When the wallet sender address is unknown
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.
Example:
curl https://api.unblockpay.com/payout \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: YOUR_SECRET_TOKEN' \
--data '{
"amount": 5.00,
"quote_id": "bfc36f00-ed1f-11ef-9fe4-378e29e10831",
"sender": {
"currency": "USDC",
"payment_rail": "solana"
},
"receiver": {
"external_account_id": "b11f8920-ece3-11ef-8e88-2d5fe9bab89b"
}
}'
To confirm the stablecoin deposit, you will need to call POST/payout/confirm
, including the transaction_id
and the transaction_hash
.
Here's an example request:
curl https://api.unblockpay.com/payout/confirm \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: YOUR_SECRET_TOKEN' \
--data '{
"transaction_id": "246c6260-faa7-11ef-b04c-d5117df2fce9",
"transaction_hash": "3uRi73QxDrNmhAz9Mkxke7H8Xws2ZJmwUTUXWbgM2CHoejefWY32nXTq1vitximhRWX25vuRASWRC3iXChVvXiro"
}'
Understanding transaction_id
and transaction_hash
:
Parameter |
Definition |
Example |
---|---|---|
|
A unique identifier that identifies each UnblockPay payout transaction. |
|
|
The blockchain transaction ID that identifies the stablecoin transfer to UnblockPay's wallet. You can get the |
|
This endpoint should only be called after sending the tokens to the deposit_address
provided in the sender_deposit_instructions
.
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
.
Step 3: View transaction detailsCopied!
GET /transactions/{id}
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
}
Step 4: Cancel a transactionCopied!
PUT /transactions/{id}
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
.