Increase limits
Raise a COMPANY subaccount's operating limits by submitting Proof of Address (company and UBO) and Proof of Financial Capacity.
Business accounts only. Higher operating limits are granted to a
COMPANYsubaccount whose KYB is alreadyAPPROVED. There is nothing to submit before that.
Overview
A newly verified COMPANY subaccount transacts under a default limit. Raising it is
a separate compliance step from KYB Level 1: you submit three documents, each
uploaded, submitted and polled independently.
| Document | documentType | Proves | Submit with |
|---|---|---|---|
| Company address | PROOF-OF-ADDRESS-COMPANY | The registered business address | POST /api/kyb/proof-of-address · addressType: "COMPANY" |
| UBO address | PROOF-OF-ADDRESS | The Ultimate Beneficial Owner's residence | POST /api/kyb/proof-of-address · addressType: "UBO" |
| Financial capacity | PROOF-OF-FINANCIAL-CAPACITY | That the company's funds or income back the declared volume | POST /api/kyb/proof-of-financial-capacity |
The higher limits apply once all three attempts are APPROVED. Proof of Address
alone does not raise a limit: the address documents place the company and its owner,
and Proof of Financial Capacity is what evidences the volume you are asking for. The
same Proof of Financial Capacity also unlocks USD operations — one submission
serves both.
Document requirements
Applies to all three documents:
- Single-sided PDF. Multi-page scans and images are rejected in review.
- Dated within the last 3 months. A document collected during your own onboarding may be accepted up to 12 months old.
- Up to ~10MB when you send the optional inline
documentcopy. - Third-party issued and legible, with the holder's name matching the subaccount's
KYB data. Compliance judges the document itself — a file that merely has the right
documentTypestill gets rejected on its content.
Flow
- Confirm the
COMPANYsubaccount's KYB isAPPROVED. - Company address — register a
PROOF-OF-ADDRESS-COMPANYdocument,PUTthe PDF, poll untilready: true, then submit it withaddressType: "COMPANY". - UBO address — register a
PROOF-OF-ADDRESSdocument,PUTthe PDF, poll untilready: true, then submit it withaddressType: "UBO". - Financial capacity — register a
PROOF-OF-FINANCIAL-CAPACITYdocument,PUTthe PDF, poll untilready: true, then submit it toPOST /api/kyb/proof-of-financial-capacity. - Poll each of the three
attemptIds untilstatus: APPROVED.
Steps 2, 3 and 4 are independent — you can run them in parallel and in any order. Nothing serialises them.
Alternate rail
Accounts routed through the alternate limit-increase rail submit these same three endpoints unchanged. The comprovantes are routed to the configured limit-increase API — the rail is selected automatically from the platform account's configuration, and no request field toggles it. Other accounts use the default rail exactly as documented above.
On the alternate rail, POST /api/kyb/proof-of-address accepts one extra optional
field, desiredMonthlyLimit — the requested ceiling in BRL cents. It is ignored
(accepted as a no-op) on the default rail, so a single client integration works
against both. When omitted, the configured rail uses a default ceiling of
R$ 500.000,00. The first comprovante submitted opens the limit request; a second
one (company or UBO) attaches to that same still-open request instead of
creating a new one — only one open request per company is allowed at a time.
Uploading a Proof of Address document
Proof of Address documents reuse the document upload
endpoints. Register the document, PUT the PDF to the returned URL, then poll
readiness — the only difference is the documentType.
1. Register the document — POST /api/kyc/document
curl --request POST \
--url https://api.hodle.com.br/api/kyc/document \
--header "Authorization: Bearer $API_KEY" \
--header "Content-Type: application/json" \
--data '{ "subAccountId": "c852df87-...", "documentType": "PROOF-OF-ADDRESS-COMPANY" }'| Field | Type | Required | Description |
|---|---|---|---|
subAccountId | string | Yes | The COMPANY subaccount the document belongs to. |
documentType | string | Yes | PROOF-OF-ADDRESS-COMPANY (company address) or PROOF-OF-ADDRESS (UBO residence). |
{
"success": true,
"data": {
"documentId": "doc_poa_9a1...",
"uploadUrlFront": "https://uploads.hodle.com.br/..."
}
}2. Upload the PDF
curl --request PUT \
--url "$UPLOAD_URL_FRONT" \
--header "If-None-Match: *" \
--header "Content-Type: application/pdf" \
--data-binary "@company-address.pdf"3. Check readiness — GET /api/kyc/document/{documentId}
curl --url "https://api.hodle.com.br/api/kyc/document/doc_poa_9a1...?subAccountId=c852df87-..." \
--header "Authorization: Bearer $API_KEY"{ "success": true, "data": { "documentId": "doc_poa_9a1...", "ready": true } }Poll until ready: true, then submit the document below.
POST /api/kyb/proof-of-address
Submit one ready document. Call it once for the company address and once for the
UBO address — each call returns its own attemptId.
Request
curl --request POST \
--url https://api.hodle.com.br/api/kyb/proof-of-address \
--header "Authorization: Bearer $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"subAccountId": "c852df87-ac61-4259-8242-6451658dfedb",
"uploadedPoAId": "doc_poa_9a1...",
"addressType": "COMPANY"
}'const res = await fetch('https://api.hodle.com.br/api/kyb/proof-of-address', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HODLE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
subAccountId,
uploadedPoAId,
addressType: 'COMPANY',
}),
})
const data = await res.json()import os, requests
res = requests.post(
"https://api.hodle.com.br/api/kyb/proof-of-address",
headers={
"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}",
"Content-Type": "application/json",
},
json={
"subAccountId": sub_account_id,
"uploadedPoAId": uploaded_poa_id,
"addressType": "COMPANY",
},
)
data = res.json()Parameters
| Field | Type | Required | Description |
|---|---|---|---|
subAccountId | string | Yes | The COMPANY subaccount's id. |
uploadedPoAId | string | Yes | documentId of the uploaded Proof of Address PDF, with ready: true. |
addressType | string | Yes | COMPANY for the company address, UBO for the beneficial owner's residence. |
sandboxReject | boolean | No | Sandbox only. true simulates a rejected attempt; ignored in production. |
desiredMonthlyLimit | integer | No | Alternate rail only. Requested monthly ceiling in BRL cents; defaults to 50000000 (R$ 500.000,00). No-op on the default rail. |
document | string | No | The PDF itself, base64-encoded, stored encrypted alongside the attempt. Optional — the document you uploaded with uploadedPoAId is what gets reviewed. Max ~10MB of binary. |
documentMimeType | string | No | MIME type of document, e.g. application/pdf. |
sandboxReject | boolean | No | Sandbox only. true makes the attempt resolve as REJECTED; ignored in production. |
Response
{
"success": true,
"data": {
"attemptId": "poa_1e6b8f42-9c3d-4a71-b5e8-2f0d6c8a1b34",
"addressType": "COMPANY",
"status": "PENDING",
"createdAt": "2026-07-22T22:00:00.000Z"
}
}The 202 means the document was accepted for review, not that it was approved.
Compliance reviews it and the outcome shows up on the poll below — see
Review timing.
Errors
{
"success": false,
"error": "Validation failed",
"details": [{ "field": "addressType", "message": "Invalid enum value" }]
}{ "success": false, "error": "Subaccount not found for this platform" }A subaccount that belongs to another platform answers 404, not 403, so the
status code cannot be used to probe for ids you do not own.
GET /api/kyb/proof-of-address/{attemptId}
Poll a submitted attempt until it resolves.
Request
curl --request GET \
--url https://api.hodle.com.br/api/kyb/proof-of-address/poa_1e6b8f42-9c3d-4a71-b5e8-2f0d6c8a1b34 \
--header "Authorization: Bearer $API_KEY"const res = await fetch(
`https://api.hodle.com.br/api/kyb/proof-of-address/${attemptId}`,
{ headers: { Authorization: `Bearer ${process.env.HODLE_API_KEY}` } },
)
const data = await res.json()import os, requests
res = requests.get(
f"https://api.hodle.com.br/api/kyb/proof-of-address/{attempt_id}",
headers={"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}"},
)
data = res.json()Response
{
"success": true,
"data": {
"attemptId": "poa_1e6b8f42-9c3d-4a71-b5e8-2f0d6c8a1b34",
"addressType": "COMPANY",
"status": "APPROVED",
"resultMessage": null,
"createdAt": "2026-07-22T22:00:00.000Z",
"updatedAt": "2026-07-22T22:01:43.000Z"
}
}{
"success": true,
"data": {
"attemptId": "poa_1e6b8f42-9c3d-4a71-b5e8-2f0d6c8a1b34",
"addressType": "UBO",
"status": "REJECTED",
"resultMessage": "Document older than 3 months",
"createdAt": "2026-07-22T22:00:00.000Z",
"updatedAt": "2026-07-22T22:01:43.000Z"
}
}An attemptId that does not belong to your platform answers
404 { "error": "Attempt not found" }.
Status values
| Status | Meaning |
|---|---|
PENDING | Under review. Keep polling. |
APPROVED | This document is accepted. Limits rise once all three attempts are APPROVED. |
REJECTED | Failed. resultMessage describes why; upload a fresh document and resubmit. |
resultMessage is only populated on REJECTED — it is null while pending and on
approval.
Proof of Financial Capacity
The third document has its own endpoint pair, documented in full under
Proof of Financial Capacity. The shape mirrors
Proof of Address, minus addressType:
curl --request POST \
--url https://api.hodle.com.br/api/kyb/proof-of-financial-capacity \
--header "Authorization: Bearer $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"subAccountId": "c852df87-ac61-4259-8242-6451658dfedb",
"uploadedPoFCId": "doc_pofc_9a1..."
}'A bank statement, an account balance statement, audited financial statements or an accountant-issued income statement all qualify. Submit the statement that actually covers the volume you are asking for: a statement well below the declared volume is the most common rejection.
Review timing
These documents are reviewed by Hodle's compliance team before they reach the
underlying provider, so PENDING is not a machine-speed state: an attempt can sit
there for hours. Poll on a slow interval (minutes, not seconds) and drive your UI
off the poll rather than off the POST response.
There is no webhook for these attempts. Unlike KYC and KYB,
which push kyc.completed, Proof of Address and Proof of Financial Capacity are
poll-only. Persist each attemptId when you submit it — it is the only handle you
get on the review.
Because the review is human, a document submitted while the subaccount's KYB is
still not APPROVED is accepted with 202 and rejected later — you will not get a
synchronous error for it. Confirm KYB is APPROVED before submitting.
Testing in the sandbox
In the sandbox there is no human in the loop: the attempt resolves on submission, so your first poll already returns the final status. That makes the whole three-document flow testable end to end in seconds:
- Submit normally → first poll returns
APPROVED. - Submit with
sandboxReject: true→ first poll returnsREJECTEDwithresultMessage: "Sandbox: attempt rejected on request".
Build your retry-and-resubmit path against sandboxReject before you send a real
document to production, where a rejection costs hours.