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

POST /api/deposit/asset
{
  "value": 5000,
  "address": "lnbc500u1pj...",
  "asset": "LIGHTNING",
  "externalId": "my-order-123"
}

Parameters

FieldTypeRequiredDescription
valueintegerYesAmount in BRL cents. Minimum 100 (R$1.00).
addressstringCond.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.
assetstringYesAsset type: LIGHTNING, USDT, USDC, USDCE, BRLA, BRS, DEPIX, or BTC.
networkstringCond.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.
externalIdstringNoYour 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.
subAccountIdstringNoThe 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.
walletPinstringCond.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.
protectedSymmetricKeystringCond.BRS to an external address only. That wallet's protectedSymmetricKey.
walletIdstringNoBRS 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).
maxDeliveryFeeCentsintegerNoBRS 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.
expiresInintegerNoSeconds 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.
taxIdstringNoCPF/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):

POST /api/deposit/asset
{
  "value": 5000,
  "address": "0x8f3c...",
  "asset": "USDC",
  "network": "base",
  "externalId": "order-2042",
  "expiresIn": 1800
}
200 OK
{
  "success": true,
  "externalId": "order-2042",
  "walletCharge": "66fc0b7e2a4b5c6d7e8f9013",
  "qrCode": "00020101021226...",
  "expiresAt": "2026-10-05T15:30:00.000Z",
  "fee": 100,
  "fxRateAtTx": 5.32
}
  • After expiresAt the Pix code stops being payable and the deposit moves to EXPIRED. Create a new deposit (with a new externalId) to give your customer a fresh code.
  • expiresAt is null when the rail that serves the deposit sets its own lifetime for the Pix code; expiresIn is then ignored.
  • Repeating an externalId returns the original deposit with its original expiresAt; a new expiresIn does not extend it.
  • expiresIn outside 60–86400 answers 400 with Validation 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

AssetNetworksAddress format
LIGHTNINGlightningLightning invoice or LNURL email
USDTpolygon, arbitrum, solanaEVM address (0x...); Solana address on solana
USDCpolygon, base, gnosis, solanaEVM address (0x...); Solana address on solana
USDCEgnosisEVM address (0x...)
BRLApolygon, baseEVM address (0x...)
BRSsolana, baseOwn Hodle Solana wallet (any Solana address with external delivery — ask support); on base, the account's own EVM wallet
DEPIXspark, liquidSpark address (spark1...) or Liquid address
BTCsparkSpark 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, or lnbcrt
  • 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

  1. 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.
  2. Quote it. POST /api/quote with outputPaymentMethod: "SOLANA". The quote is priced on the Base leg and says bridgeFeeIncluded: false: the bridge cost is taken on delivery, so the amount that lands on Solana is slightly below outputAmount.
  3. 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.
POST /api/deposit/asset
{
  "value": 5000,
  "asset": "USDC",
  "network": "solana",
  "address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
  "externalId": "order-2041"
}
200 OK
{
  "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:

StepEventWhat it means
Pix paidDEPOSIT_PIX_CONFIRMEDThe Pix settled. Safe to release your customer's order.
DeliveredDEPOSIT_ASSET_SUCCESSThe asset landed on address, usually about a minute after the Pix. valueInCurrency is the amount delivered.
Not deliveredDEPOSIT_ASSET_FAILEDThe 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.

StatusBodyMeaning
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:

DestinationFee (in BRS)*
Never held BRS1,44 BRS
Already holds BRS0,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.

Request
{
  "value": 10000,
  "asset": "BRS",
  "network": "solana",
  "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
  "externalId": "order-1029",
  "walletPin": "424242",
  "protectedSymmetricKey": "AoofiKHyVRLvdrknnXzoIh1Gd1YTwLaOBn4ibm103a4d...",
  "maxDeliveryFeeCents": 200
}
200 OK
{
  "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; expiresAt and deliveryFee.expiresAt carry 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 externalId returns the original deposit and does not debit the fee again.
StatuserrorCodeMeaning
403BRS_EXTERNAL_DELIVERY_DISABLEDThe account can only receive BRS in its own wallet. Ask support to enable external delivery.
400DELIVERY_FEE_REQUIREDExternal address without walletPin + protectedSymmetricKey.
400DELIVERY_FEE_ABOVE_MAXThe live fee is above maxDeliveryFeeCents; the response carries quotedDeliveryFeeCents. Nothing was signed.
400INSUFFICIENT_BRS_FOR_DELIVERY_FEEThe paying wallet holds less BRS than the fee.
401INVALID_PINWrong PIN (repeated failures lock the wallet for 24h).
503DELIVERY_FEE_UNAVAILABLEThe 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:

POST /api/wallet/create
{
  "network": "solana",
  "walletPin": "424242",
  "protectedSymmetricKey": "<protectedSymmetricKey of the wallet this PIN protects>"
}
201 Created
{
  "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" — without protectedSymmetricKey, the PIN is checked against the account's oldest protected wallet, which may use a different PIN. Send the protectedSymmetricKey of 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:

POST /api/quote
{
  "inputCurrency": "BRL",
  "inputPaymentMethod": "PIX",
  "outputCurrency": "BRS",
  "outputPaymentMethod": "SOLANA",
  "inputAmount": "10.00",
  "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
}
200 OK (excerpt)
{
  "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:

POST /api/deposit/asset
{
  "value": 1000,
  "asset": "BRS",
  "network": "solana",
  "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
  "externalId": "order-1030",
  "walletId": "66fb2a10c4e8d91f0a3b7c55",
  "walletPin": "424242",
  "protectedSymmetricKey": "<the NEW wallet's protectedSymmetricKey>",
  "maxDeliveryFeeCents": 200
}
200 OK
{
  "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

200 OK
{
  "success": true,
  "externalId": "my-order-123",
  "qrCode": "lnbc500u1pj...",
  "expiresAt": "2026-10-05T14:15:00.000Z",
  "fee": 100,
  "fxRateAtTx": 408000.50,
  "walletCharge": "charge_abc123"
}

Fields

FieldTypeDescription
successbooleanWhether the deposit was created.
externalIdstringThe external ID for reconciliation (your value or auto-generated UUID).
qrCodestring | nullQR code for the deposit.
expiresAtstring | nullWhen the Pix code stops being payable (ISO 8601), from expiresIn. null when the rail sets its own lifetime. See QR code expiration.
feenumberFee charged for the deposit.
fxRateAtTxnumberBTC/BRL exchange rate at the time of transaction.
walletChargestring | nullWallet charge identifier.
deliveryFeeobject | nullBRS only: the delivery fee debited for an external address, null for the account's own wallet. See BRS to an external Solana wallet.

Errors

400 Bad Request
{
  "success": false,
  "error": "Validation failed",
  "details": [
    { "field": "value", "message": "Expected number, received string" }
  ]
}
409 Conflict
{
  "success": false,
  "error": "A deposit with this externalId already exists"
}

Idempotency

externalId is an idempotency key:

  • Same externalId, your own deposit — answers 200 with the original deposit: the same walletCharge, qrCode, fee and fxRateAtTx. 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 same externalId also get the same deposit.
  • externalId already used by a deposit that is not yours — answers 409 and creates nothing. Retry with a different externalId.

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

FieldInRequiredDescription
idpathYesThe walletCharge, trackId or externalId of the deposit.
subAccountIdqueryCond.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

StatusBodyWhen
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

FieldInRequiredDescription
externalIdpathYesThe externalId of the deposit — your own value, or the UUID we generated and returned on the POST.
subAccountIdqueryCond.Required when the deposit was created with subAccountId. Send the same id back; an id you do not own is refused with 404.

Response

200 OK
{
  "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

FieldTypeDescription
externalIdstringYour reconciliation key.
walletChargestringHodle id of the charge, the same one the POST returned.
trackIdstring | nullHodle tracking id of the operation.
statusstringPENDING, FIAT_PAID, PROCESSING, COMPLETED, FAILED, REFUNDED or EXPIRED. See below.
processingStepstring | nullWhere a PROCESSING deposit currently is. Report it, never route on it.
assetstringAsset being delivered.
networkstringNetwork the asset is delivered on.
addressstringDestination address of the delivery.
valueintegerAmount in BRL cents.
valueInBrlstringSame amount, decimal string.
feeintegerFee in BRL cents.
feeInBrlstringSame fee, decimal string.
fxRateAtTxnumber | nullExchange rate at the time of the transaction.
receivedAmountinteger | nullAmount actually delivered, in the asset's smallest unit.
receivedAmountDecimalsinteger | nullDecimals of receivedAmount.
transactionHashstring | nullOn-chain tx hash of the delivery, when there is one.
qrCodestring | nullThe PIX QR code / BR Code we handed you on creation.
expiresAtstring | nullISO-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.
endToEndIdstring | nullEnd-to-end id of the PIX that funded the deposit. null until it is paid.
paidAtstring | nullISO-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.
payerobject | nullWho paid the PIX — see Who paid the QR code.
confirmedAtstring | nullISO-8601 timestamp of the delivery.
createdAtstringISO-8601 creation timestamp.
updatedAtstringISO-8601 timestamp of the last change.

Status

StatusMeaning
PENDINGCharge created, PIX not paid yet.
FIAT_PAIDFiat payment received; the asset has not yet been delivered. paidAt and endToEndId are available. Keep polling.
PROCESSINGFiat payment received, asset delivery in progress. Keep polling.
COMPLETEDAsset delivered. transactionHash is set when the delivery has one.
FAILEDThe 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.
REFUNDEDThe PIX was refunded to the payer.
EXPIREDThe 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.

FieldTypeDescription
namestring | nullName of the payer.
taxIdstring | nullCPF/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

404 Not Found
{
  "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.