Payout Beneficiary

Resolve who owns a PIX key and lock the price of the payout to it, then pay against the quoteId.

Overview

POST /api/wallet/payout/beneficiary answers the two questions /api/wallet/payout cannot answer before it is too late: who the PIX key belongs to, and what the payout will cost. It returns the payee's name, masked tax id and bank, the full price, and a quoteId you pass to the payout — which then settles exactly what was quoted.

Nothing is debited here. No transaction is created, no PIX is paid, and calling it twice costs nothing but a second quote.

A quote is a lock, not an estimate. /api/wallet/payout reads the fee, the FX rate, the asset and the destination from the quote instead of pricing them again. This is what makes it different from POST /api/quote, which is indicative pricing for any pair and reserves nothing.

Only a PIX key is quotable. A BR Code already carries its own amount and payee — pay it directly with qrCode on /api/wallet/payout.

Flow

  1. POST /api/wallet/payout/beneficiary — show the user the name that came back, and the fee.
  2. POST /api/wallet/payout with quoteId + walletPin — settles at the quoted price.
  3. GET /api/wallet/payout/{transactionId} — poll until COMPLETED or FAILED.

The quote expires 5 minutes after it is issued, and is consumed by the payout that uses it. An expired, unknown or already used quoteId answers 400 — quote again.

Request

curl --request POST \
  --url https://api.hodle.com.br/api/wallet/payout/beneficiary \
  --header "Authorization: Bearer $API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "value": 5000,
    "network": "base",
    "asset": "BRLA",
    "pixKey": "[email protected]",
    "pixKeyType": "EMAIL"
  }'
const res = await fetch('https://api.hodle.com.br/api/wallet/payout/beneficiary', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HODLE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    value: 5000,
    network: 'base',
    asset: 'BRLA',
    pixKey: '[email protected]',
    pixKeyType: 'EMAIL',
  }),
})
const quote = await res.json()
import os, requests

res = requests.post(
    "https://api.hodle.com.br/api/wallet/payout/beneficiary",
    headers={
        "Authorization": f"Bearer {os.environ['HODLE_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "value": 5000,
        "network": "base",
        "asset": "BRLA",
        "pixKey": "[email protected]",
        "pixKeyType": "EMAIL",
    },
)
quote = res.json()

Parameters

FieldTypeRequiredDescription
valueintegerYesAmount the beneficiary receives, in BRL cents. Min 10. The fee is charged on top of it.
networkstringYespolygon, base, tron or solana.
assetstringNoStablecoin to debit. Defaults to the network default. A quote does not fall back to a second asset the way the payout does — it names the one it priced.
pixKeystringYesPIX key of the beneficiary.
pixKeyTypestringYesPHONE, CPF, EMAIL, RANDOM or CNPJ.
taxIdstringNoBeneficiary CPF/CNPJ. Needed when paying a third party — see Third-party operations.
subAccountIdstringNoThe subaccount the payout is for. Recorded on the quote; /api/wallet/payout has to be called with the same one.

Response

200 OK
{
  "success": true,
  "status": "PENDING",
  "quoteId": "b0f1c7de-9f2b-4a71-9c1e-2f4e2a1d6c88",
  "expiresAt": "2026-08-31T14:35:00.000Z",
  "settlement": "OWN_ACCOUNT",
  "network": "base",
  "asset": "BRLA",
  "pixKey": "[email protected]",
  "pixKeyOwner": {
    "name": "Thiago Mota dos Santos",
    "taxId": "545.***.***-10",
    "bankName": "NU PAGAMENTOS - IP"
  },
  "quote": {
    "inputAmount": 50.75,
    "inputCurrency": "BRLA",
    "outputAmount": 50,
    "outputCurrency": "BRL",
    "rate": 1,
    "fee": 0.75
  },
  "payoutId": "6a878236a83b0a4e8cc8c40b",
  "correlationId": "b0f1c7de-9f2b-4a71-9c1e-2f4e2a1d6c88"
}
FieldDescription
statusPENDING — the payout is already reserved at the settling account and is waiting to be paid. QUOTED — a price, with nothing reserved behind it. See Reserved payouts.
quoteIdPass as quoteId on /api/wallet/payout.
expiresAtWhen the locked price stops being honoured (5 minutes).
settlementWhich account settles the PIX: OWN_ACCOUNT (the account opened in your name) or HODLE.
pixKeyOwnerWho the key belongs to, as the provider reports it. taxId is masked at the source — show it to the user, do not try to match it.
payoutIdId of the reserved payout at the settling account. Only when status is PENDING.
correlationIdIdempotency key of that reserved payout. Only when status is PENDING.

Reading the quote

FieldMeaning
inputAmountStablecoin debited from the wallet: value + fee, converted at rate.
outputAmountBRL the beneficiary receives — exactly value.
rateBRL per unit of inputCurrency. 1 for the BRL-pegged assets (BRLA, BRS).
feeHodle's fee, in BRL. Already includes any affiliate discount your account carries.

Paying against the quote

Send the quoteId with the wallet credentials. pixKey and pixKeyType may be omitted — they come from the quote:

curl --request POST \
  --url https://api.hodle.com.br/api/wallet/payout \
  --header "Authorization: Bearer $API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "value": 5000,
    "network": "base",
    "quoteId": "b0f1c7de-9f2b-4a71-9c1e-2f4e2a1d6c88",
    "walletPin": "1234",
    "protectedSymmetricKey": "AoofiKHyVRLvdrknnXzo..."
  }'

What the payout takes from the quote: the fee, the FX rate, the asset, the destination, and — when the payout was already reserved — the reservation it releases. What you send is only checked against it: value, network, asset, pixKey and subAccountId must agree with the quote or the request is refused with, for example, "value does not match the quote".

quoteId needs walletPin. A quoted payout is signed by Hodle. The sign-on-your-own-device legs (payoutIntentId + signature) carry their own quote and do not take a quoteId.

Reserved payouts (status: PENDING)

When the PIX is settled by an account opened in your name (settlement: "OWN_ACCOUNT"), the quote does more than price the payout: it reserves it, and /api/wallet/payout then only releases the reservation. That is what status: "PENDING" means, and it is why payoutId comes back — the payout already exists, waiting to be paid. One round trip less at the moment the money moves, and no way for the payout to come out at a different price than the one you were quoted.

A reservation needs the settling account to already hold the balance, which an account that keeps no float does not: the stablecoin only arrives with the debit, at settlement time. The quote then answers status: "QUOTED" instead — a price, with nothing reserved behind it. The quoteId works exactly the same way; the payout is simply created when it is paid, as it always has been.

A reserved payout that is never paid stays PENDING. There is no cancel route for it. Quote when the user is about to pay, not speculatively.

Errors

StatusWhen
400Validation failure, or a PIX key the DICT does not resolve.
401Missing or invalid API key.
403Account blocked, missing the WALLET_PAYOUT_API flag, over a limit, blocked PIX key, or a third-party payout not enabled.
429Quote cooldown for this account.
502The FX rate could not be read.
503PIX payments are temporarily disabled, or the quote could not be stored.

Gated by the same WALLET_PAYOUT_API per-user feature flag as /api/wallet/payout.