Deposit Asset
Create a deposit via Lightning, USDT, USDC, USDCE, BRLA, or BRS.
POST /api/deposit/asset
Create a deposit that converts BRL to the specified crypto asset. For BRS, the asset is delivered to the user's own Hodle Solana wallet, or to any Solana address when external delivery is enabled on the account — ask support (see BRS to an external Solana wallet); the other assets are sent to the provided address.
Request
curl --request POST \
--url https://api.hodle.com.br/api/deposit/asset \
--header "Authorization: Bearer $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"value": 5000,
"address": "lnbc500u1pj...",
"asset": "LIGHTNING",
"externalId": "my-order-123"
}'const res = await fetch('https://api.hodle.com.br/api/deposit/asset', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HODLE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
value: 5000,
address: 'lnbc500u1pj...',
asset: 'LIGHTNING',
externalId: 'my-order-123',
}),
})
const data = await res.json()import os, requests
res = requests.post(
"https://api.hodle.com.br/api/deposit/asset",
headers={
"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}",
"Content-Type": "application/json",
},
json={
"value": 5000,
"address": "lnbc500u1pj...",
"asset": "LIGHTNING",
"externalId": "my-order-123",
},
)
data = res.json()Body example
{
"value": 5000,
"address": "lnbc500u1pj...",
"asset": "LIGHTNING",
"externalId": "my-order-123"
}Parameters
| Field | Type | Required | Description |
|---|---|---|---|
value | integer | Yes | Amount in BRL cents. Minimum 100 (R$1.00). |
address | string | Cond. | Destination address. Required for LIGHTNING, USDT, USDC, USDCE, and BRLA. For BRS, omit it (or send one of the account's own Solana wallets) to receive in the account's Hodle wallet; any other Solana address requires external delivery enabled on the account (ask support) plus walletPin and protectedSymmetricKey. |
asset | string | Yes | Asset type: LIGHTNING, USDT, USDC, USDCE, BRLA, BRS, DEPIX, or BTC. |
network | string | Cond. | On-chain network for the asset. Required for USDT, USDC, USDCE, and BRLA; BRS supports solana (the default when omitted) and base (production only). Not needed for LIGHTNING. See Assets & Networks. |
externalId | string | No | Your own ID for reconciliation and idempotency key. Sending the same externalId again returns the original deposit (same walletCharge and qrCode) instead of creating a new one — see Idempotency. A UUID is generated if not provided. |
subAccountId | string | No | The subaccount this deposit belongs to (scoped to your API key — an id you do not own is refused with 404). Defaults to the main account. What it does depends on how your account is set up — see What subAccountId does. |
walletPin | string | Cond. | BRS to an external address only. The platform PIN of the wallet that pays the delivery fee, as in /api/wallet/transfer. Used in memory to sign the fee debit and discarded. |
protectedSymmetricKey | string | Cond. | BRS to an external address only. That wallet's protectedSymmetricKey. |
walletId | string | No | BRS to an external address only. Which of the account's wallets pays the delivery fee. Defaults to the default wallet (of the subAccountId when one is sent). |
maxDeliveryFeeCents | integer | No | BRS to an external address only. The highest delivery fee you accept, in BRL cents. A live fee above it is refused with DELIVERY_FEE_ABOVE_MAX before anything is signed. |
expiresIn | integer | No | Seconds until the Pix code stops being payable. Default 900 (15 minutes); accepted range 60 to 86400 (24 hours). The response returns the resulting expiresAt. See QR code expiration. |
taxId | string | No | CPF/CNPJ of the payer, when the payer is not the account holder. Digits only or masked. Rejected with 403 when third-party operations are not enabled for the account. On its own it does not let a third party pay the charge — see Who can pay the PIX. |
QR code expiration
Every Pix code is created with a lifetime of expiresIn seconds, 15 minutes unless you
send another value, and the response carries the moment it ends as expiresAt (ISO 8601):
{
"value": 5000,
"address": "0x8f3c...",
"asset": "USDC",
"network": "base",
"externalId": "order-2042",
"expiresIn": 1800
}{
"success": true,
"externalId": "order-2042",
"walletCharge": "66fc0b7e2a4b5c6d7e8f9013",
"qrCode": "00020101021226...",
"expiresAt": "2026-10-05T15:30:00.000Z",
"fee": 100,
"fxRateAtTx": 5.32
}- After
expiresAtthe Pix code stops being payable and the deposit moves toEXPIRED. Create a new deposit (with a newexternalId) to give your customer a fresh code. expiresAtisnullwhen the rail that serves the deposit sets its own lifetime for the Pix code;expiresInis then ignored.- Repeating an
externalIdreturns the original deposit with its originalexpiresAt; a newexpiresIndoes not extend it. expiresInoutside60–86400answers400withValidation failed.
What subAccountId does
What the field selects depends on how your integration is set up. There are two setups, and Hodle configures which one your account is on:
Your customers ramp through the partner. The deposit is settled on that subaccount's own account at the regulated partner, under their KYC. This is why an asset/network the subaccount does not support is refused here rather than quietly falling back to your own account — settling a customer's money on your balance is not the same operation.
Your customers are onboarded through Hodle's partner KYC, but your ramp settles elsewhere. Your subaccounts have no account of their own to receive a deposit, so the field does not select one. The deposit runs on your own account — your limits, fees and KYC, with every asset you normally support — and the id is recorded on the charge, so you can still tell which of your customers each deposit came from.
Ownership is checked either way: a subaccount id you do not own is refused with 404. The
same setup governs POST /api/wallet/payout. Ask support if you are
not sure which one your account is on.
Who can pay the PIX
The PIX charge returned by this endpoint can only be paid by the CPF/CNPJ that owns the
account the deposit belongs to — the subaccount holder when subAccountId selects the
subaccount's own account, your own account otherwise. A PIX
sent by anyone else is refused by the bank at payment time and no crypto is delivered.
To accept a PIX paid by a third party — for example when you charge your own end customer on your account — ask support to enable third-party deposits on the account.
Assets & Networks
| Asset | Networks | Address format |
|---|---|---|
LIGHTNING | lightning | Lightning invoice or LNURL email |
USDT | polygon, arbitrum, solana | EVM address (0x...); Solana address on solana |
USDC | polygon, base, gnosis, solana | EVM address (0x...); Solana address on solana |
USDCE | gnosis | EVM address (0x...) |
BRLA | polygon, base | EVM address (0x...) |
BRS | solana, base | Own Hodle Solana wallet (any Solana address with external delivery — ask support); on base, the account's own EVM wallet |
DEPIX | spark, liquid | Spark address (spark1...) or Liquid address |
BTC | spark | Spark address (spark1...) |
Available networks per asset can depend on your account configuration and KYC level. For the full matrix across every endpoint see Assets & Networks.
USDT and USDC on solana are officially supported and delivered to any Solana
wallet address you send; Solana delivery must be enabled on your account — ask support.
See USDC or USDT to a Solana wallet for the full flow.
BRS must be enabled on your account. By default it is delivered to the user's own Hodle
Solana wallet; with external delivery enabled (ask support) it can go to any Solana address, paying a
delivery fee — see BRS to an external Solana wallet
and BRS.
Address Formats
For LIGHTNING assets, the address can be:
- A Lightning invoice starting with
lnbc,lntb, orlnbcrt - An LNURL email in the format
[email protected]
For USDT, USDC, USDCE, and BRLA assets, the address is an EVM address (0x...) on the selected network, or a base58 Solana address when network is solana.
For BRS, omit address to receive in the account's own Hodle Solana wallet, or send a
base58 Solana address to deliver there (external delivery must be enabled on the account — ask support). With
network: "base", omit address to receive in the account's own Hodle Base wallet, or send
one of the account's own EVM addresses; BRS on Base is not available on the sandbox
(403, errorCode: "BRS_BASE_PRODUCTION_ONLY"). See BRS.
USDC or USDT to a Solana wallet
A Pix in BRL can be delivered as USDC or USDT to any Solana wallet, including one
outside Hodle. You only call this endpoint; Hodle settles the purchase and bridges it to
Solana for you. There is no extra parameter for the bridge.
Before the first deposit
- Solana delivery is enabled on your account. It is a per-account setting — ask support.
Without it the request is refused with
400,errorCode: "DEPOSIT_FAILED"and"error": "Recurso depósito Solana indisponível", and no Pix is created. - Quote it.
POST /api/quotewithoutputPaymentMethod: "SOLANA". The quote is priced on the Base leg and saysbridgeFeeIncluded: false: the bridge cost is taken on delivery, so the amount that lands on Solana is slightly belowoutputAmount. - Send the wallet address, base58, the one you would paste into any Solana wallet — not a token account. The destination does not need to hold USDC already.
{
"value": 5000,
"asset": "USDC",
"network": "solana",
"address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
"externalId": "order-2041"
}{
"success": true,
"externalId": "order-2041",
"walletCharge": "66fc0b7e2a4b5c6d7e8f9012",
"qrCode": "00020101021226...",
"expiresAt": "2026-10-05T14:15:00.000Z",
"fee": 100,
"fxRateAtTx": 5.32
}qrCode is a regular Pix charge for value (R$ 50,00 here); fee is the Hodle fee in BRL
cents. The deposit then moves through
these events on your webhook:
| Step | Event | What it means |
|---|---|---|
| Pix paid | DEPOSIT_PIX_CONFIRMED | The Pix settled. Safe to release your customer's order. |
| Delivered | DEPOSIT_ASSET_SUCCESS | The asset landed on address, usually about a minute after the Pix. valueInCurrency is the amount delivered. |
| Not delivered | DEPOSIT_ASSET_FAILED | The Pix was paid but the asset could not be delivered. The refund is confirmed later by DEPOSIT_ASSET_REFUNDED. |
The Solana transaction hash of the delivery and the exact amount received are on
GET /api/deposit/{id} once the deposit completes, so you can show it on
any Solana explorer.
| Status | Body | Meaning |
|---|---|---|
400 | "Address is required for USDC deposits" | address missing. |
400 | "Endereço Solana inválido" | address is not a valid Solana address. |
400 | "Recurso depósito Solana indisponível" | Solana delivery is not enabled on the account. |
400 | "Unsupported asset/network combination: USDC/SOLANA" | The account has no route that delivers this pair. Ask support. |
USDT works the same way with "asset": "USDT". Sandbox does not deliver on Solana (its test
stables are minted on Base Sepolia), so validate this route in production with a small amount.
BRS to an external Solana wallet
When external delivery is enabled on the account (ask support), a BRS deposit can deliver to any Solana address. The buyer still receives the full amount (Pix R$ 100 → 100 BRS at the destination); the delivery has its own fee, paid in BRS from the account's own Hodle Solana wallet and debited when the deposit is created, before the Pix exists.
The fee is priced live per delivery and is higher the first time an address receives BRS:
| Destination | Fee (in BRS)* |
|---|---|
| Never held BRS | 1,44 BRS |
| Already holds BRS | 0,52 BRS |
* Reference values from 2026-09-30; the live fee follows the Solana network cost. Preview it
with POST /api/quote and the same address.
Keep a BRS balance in the account's Hodle wallet. The fee is debited from it when the deposit is created, so a wallet without enough BRS refuses the deposit with
INSUFFICIENT_BRS_FOR_DELIVERY_FEE. We recommend keeping at least 2 BRS per external delivery you expect to create before topping up again.
Because the fee is debited from a self-custodial wallet, the request carries that wallet's
walletPin and protectedSymmetricKey, exactly as /api/wallet/transfer.
Send maxDeliveryFeeCents to cap what you accept.
{
"value": 10000,
"asset": "BRS",
"network": "solana",
"address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"externalId": "order-1029",
"walletPin": "424242",
"protectedSymmetricKey": "AoofiKHyVRLvdrknnXzoIh1Gd1YTwLaOBn4ibm103a4d...",
"maxDeliveryFeeCents": 200
}{
"success": true,
"externalId": "order-1029",
"walletCharge": "66fa1c0e9b1d4a0012ab34cd",
"qrCode": "00020101021226...",
"expiresAt": "2026-09-30T13:15:00.000Z",
"fee": 0,
"fxRateAtTx": 1,
"deliveryFee": {
"feeCents": 144,
"recipientHasTokenAccount": false,
"txHash": "5KqV...",
"expiresAt": "2026-09-30T13:15:00.000Z"
}
}- The Pix charge expires after
expiresIn(15 minutes by default;expiresAtanddeliveryFee.expiresAtcarry the same moment). If it is not paid, the fee goes back to the wallet that paid it in full, about 10 minutes after expiry. - A Pix that still lands after that refund is delivered to the account's own Hodle wallet, not to the external address.
- Retrying with the same
externalIdreturns the original deposit and does not debit the fee again.
| Status | errorCode | Meaning |
|---|---|---|
403 | BRS_EXTERNAL_DELIVERY_DISABLED | The account can only receive BRS in its own wallet. Ask support to enable external delivery. |
400 | DELIVERY_FEE_REQUIRED | External address without walletPin + protectedSymmetricKey. |
400 | DELIVERY_FEE_ABOVE_MAX | The live fee is above maxDeliveryFeeCents; the response carries quotedDeliveryFeeCents. Nothing was signed. |
400 | INSUFFICIENT_BRS_FOR_DELIVERY_FEE | The paying wallet holds less BRS than the fee. |
401 | INVALID_PIN | Wrong PIN (repeated failures lock the wallet for 24h). |
503 | DELIVERY_FEE_UNAVAILABLE | The fee could not be priced or debited right now; retry. |
Not available in sandbox.
End-to-end example
The same sequence was run in production on 2026-09-30: a R$ 10,00 BRS purchase delivered to an external address that already held BRS, with a 0,52 BRS delivery fee.
1. Have a Solana wallet with BRS to pay the fee. Any of the account's own Solana wallets
works. To create one just for this, call POST /api/wallet/create with
network: "solana", the platform walletPin, and the protectedSymmetricKey of the existing
wallet that this PIN protects:
{
"network": "solana",
"walletPin": "424242",
"protectedSymmetricKey": "<protectedSymmetricKey of the wallet this PIN protects>"
}{
"success": true,
"data": {
"id": "66fb2a10c4e8d91f0a3b7c55",
"address": "C7yBfCPY2kzknbsFiWeYsocUZAKTFKjYWGNdVhikgaUZ",
"network": "solana",
"balance": 0,
"protectedSymmetricKey": "<the NEW wallet's protectedSymmetricKey>"
}
}Keep data.id and the new protectedSymmetricKey; the deposit uses both. Then put BRS in
the wallet — for example with POST /api/wallet/transfer from another
of your wallets. The fee is taken from this balance, so it must hold at least deliveryFee.feeCents.
403 "walletPin does not match the PIN registered on the platform"— withoutprotectedSymmetricKey, the PIN is checked against the account's oldest protected wallet, which may use a different PIN. Send theprotectedSymmetricKeyof the wallet you know the PIN of. Three wrong PINs lock the account's wallet operations for 24 hours (429).
2. Preview the fee. POST /api/quote with the destination address.
Nothing is signed or debited:
{
"inputCurrency": "BRL",
"inputPaymentMethod": "PIX",
"outputCurrency": "BRS",
"outputPaymentMethod": "SOLANA",
"inputAmount": "10.00",
"address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
}{
"outputAmount": "10.00",
"settlement": { "deliveryNetwork": "SOLANA", "destination": "EXTERNAL_SOLANA_WALLET" },
"deliveryFee": {
"feeCents": 52,
"recipientHasTokenAccount": true,
"paidIn": "BRS",
"paidFrom": "OWN_HODLE_SOLANA_WALLET"
}
}3. Create the deposit with the wallet from step 1 (walletId + its own
protectedSymmetricKey) and a cap above the previewed fee:
{
"value": 1000,
"asset": "BRS",
"network": "solana",
"address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"externalId": "order-1030",
"walletId": "66fb2a10c4e8d91f0a3b7c55",
"walletPin": "424242",
"protectedSymmetricKey": "<the NEW wallet's protectedSymmetricKey>",
"maxDeliveryFeeCents": 200
}{
"success": true,
"externalId": "order-1030",
"walletCharge": "66fb2a4be1d07c3a9f12de80",
"qrCode": "00020101021226810014br.gov.bcb.pix...",
"expiresAt": "2026-09-30T17:50:53.180Z",
"fee": 0,
"fxRateAtTx": 1,
"deliveryFee": {
"feeCents": 52,
"recipientHasTokenAccount": true,
"txHash": "3uVw8AsoGMbVLPVZh1pqUoyw1zp3qkLsNf3uos5gT4vnDq2xzyZxwHPCZRKrHd1CSYr6XJGsppk7PPkm261HP79v",
"expiresAt": "2026-09-30T17:50:53.180Z"
}
}4. Check the fee on-chain. deliveryFee.txHash is a Solana transaction you can open on any
explorer: one SPL transferChecked of feeCents / 100 BRS from the wallet of step 1 to Hodle,
with Hodle as fee payer (your wallet spends no SOL). In the run above the wallet went from
2 BRS to 1.48 BRS.
5. Pay the Pix. The qrCode is a regular Pix charge for value; once paid, the full
10 BRS are delivered to address. If it is not paid by deliveryFee.expiresAt, the 0,52 BRS
go back to the wallet of step 1 about 10 minutes later.
Response
{
"success": true,
"externalId": "my-order-123",
"qrCode": "lnbc500u1pj...",
"expiresAt": "2026-10-05T14:15:00.000Z",
"fee": 100,
"fxRateAtTx": 408000.50,
"walletCharge": "charge_abc123"
}Fields
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the deposit was created. |
externalId | string | The external ID for reconciliation (your value or auto-generated UUID). |
qrCode | string | null | QR code for the deposit. |
expiresAt | string | null | When the Pix code stops being payable (ISO 8601), from expiresIn. null when the rail sets its own lifetime. See QR code expiration. |
fee | number | Fee charged for the deposit. |
fxRateAtTx | number | BTC/BRL exchange rate at the time of transaction. |
walletCharge | string | null | Wallet charge identifier. |
deliveryFee | object | null | BRS only: the delivery fee debited for an external address, null for the account's own wallet. See BRS to an external Solana wallet. |
Errors
{
"success": false,
"error": "Validation failed",
"details": [
{ "field": "value", "message": "Expected number, received string" }
]
}{
"success": false,
"error": "A deposit with this externalId already exists"
}Idempotency
externalId is an idempotency key:
- Same
externalId, your own deposit — answers200with the original deposit: the samewalletCharge,qrCode,feeandfxRateAtTx. No new charge is created, so a retry after a timeout is safe and gives your customer the same QR code. Two simultaneous requests with the sameexternalIdalso get the same deposit. externalIdalready used by a deposit that is not yours — answers409and creates nothing. Retry with a differentexternalId.
You can also read the QR code back at any time with
GET /api/deposit/{id} (qrCode field).
Rate limit
Requests are limited to 60 per minute per API key, counted across every
API route (creating deposits included), plus 500 per minute per IP. Above the
limit the API answers 429 with a Retry-After header.
GET /api/deposit/{id}
Polls a deposit for its current state, by any id you hold for it: the
walletCharge returned by the POST, the trackId sent on every webhook, or
your own externalId. It is the deposit twin of
GET /api/wallet/payout/{transactionId}.
Use it when you do not want to depend on the webhook alone: to poll while your customer waits, or to reconcile a deposit whose webhook you missed.
Request
curl --request GET \
--url https://api.hodle.com.br/api/deposit/trk_abc123 \
--header "Authorization: Bearer $API_KEY"const res = await fetch(
'https://api.hodle.com.br/api/deposit/trk_abc123',
{ headers: { Authorization: `Bearer ${process.env.HODLE_API_KEY}` } },
)
const data = await res.json()import os, requests
res = requests.get(
"https://api.hodle.com.br/api/deposit/trk_abc123",
headers={"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}"},
)
data = res.json()Parameters
| Field | In | Required | Description |
|---|---|---|---|
id | path | Yes | The walletCharge, trackId or externalId of the deposit. |
subAccountId | query | Cond. | Required when the deposit was created with subAccountId. Send the same id back; an id you do not own is refused with 404. |
Response
The same body as
GET /api/deposit/asset/{externalId} below;
see its fields and status.
Prefer the webhooks and use polling
as a fallback. When you poll, keep the whole API key under the
rate limit of 60 requests per minute: poll a deposit every 5
seconds only while your customer is watching it, back off to every 30–60
seconds otherwise, and stop at a final status (COMPLETED, FAILED after its
refund, REFUNDED, EXPIRED). FIAT_PAID is the same moment as the
DEPOSIT_PIX_CONFIRMED webhook: the
PIX settled and the asset is on its way.
Errors
| Status | Body | When |
|---|---|---|
400 | { "success": false, "error": "Invalid id" } | Empty id, or longer than 128 characters. |
401 | { "success": false, "error": "Unauthorized" } | Missing or invalid API key. |
404 | { "success": false, "error": "Deposit not found" } | No deposit with that id under your API key (or the subAccountId is not yours). |
Available in production and sandbox.
GET /api/deposit/asset/{externalId}
Reads a deposit back: whether the PIX was paid, whether the asset was delivered, and — on your own virtual account — who paid the QR code.
Poll it, or use it to reconcile after a
DEPOSIT_ASSET_SUCCESS webhook.
Request
curl --request GET \
--url https://api.hodle.com.br/api/deposit/asset/my-order-123 \
--header "Authorization: Bearer $API_KEY"const res = await fetch(
'https://api.hodle.com.br/api/deposit/asset/my-order-123',
{ headers: { Authorization: `Bearer ${process.env.HODLE_API_KEY}` } },
)
const data = await res.json()import os, requests
res = requests.get(
"https://api.hodle.com.br/api/deposit/asset/my-order-123",
headers={"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}"},
)
data = res.json()Parameters
| Field | In | Required | Description |
|---|---|---|---|
externalId | path | Yes | The externalId of the deposit — your own value, or the UUID we generated and returned on the POST. |
subAccountId | query | Cond. | Required when the deposit was created with subAccountId. Send the same id back; an id you do not own is refused with 404. |
Response
{
"success": true,
"data": {
"externalId": "my-order-123",
"walletCharge": "68b1f0c2a4d3e50011aa22bb",
"trackId": "trk_abc123",
"status": "COMPLETED",
"processingStep": null,
"asset": "USDT",
"network": "polygon",
"address": "0x000000000000000000000000000000000000dEaD",
"value": 5000,
"valueInBrl": "50.00",
"fee": 100,
"feeInBrl": "1.00",
"fxRateAtTx": 5.55,
"receivedAmount": 8820000,
"receivedAmountDecimals": 6,
"transactionHash": "0xabc...",
"qrCode": "00020126...",
"expiresAt": "2026-08-30T12:15:00.000Z",
"endToEndId": "E1823612020260830120000000000001",
"paidAt": "2026-08-30T12:00:00.000Z",
"payer": {
"name": "FULANO DE TAL",
"taxId": "12345678901"
},
"confirmedAt": "2026-08-30T12:00:31.000Z",
"createdAt": "2026-08-30T11:59:02.000Z",
"updatedAt": "2026-08-30T12:00:31.000Z"
}
}Fields
| Field | Type | Description |
|---|---|---|
externalId | string | Your reconciliation key. |
walletCharge | string | Hodle id of the charge, the same one the POST returned. |
trackId | string | null | Hodle tracking id of the operation. |
status | string | PENDING, FIAT_PAID, PROCESSING, COMPLETED, FAILED, REFUNDED or EXPIRED. See below. |
processingStep | string | null | Where a PROCESSING deposit currently is. Report it, never route on it. |
asset | string | Asset being delivered. |
network | string | Network the asset is delivered on. |
address | string | Destination address of the delivery. |
value | integer | Amount in BRL cents. |
valueInBrl | string | Same amount, decimal string. |
fee | integer | Fee in BRL cents. |
feeInBrl | string | Same fee, decimal string. |
fxRateAtTx | number | null | Exchange rate at the time of the transaction. |
receivedAmount | integer | null | Amount actually delivered, in the asset's smallest unit. |
receivedAmountDecimals | integer | null | Decimals of receivedAmount. |
transactionHash | string | null | On-chain tx hash of the delivery, when there is one. |
qrCode | string | null | The PIX QR code / BR Code we handed you on creation. |
expiresAt | string | null | ISO-8601 time the QR code stops being payable, the same value the POST returned. null for deposits created before it existed or on a rail that sets its own lifetime. |
endToEndId | string | null | End-to-end id of the PIX that funded the deposit. null until it is paid. |
paidAt | string | null | ISO-8601 time the PIX settled, as reported by the settling institution (not when we processed it). When the institution does not report it, the time Hodle received the settlement confirmation. |
payer | object | null | Who paid the PIX — see Who paid the QR code. |
confirmedAt | string | null | ISO-8601 timestamp of the delivery. |
createdAt | string | ISO-8601 creation timestamp. |
updatedAt | string | ISO-8601 timestamp of the last change. |
Status
| Status | Meaning |
|---|---|
PENDING | Charge created, PIX not paid yet. |
FIAT_PAID | Fiat payment received; the asset has not yet been delivered. paidAt and endToEndId are available. Keep polling. |
PROCESSING | Fiat payment received, asset delivery in progress. Keep polling. |
COMPLETED | Asset delivered. transactionHash is set when the delivery has one. |
FAILED | The deposit was paid but could not be delivered. Hodle returns the PIX to the payer; keep polling for the refund. Never shown before the PIX is paid. |
REFUNDED | The PIX was refunded to the payer. |
EXPIRED | The charge expired without being paid. If a PIX still arrives for it afterwards, it is honored: the deposit moves on to FIAT_PAID and is delivered normally. |
Who paid the QR code
payer carries the identity of whoever actually paid the PIX, as the payer's bank
reports it.
| Field | Type | Description |
|---|---|---|
name | string | null | Name of the payer. |
taxId | string | null | CPF/CNPJ of the payer, digits only. |
It is null when the PIX has not been paid yet, when the payer's identity was
not reported for that payment, or when your account does not own a virtual
account. Only on your own virtual account is the payer your own customer; on the shared Hodle account they are our
counterparty and stay private. Ask support to enable it if you need it.
Errors
{
"success": false,
"error": "Deposit not found"
}A deposit that belongs to another API key answers 404, never 403, so the
status code cannot be used to probe for someone else's externalId.