Nominal accounts
Open a real bank account in a subaccount's own name, and follow its approval and KYB.
Production only. A nominal account is a real account opened at a regulated institution under a real document. There is no sandbox equivalent —
sandbox-api.hodle.com.branswers422 SANDBOX_UNAVAILABLEbefore anything is written. See Sandbox.
Overview
A subaccount identifies one of your end-users. A nominal account gives that end-user a bank account in their own name: their CNPJ on the account, their own Pix keys, their own balance — not a ledger position inside yours.
One subaccount holds at most one nominal account, and a subaccount is addressed the
same way everywhere else in the API, so the subAccountId you already hold is the only
identifier you need. Every operation that names it is routed to that account's own
credentials, never to your main account's.
POST /api/subaccount/{subAccountId}/account— open the account. Returns anonboardingUrlfor the end-user to complete.GET /api/subaccount/{subAccountId}/account— read the account and its status. There is no outbound event for this lifecycle yet, so poll it.
Lifecycle
POST /api/subaccount/{id}/account
│
▼
PENDING ──── end-user completes onboardingUrl ────┐
▼
institution approves
│
Hodle provisions this account's own
credentials + webhooks, opens its
stablecoin subaccount, starts the KYB
│
▼
PENDING ──► APPROVEDstatus stays PENDING from creation until the KYB completes. APPROVED is the only
state in which the account can transact; REJECTED carries a rejectionReason.
The KYB is not something you start. Approval of the account register is what triggers it, and the hosted forms it produces are reported on the account.
POST /api/subaccount/{subAccountId}/account
curl --request POST \
--url https://api.hodle.com.br/api/subaccount/c852df87-ac61-4259-8242-6451658dfedb/account \
--header "Authorization: Bearer $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"companyName": "Acme Ltda.",
"taxId": "12345678000199",
"representativeTaxId": "12345678901"
}'| Field | Type | Required | Description |
|---|---|---|---|
companyName | string | Yes | Legal name of the company the account is opened for. Up to 120 chars. |
taxId | string | Yes | The company's CNPJ, digits only. |
representativeTaxId | string | No | CPF of the legal representative who will complete the onboarding. |
{
"success": true,
"data": {
"accountRef": "68c0f1a2b3c4d5e6f7a8b9c0",
"subAccountId": "c852df87-ac61-4259-8242-6451658dfedb",
"companyName": "Acme Ltda.",
"taxId": "12345678000199",
"status": "PENDING",
"stableSubAccountStatus": null,
"accountId": null,
"onboardingUrl": "https://onboarding.woovi.com/...?embed=true",
"rejectionReason": null,
"createdAt": "2026-09-21T14:03:11.000Z"
}
}Send the end-user to onboardingUrl. Nothing else happens until they finish it.
| Status | Body | Meaning |
|---|---|---|
404 | Subaccount not found for this platform | The subaccount is not yours. Not a hint that it exists. |
409 | errorCode: ACCOUNT_ALREADY_EXISTS | This subaccount already has a nominal account. |
422 | errorCode: SANDBOX_UNAVAILABLE | Called against the sandbox. Nothing was written. |
GET /api/subaccount/{subAccountId}/account
curl --url "https://api.hodle.com.br/api/subaccount/c852df87-ac61-4259-8242-6451658dfedb/account" \
--header "Authorization: Bearer $API_KEY"{
"success": true,
"data": {
"accounts": [
{
"accountRef": "68c0f1a2b3c4d5e6f7a8b9c0",
"subAccountId": "c852df87-ac61-4259-8242-6451658dfedb",
"companyName": "Acme Ltda.",
"taxId": "12345678000199",
"status": "APPROVED",
"stableSubAccountStatus": "CONFIRMED",
"accountId": "acc_9f2b...",
"onboardingUrl": "https://onboarding.woovi.com/...?embed=true",
"rejectionReason": null,
"createdAt": "2026-09-21T14:03:11.000Z"
}
]
}
}stableSubAccountStatus tracks the stablecoin leg separately from the account itself:
IN_REVIEW and AWAITING_DOCUMENTS mean the institution is still reviewing it, and only
CONFIRMED together with status: APPROVED means the account can move money.
Why the account is yours to open but not yours to spend
The credentials minted for a nominal account belong to that account. They are never returned by this API and never shared with your main account: a request that names the subaccount is signed with that account's own key, and its Pix and stablecoin webhooks are registered against it. That is what keeps one end-user's balance out of another's, and out of yours.