Wallet Keys
Fetch the protectedSymmetricKey needed for wallet payouts.
POST /api/wallet/keys
Returns the user's protectedSymmetricKey and email. You'll pass protectedSymmetricKey in every POST /api/wallet/payout request.
Call this endpoint once per user and cache both values in your database. They only change if the user resets their PIN.
Gated by feature flag. Requires the
WALLET_PAYOUT_APIper-user feature flag. Without it the endpoint responds403. Contact Hodle to enable it.
How the PIN is established
Hodle wallets are non-custodial: the wallet mnemonic is encrypted with a symmetric key that is itself protected by the user's wallet PIN. Hodle never stores the PIN in plaintext — a payout works only because you pass the PIN back per request, and the server uses it to transiently decrypt and sign.
Because of this, the PIN must be established before a payout-capable wallet exists — it cannot be minted by the API out of thin air:
- The account holder sets a wallet PIN on the Hodle platform (web/app onboarding). This creates the first PIN-protected wallet and its
protectedSymmetricKey. - To create an additional payout-capable wallet (e.g. a second network) through the API, call
POST /api/wallet/createwith awalletPinfield. The endpoint verifies thatwalletPinmatches the PIN already registered on the platform, then provisions the wallet under that same PIN.- If the user has no PIN registered on the platform, the call fails:
This user has no wallet PIN registered on the platform.... Establish the PIN first. - If
walletPindoes not match:walletPin does not match the PIN registered on the platform(403).
- If the user has no PIN registered on the platform, the call fails:
POST /api/wallet/createwithoutwalletPinstill creates a legacy server-custodial wallet — but that wallet has noprotectedSymmetricKey, so/api/wallet/keysreturns404for it and it cannot be used for payouts.
The walletPin you pass to /api/wallet/payout and /api/wallet/transfer is this same platform PIN.
PIN format
Six digits. That is what the Hodle platform asks the account holder for, so it is what these endpoints expect. Collect six digits in your UI.
The API validates only that walletPin is a non-empty string — it has no way to compare
against the PIN the user chose, only to try it. A four-digit value is therefore not rejected
by the schema; it simply fails to unlock the wallet and comes back as 400 Invalid PIN.
Rate limit
1 request per minute per API key. Hitting the limit responds 429 with a Retry-After header.
Request
The body is empty. The user is identified from the API key.
curl --request POST \
--url https://api.hodle.com.br/api/wallet/keys \
--header "Authorization: Bearer $API_KEY" \
--header "Accept: application/json"const res = await fetch('https://api.hodle.com.br/api/wallet/keys', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HODLE_API_KEY}`,
Accept: 'application/json',
},
})
const data = await res.json()import os, requests
res = requests.post(
"https://api.hodle.com.br/api/wallet/keys",
headers={
"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}",
"Accept": "application/json",
},
)
data = res.json()Response
{
"success": true,
"data": {
"protectedSymmetricKey": "AoofiKHyVRLvdrknnXzoIh1Gd1YTwLaOBn4ibm103a4dpwHZA36dU9DiiZdDvQmNn...",
"email": "[email protected]"
}
}| Field | Type | Description |
|---|---|---|
data.protectedSymmetricKey | string | Pass this in POST /api/wallet/payout body. |
data.email | string | The user's email — keep alongside the key in your records. |
Errors
{ "success": false, "error": "WALLET_PAYOUT_API feature flag is not enabled for this user" }{ "success": false, "error": "Wallet not found" }{ "success": false, "error": "Wallet has no protected symmetric key" }A
Wallet has no protected symmetric keyresponse means the user's default wallet was created without a PIN (e.g. via/api/wallet/createwith nowalletPin). See How the PIN is established.
{ "success": false, "error": "Too many requests. Retry in 47 seconds" }When to refetch
Almost never. Re-fetch only if:
- You don't have the value cached yet for this user.
- A previous payout returned
Invalid PINdespite the user typing correctly — could mean the user reset their PIN and your cache is stale.