UnblockPayGuideReference

Stablecoin to fiat payout (crypto off-ramp)

Payout API endpointsCopied!

Here are the endpoints you'll need to complete or cancel a payout transaction:

  1. POST /quote: Create a stablecoin/fiat quote

  2. POST /payout: Initiate stablecoin to fiat payout:

    1. When the wallet sender address is known

    2. When the wallet sender address is unknown

  3. GET /transactions/{id}: View transaction details

  4. PUT /transactions/{id}: Cancel a transaction (only available for transactions with awaiting_deposit status)


How Funds Flow?Copied!

Here's how funds flow through UnblockPay's payout process:

  1. First, the customer requests a quote.

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

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

  4. 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 or USDT).

  • payment_rail: Blockchain network where the transaction will occur (i.e. solana or ethereum).

  • 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 or MXN).

  • payment_rail: Local payment network through which the payout will be sent (e.g. pix or spei).

  • 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 or dcta478j-196l-03fm-t6gh-4298er7845m2).

  • document: the receiver tax ID (e.g. 14965065700 for individuals or 58444469000108 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 or USDT).

  • payment_rail: The blockchain network name (e.g. solana or ethereum).

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