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 COMPANY subaccount whose KYB is already APPROVED. 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.

DocumentdocumentTypeProvesSubmit with
Company addressPROOF-OF-ADDRESS-COMPANYThe registered business addressPOST /api/kyb/proof-of-address · addressType: "COMPANY"
UBO addressPROOF-OF-ADDRESSThe Ultimate Beneficial Owner's residencePOST /api/kyb/proof-of-address · addressType: "UBO"
Financial capacityPROOF-OF-FINANCIAL-CAPACITYThat the company's funds or income back the declared volumePOST /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 document copy.
  • 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 documentType still gets rejected on its content.

Flow

  1. Confirm the COMPANY subaccount's KYB is APPROVED.
  2. Company address — register a PROOF-OF-ADDRESS-COMPANY document, PUT the PDF, poll until ready: true, then submit it with addressType: "COMPANY".
  3. UBO address — register a PROOF-OF-ADDRESS document, PUT the PDF, poll until ready: true, then submit it with addressType: "UBO".
  4. Financial capacity — register a PROOF-OF-FINANCIAL-CAPACITY document, PUT the PDF, poll until ready: true, then submit it to POST /api/kyb/proof-of-financial-capacity.
  5. Poll each of the three attemptIds until status: 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" }'
FieldTypeRequiredDescription
subAccountIdstringYesThe COMPANY subaccount the document belongs to.
documentTypestringYesPROOF-OF-ADDRESS-COMPANY (company address) or PROOF-OF-ADDRESS (UBO residence).
201 Created
{
  "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"
200 OK
{ "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

FieldTypeRequiredDescription
subAccountIdstringYesThe COMPANY subaccount's id.
uploadedPoAIdstringYesdocumentId of the uploaded Proof of Address PDF, with ready: true.
addressTypestringYesCOMPANY for the company address, UBO for the beneficial owner's residence.
sandboxRejectbooleanNoSandbox only. true simulates a rejected attempt; ignored in production.
desiredMonthlyLimitintegerNoAlternate rail only. Requested monthly ceiling in BRL cents; defaults to 50000000 (R$ 500.000,00). No-op on the default rail.
documentstringNoThe PDF itself, base64-encoded, stored encrypted alongside the attempt. Optional — the document you uploaded with uploadedPoAId is what gets reviewed. Max ~10MB of binary.
documentMimeTypestringNoMIME type of document, e.g. application/pdf.
sandboxRejectbooleanNoSandbox only. true makes the attempt resolve as REJECTED; ignored in production.

Response

202 Accepted
{
  "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

400 — validation failed
{
  "success": false,
  "error": "Validation failed",
  "details": [{ "field": "addressType", "message": "Invalid enum value" }]
}
404 — subaccount not found
{ "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

status: APPROVED
{
  "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"
  }
}
status: REJECTED
{
  "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

StatusMeaning
PENDINGUnder review. Keep polling.
APPROVEDThis document is accepted. Limits rise once all three attempts are APPROVED.
REJECTEDFailed. 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 returns REJECTED with resultMessage: "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.