Skip to main content

KYB Level 1 - API

This guide describes the fully server-to-server flow to complete KYB Level 1. Use it when you want to collect company data, UBOs and documents on your own frontend and submit everything via the API instead of redirecting the user to the Web SDK flow.

info

KYB is always performed against a COMPANY subaccount. Pass its ID as ?subAccountId= on every call below.

The complete flow is:

  1. Create a COMPANY subaccount.
  2. Upload the corporate documents (Certificate of Incorporation, Tax Identification Document) and one identification document per UBO.
  3. Register each Ultimate Beneficial Owner (UBO).
  4. Submit KYB Level 1 and wait for approval.

Additional documents (Proof of Financial Capacity, Proof of Revenue) are only required for the follow-up rails - see KYB USD and KYB EUR.


Step 1 - Create a COMPANY Subaccount

HTTP POST Request

https://api.sandbox.avenia.io:10952/v2/account/sub-accounts

Sample JSON Body

{
"accountType": "COMPANY",
"name": "ACME Digital Solutions Ltd"
}

JSON Response

{
"id": "a3f2c891-7b4d-4e6a-9d15-c82e0f3a1b57"
}

Save this id and pass it as ?subAccountId=a3f2c891-7b4d-4e6a-9d15-c82e0f3a1b57 on every subsequent request.

See Subaccount Management for the full subaccount reference.


Step 2 - Upload Required Documents

At minimum, three documents must be uploaded before KYB Level 1 can be submitted:

DocumentdocumentTypeWhen required
Certificate of IncorporationCERTIFICATE-OF-INCORPORATIONKYB Level 1
Tax Identification DocumentCOMPANY-TAX-IDENTIFICATION-DOCUMENTKYB Level 1
UBO identification (per UBO)ID, DRIVERS-LICENSE, PASSPORT, RESIDENCE-PERMITKYB Level 1
tip

Proof of Financial Capacity and Proof of Revenue are not required at Level 1. You only need to upload them later, for KYB USD or KYB EUR. See KYB USD and KYB EUR.

Each upload is a two-phase operation: first create the document record, then PUT the file bytes to the returned pre-signed URL.

Step 2a - Create the Document Record

HTTP POST Request

https://api.sandbox.avenia.io:10952/v2/documents?subAccountId={subAccountId}

Sample JSON Body

{
"documentType": "CERTIFICATE-OF-INCORPORATION",
"isDoubleSided": false
}

JSON Response

{
"id": "2d7f4b91-e3a8-4c1f-9e62-5b0d7a8f3c16",
"uploadURLFront": "https://s3.amazonaws.com/bucket/path?X-Amz-Signature=...",
"uploadURLBack": ""
}
info

Only identification documents (ID, DRIVERS-LICENSE, PASSPORT, RESIDENCE-PERMIT) support isDoubleSided: true. All corporate documents are single-sided.

Step 2b - Upload the File

curl -X PUT "<uploadURLFront>" \
-H "Content-Type: application/pdf" \
-H "If-None-Match: *" \
--data-binary "@/path/to/certificate.pdf"

A 200 OK or 204 No Content response means the upload succeeded. Corporate documents accept application/pdf; identification documents accept image/png or image/jpeg.

Step 2c - Poll Until Ready

GET https://api.sandbox.avenia.io:10952/v2/documents/{documentId}?subAccountId={subAccountId}
{
"document": {
"id": "2d7f4b91-e3a8-4c1f-9e62-5b0d7a8f3c16",
"documentType": "CERTIFICATE-OF-INCORPORATION",
"uploadStatusFront": "PROCESSED",
"ready": true
}
}

Only documents with ready: true can be referenced in Steps 3 and 4. Listen for the matching document webhook if you prefer not to poll.


Step 3 - Create UBOs (Ultimate Beneficial Owners)

At least one UBO is required, and at least one UBO must have hasControl set to a valid corporate role.

HTTP POST Request

https://api.sandbox.avenia.io:10952/v2/account/ubos?subAccountId={subAccountId}

Fields

FieldTypeRequiredDescription
fullNamestringYesFull legal name (max 256 chars).
dateOfBirthstringYesYYYY-MM-DD. Minimum age 18.
countryOfTaxIdstringYesISO 3166-1 alpha-3 (e.g. BRA, USA).
taxIdNumberstringYesTax identification number. For BRA a valid CPF is required; for USA a 9-digit number is required.
percentageOfOwnershipstringYesOwnership share as a numeric string between "0" and "100" (e.g. "25.5").
hasControlstringCond.Corporate role. Required for at least one UBO in the set. See the table below for valid values.
uploadedIdentificationIdstringYesID from Step 2 of this UBO's identification document (must have ready: true).
documentCountrystringYesCountry that issued the identification document (ISO alpha-3).
streetLine1stringYesStreet address, first line (max 256 chars).
streetLine2stringNoStreet address, second line.
streetLine3stringNoStreet address, third line.
citystringYesCity of residence.
statestringYesISO 3166-2 subdivision code (e.g. SP, NY).
zipCodestringYesPostal code.
countrystringYesCountry of residence (ISO alpha-3).

Valid hasControl roles

CEO, CFO, COO, CTO, President, Vice President, Director, Managing Director, Managing Partner, General Partner, Partner, Secretary, Treasurer, Chairman, Board Member, Authorized Signatory, General Counsel, Owner, Founder, Manager, Member, Comptroller, Chief Compliance Officer.

info

email and phone are not accepted by this endpoint. Contact details for the UBO are collected elsewhere.

Sample JSON Body

{
"fullName": "USERS FULL NAME",
"dateOfBirth": "1988-07-22",
"countryOfTaxId": "BRA",
"taxIdNumber": "11182159111",
"percentageOfOwnership": "100",
"hasControl": "CEO",
"uploadedIdentificationId": "8b3e1a72-5c9f-4d2e-b847-xxxxxxxxxxxxx",
"documentCountry": "BRA",
"streetLine1": "RUA AURORA 456 APTO 12",
"streetLine2": "",
"streetLine3": "",
"city": "SAO PAULO",
"state": "SP",
"zipCode": "01209-001",
"country": "BRA"
}

JSON Response

{
"id": "3b7e9f24-1c5a-4d8e-a613-xxxxxxx"
}

Save every UBO id - all of them must be listed in Step 4.

tip

A single company can register up to 50 UBOs.


Step 4 - Submit KYB Level 1

HTTP POST Request

https://api.sandbox.avenia.io:10952/v2/kyc/new-level-1/api?subAccountId={subAccountId}

Fields

FieldTypeRequiredDescription
uboIdsarrayYesUUIDs of all UBOs created in Step 3. At least one must have hasControl set.
companyLegalNamestringYesCompany legal name (stored uppercase).
companyRegistrationNumberstringYesCompany registration number.
taxIdentificationNumberTinstringYesCompany TIN/CNPJ.
businessActivityDescriptionstringYesFree-text description (1-2000 chars).
reasonForAccountOpeningstringYesEnum (see below).
sourceOfFundsAndIncomestringYesEnum (see below).
numberOfEmployeesstringYesEnum (see below).
estimatedAnnualRevenueUsdstringYesEnum (see below).
estimatedMonthlyVolumeUsdstringYesPositive integer as string (e.g. "2000").
countryTaxResidencestringYesISO alpha-3, or the literal "N/A".
countrySubdivisionTaxResidencestringNoISO 3166-2 subdivision code (e.g. SP).
companyStreetLine1stringYesCompany address, first line (max 256 chars).
companyStreetLine2stringNoCompany address, second line.
companyStreetLine3stringNoCompany address, third line.
companyCitystringYesCompany city (max 256 chars).
companyStatestringYesISO subdivision for the company country.
companyZipCodestringYesCompany postal code (max 256 chars).
companyCountrystringYesCompany country name or ISO alpha-3 (normalized server-side).
certificateOfIncorporationDocumentIdstringYesID of a CERTIFICATE-OF-INCORPORATION document uploaded in Step 2 (must have ready: true).
taxIdentificationDocumentIdstringYesID of a COMPANY-TAX-IDENTIFICATION-DOCUMENT uploaded in Step 2 (must have ready: true).
websitestringNoCompany website URL.
socialMediastringNoSocial media URL.
emailPixKeystringNoEmail PIX key, when applicable.
sandboxRejectbooleanNoSet to true to simulate a rejected KYB in the sandbox environment. Defaults to false.

Enum Values

FieldValid Values
reasonForAccountOpeningcharitable_donations, ecommerce_retail_payments, investment_purposes, other, payments_to_friends_or_family_abroad, payroll, personal_or_living_expenses, protect_wealth, purchase_goods_and_services, receive_payments_for_goods_and_services, tax_optimization, third_party_money_transmission, treasury_management
sourceOfFundsAndIncomebusiness_loans, grants, inter_company_funds, investment_proceeds, legal_settlement, owners_capital, pension_retirement, sale_of_assets, sales_of_goods_and_services, third_party_funds, treasury_reserves
numberOfEmployees1-10, 11-50, 51-200, 201-500, 501-1000, 1001+
estimatedAnnualRevenueUsdless_than_100k, 100k_to_1m, 1m_to_10m, 10m_to_50m, 50m_to_100m, more_than_100m

Sample JSON Body

{
"uboIds": ["3b7e9f24-1c5a-4d8e-a613-8f2b0d4e7c91"],
"companyLegalName": "ACME SOLUCOES DIGITAIS LTDA",
"companyRegistrationNumber": "42731085000167",
"taxIdentificationNumberTin": "42.731.085/0001-67",
"businessActivityDescription": "Custom software development and IT consulting services. The company provides SaaS solutions for financial institutions.",
"website": "https://www.nexoradigital.com.br",
"socialMedia": "https://linkedin.com/company/nexoradigital",
"reasonForAccountOpening": "receive_payments_for_goods_and_services",
"sourceOfFundsAndIncome": "sales_of_goods_and_services",
"numberOfEmployees": "1-10",
"estimatedAnnualRevenueUsd": "less_than_100k",
"estimatedMonthlyVolumeUsd": "2000",
"countryTaxResidence": "BRA",
"countrySubdivisionTaxResidence": "BR-SP",
"companyStreetLine1": "AV PAULISTA 1000 CONJ 204",
"companyStreetLine2": "",
"companyStreetLine3": "",
"companyCity": "SAO PAULO",
"companyState": "SP",
"companyZipCode": "01310-100",
"companyCountry": "Brazil",
"certificateOfIncorporationDocumentId": "2d7f4b91-e3a8-4c1f-9e62-5b0d7a8f3c16",
"taxIdentificationDocumentId": "6a1c8e35-9b4f-4d7a-e291-3f8c5b0e1a74"
}

JSON Response

{
"id": "9c4a2f71-8d3e-4b6c-e157-2a0f9b5d3e84"
}

The returned id is the KYB Level 1 attempt ID.


Step 5 - Poll the KYB Attempt

Track progress via the shared KYC attempts endpoints. Polling is optional when webhooks are configured.

HTTP GET Request

https://api.sandbox.avenia.io:10952/v2/kyc/attempts/{attemptId}?subAccountId={subAccountId}

Sample JSON Response

{
"attempt": {
"id": "9c4a2f71-8d3e-4b6c-e157-2a0f9b5d3e84",
"levelName": "kyb-level-1",
"status": "COMPLETED",
"result": "APPROVED",
"resultMessage": "",
"retryable": false,
"createdAt": "2026-03-19T22:09:52.629984Z",
"updatedAt": "2026-03-19T22:09:52.629984Z"
}
}

result is APPROVED or REJECTED. If REJECTED and retryable is true, fix the data and resubmit Step 4.

See KYC Level 1 - List attempts for the full list/filter reference.


Quick Reference

StepMethodEndpointOutput
1POST/v2/account/sub-accountssubAccountId
2POST + PUT/v2/documents?subAccountId={id} + S3 URLDocument IDs (Cert. of Inc., Tax ID, UBO IDs)
3POST/v2/account/ubos?subAccountId={id}UBO ID
4POST/v2/kyc/new-level-1/api?subAccountId={id}KYB Level 1 attempt ID
5GET/v2/kyc/attempts/{attemptId}?subAccountId={id}Poll until APPROVED, or wait for webhook

After KYB Level 1 is approved, move on to KYB USD and/or KYB EUR to unlock the corresponding fiat rails.