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/payoutreads the fee, the FX rate, the asset and the destination from the quote instead of pricing them again. This is what makes it different fromPOST /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
POST /api/wallet/payout/beneficiary— show the user the name that came back, and the fee.POST /api/wallet/payoutwithquoteId+walletPin— settles at the quoted price.GET /api/wallet/payout/{transactionId}— poll untilCOMPLETEDorFAILED.
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
| Field | Type | Required | Description |
|---|---|---|---|
value | integer | Yes | Amount the beneficiary receives, in BRL cents. Min 10. The fee is charged on top of it. |
network | string | Yes | polygon, base, tron or solana. |
asset | string | No | Stablecoin 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. |
pixKey | string | Yes | PIX key of the beneficiary. |
pixKeyType | string | Yes | PHONE, CPF, EMAIL, RANDOM or CNPJ. |
taxId | string | No | Beneficiary CPF/CNPJ. Needed when paying a third party — see Third-party operations. |
subAccountId | string | No | The subaccount the payout is for. Recorded on the quote; /api/wallet/payout has to be called with the same one. |
Response
{
"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"
}| Field | Description |
|---|---|
status | PENDING — 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. |
quoteId | Pass as quoteId on /api/wallet/payout. |
expiresAt | When the locked price stops being honoured (5 minutes). |
settlement | Which account settles the PIX: OWN_ACCOUNT (the account opened in your name) or HODLE. |
pixKeyOwner | Who 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. |
payoutId | Id of the reserved payout at the settling account. Only when status is PENDING. |
correlationId | Idempotency key of that reserved payout. Only when status is PENDING. |
Reading the quote
| Field | Meaning |
|---|---|
inputAmount | Stablecoin debited from the wallet: value + fee, converted at rate. |
outputAmount | BRL the beneficiary receives — exactly value. |
rate | BRL per unit of inputCurrency. 1 for the BRL-pegged assets (BRLA, BRS). |
fee | Hodle'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".
quoteIdneedswalletPin. A quoted payout is signed by Hodle. The sign-on-your-own-device legs (payoutIntentId+signature) carry their own quote and do not take aquoteId.
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
| Status | When |
|---|---|
400 | Validation failure, or a PIX key the DICT does not resolve. |
401 | Missing or invalid API key. |
403 | Account blocked, missing the WALLET_PAYOUT_API flag, over a limit, blocked PIX key, or a third-party payout not enabled. |
429 | Quote cooldown for this account. |
502 | The FX rate could not be read. |
503 | PIX 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.