UnblockPayGuideReference

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:

  1. First, the customer or partner requests a quote.

  2. Next, the customer (or partner acting on their behalf) sends stablecoins to UnblockPay's designated wallet address.

  3. Once received, UnblockPay converts stablecoins to fiat money.

  4. 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:

    1. When the wallet sender address is known

    2. 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 with awaiting_deposit status)

YouTube

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.

USDC

USDT

payment_rail

Blockchain network where the transaction will occur.

solana

ethereum

address

The wallet address from which you are sending your tokens.

3fFnisw8zsFhyQopBH1hLLshJEPs8tCysESSLTvTGjWk

The receiver object will include:

Parameter

Definition

Example

currency

The fiat currency you want to receive the payout.

USDC

USDT

payment_rail

Local payment network through which the payout will be sent.

solana

ethereum

external_account_id

The external account unique identifier associated with a customer.

0196c1ab-a142-77a9-aa5f-38b3489393fa

pix_key

(only for Pix)

Unique identifier used for Pix transfers in Brazil's banking system, which can be a:

  • Phone number

  • Tax ID (CPF or CNPJ)

  • Email address

  • Random key

  • Phone number: 21981419836

  • Tax ID (CPF or CNPJ): 14965065700 or 58444469000108

  • Email address: satoshi@email.com

  • Random key: dcta478j-196l-03fm-t6gh-4298er7845m2

document

The tax ID or government-issued number from the receiver.

14965065700

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 deposit_address.

5

10000

currency

Blockchain token you want to convert into fiat.

USDC

USDT

payment_rail

The blockchain network for initiating the payout.

solana

ethereum

deposit_address

UnblockPay's wallet address where you should send stablecoins.

7PWWVJ3qLTbct4ucxpKXrfoZt39thmbzrnKqA9NnoGn5

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

transaction_id

A unique identifier that identifies each UnblockPay payout transaction.

246c6260-faa7-11ef-b04c-d5117df2fce9

transaction_hash

The blockchain transaction ID that identifies the stablecoin transfer to UnblockPay's wallet.

You can get the transaction_hash by consulting a blockchain explorer, like Solscan for Solana network and Etherscan for Ethereum network.

3uRi73QxDrNmhAz9Mkxke7H8Xws2ZJmwUTUXWbgM2CHoejefWY32nXTq1vitximhRWX25vuRASWRC3iXChVvXiro

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.