Brazilian Tax Compliance
Three validators, one onboarding flow. Here's how to validate a Brazilian company's CNPJ, the owner's CPF, and optionally an EU VAT number for cross-border transactions — and why verifying both the business and the individual behind it is essential for KYC compliance.
In this guide
- 1. Why Brazilian tax compliance requires multiple identifiers
- 2. The identifiers you need
- 3. Step 1: Validate the CNPJ (business)
- 4. Step 2: Validate the CPF (individual)
- 5. Step 3: Validate the VAT number (cross-border)
- 6. Putting it all together — KYC onboarding flow
- 7. cURL examples
- 8. Handling edge cases
- 9. Summary checklist
1. Why Brazilian tax compliance requires multiple identifiers
Brazil's tax system is built on two foundational identifiers: the CNPJ (Cadastro Nacional da Pessoa Juridica) for businesses and the CPF (Cadastro de Pessoas Fisicas) for individuals. Every business transaction in Brazil requires a valid CNPJ, and every individual — including business owners and directors — is identified by their CPF.
For KYC onboarding of a Brazilian company, you need to verify both: the business itself (CNPJ) and the person behind it (CPF). The CNPJ validation includes a Receita Federal lookup that returns the company's legal name, status, address, and incorporation date. The CPF validation confirms the individual's tax ID is structurally valid.
For cross-border transactions — especially when a Brazilian company trades with EU entities — you may also need to validate an EU VAT number for the European counterparty. This enables proper VAT handling on international invoices.
2. The identifiers you need
Each identifier covers a different layer of Brazilian tax compliance:
| Identifier | Issuer | What it proves | API endpoint |
|---|---|---|---|
| CNPJ | Receita Federal | The business is registered with the Brazilian Federal Revenue | GET /v0/br/cnpj |
| CPF | Receita Federal | The individual has a valid Brazilian tax ID | GET /v0/br/cpf |
| VAT | EU VIES | The EU counterparty has an active VAT registration (cross-border) | GET /v0/vat |
3. Step 1: Validate the CNPJ (business)
The CNPJ endpoint performs a multi-layer validation:
- Dual check-digit validation — two sequential modulo-11 calculations verify the last two digits
- Format validation — 14 digits with optional XX.XXX.XXX/XXXX-XX formatting
- Receita Federal lookup — queries the federal registry for company name, status, legal nature, and address
- Branch detection — identifies whether the CNPJ is a headquarters (0001) or branch (0002+)
Example response
{ "valid": true, "cnpj": "11222333000181", "formatted": "11.222.333/0001-81", "found": true, "entity": { "name": "EMPRESA EXEMPLO LTDA", "tradeName": "EXEMPLO", "status": "ATIVA", "legalNature": "2062 - SOCIEDADE EMPRESARIA LIMITADA", "openingDate": "2015-06-20", "city": "SAO PAULO", "state": "SP" } }
What to check in your code
valid === true — the CNPJ passes format and dual check-digit validation
found === true — the company exists in the Receita Federal registry
entity.status === "ATIVA" — the company is currently active, not suspended or closed
4. Step 2: Validate the CPF (individual)
The CPF endpoint validates the Brazilian individual tax ID:
- Dual check-digit validation — two sequential modulo-11 calculations, similar to CNPJ
- Format validation — 11 digits with optional XXX.XXX.XXX-XX formatting
- Known-invalid detection — rejects CPFs with all identical digits (e.g., 111.111.111-11) which pass the checksum but are invalid
- State of origin — the 9th digit identifies the issuing state (fiscal region)
Example response
{ "valid": true, "cpf": "12345678909", "formatted": "123.456.789-09", "fiscalRegion": 9, "state": "PR" }
What to check in your code
valid === true — the CPF passes format, check-digit, and known-invalid detection
state — cross-reference with the company's CNPJ state for geographic consistency
formatted — use the formatted version for display and storage consistency
5. Step 3: Validate the VAT number (cross-border)
When a Brazilian company trades with an EU entity, validating the EU counterparty's VAT number ensures proper tax handling on cross-border invoices:
- Country-specific checksum — validates the EU VAT number format and check digits
- VIES lookup — confirms the VAT registration is active in the EU system
- Company details — returns the registered name and address for invoice verification
Example response
{ "valid": true, "countryCode": "DE", "vatNumber": "123456789", "normalized": "DE123456789", "vies": { "checked": true, "valid": true, "name": "EUROPEAN PARTNER GMBH", "address": "MUSTERSTRASSE 1, 10115 BERLIN" } }
What to check in your code
vies.valid === true — the EU counterparty's VAT registration is active
vies.name — matches the EU counterparty's name on the invoice
vies.address — confirms the registered business address for invoice accuracy
6. Putting it all together — KYC onboarding flow
The following Node.js example validates a Brazilian business (CNPJ + lookup) and its owner (CPF) in parallel. If the transaction involves an EU counterparty, it also validates the EU VAT number. Use the @isvalid-dev/sdk package or the native fetch API (Node 18+).
import { createClient } from '@isvalid-dev/sdk'; const iv = createClient({ apiKey: process.env.ISVALID_API_KEY }); async function verifyBrazilianBusiness({ cnpj, cpf, euVat }) { const calls = [ iv.br.cnpj(cnpj), iv.br.cpf(cpf), ]; // Optionally validate EU counterparty VAT for cross-border if (euVat) { calls.push(iv.vat(euVat, { checkVies: true })); } const results = await Promise.all(calls); const [cnpjResult, cpfResult] = results; const vatResult = euVat ? results[2] : null; // ── Cross-reference: CPF state vs CNPJ state ────────────────────────── const stateMatch = cpfResult.state === cnpjResult.entity?.state; return { cnpj: { valid: cnpjResult.valid, found: cnpjResult.found ?? null, status: cnpjResult.entity?.status ?? null, name: cnpjResult.entity?.name ?? null, legalNature: cnpjResult.entity?.legalNature ?? null, state: cnpjResult.entity?.state ?? null, }, cpf: { valid: cpfResult.valid, state: cpfResult.state ?? null, formatted: cpfResult.formatted ?? null, }, vat: vatResult ? { valid: vatResult.valid, confirmed: vatResult.vies?.valid ?? null, name: vatResult.vies?.name ?? null, } : null, crossChecks: { stateMatch, }, }; } // ── Example: KYC onboarding with EU cross-border ─────────────────────────── const result = await verifyBrazilianBusiness({ cnpj: '11222333000181', cpf: '12345678909', euVat: 'DE123456789', }); console.log('CNPJ:', result.cnpj.found ? `✓ ${result.cnpj.name} (${result.cnpj.status})` : '✗ not found'); console.log('CPF :', result.cpf.valid ? `✓ ${result.cpf.formatted}` : '✗ invalid'); if (result.vat) { console.log('VAT :', result.vat.confirmed ? `✓ ${result.vat.name}` : '✗ not confirmed'); } console.log('State match:', result.crossChecks.stateMatch ? '✓' : '⚠ different states'); const approved = result.cnpj.valid && result.cnpj.found && result.cnpj.status === 'ATIVA' && result.cpf.valid && (!result.vat || result.vat.confirmed); console.log('KYC:', approved ? 'APPROVED' : 'REJECTED');
Promise.all. The total latency is the slowest single call. For domestic-only flows (no EU VAT), only two calls are made: CNPJ + CPF.7. cURL examples
Validate a CNPJ number:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/br/cnpj?value=11222333000181"
Validate a CPF number:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/br/cpf?value=12345678909"
Validate an EU VAT number with VIES check (for cross-border):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/vat?value=DE123456789&checkVies=true"
8. Handling edge cases
Brazilian tax compliance has unique challenges related to the dual-identifier system, company statuses, and cross-border scenarios:
CNPJ found but status is not ATIVA
The CNPJ exists in Receita Federal but has a status other than ATIVA. Brazilian companies can be SUSPENSA (suspended), INAPTA (unfit), BAIXADA (closed), or NULA (annulled). Only ATIVA companies should be onboarded.
if (cnpjResult.found && cnpjResult.entity?.status !== 'ATIVA') { // Company exists but is not active — reject return { status: 'rejected', reason: `CNPJ status is ${cnpjResult.entity.status} — only ATIVA companies can be onboarded`, }; }
CPF valid but identical digits
CPFs where all 11 digits are the same (e.g., 111.111.111-11) pass the modulo-11 checksum algorithm but are known to be invalid. These are commonly entered as test data or placeholders. The API automatically rejects these, so valid will be false.
// The API handles this automatically — no special code needed // CPFs like 000.000.000-00 or 111.111.111-11 will return valid: false if (!cpfResult.valid) { return { status: 'rejected', field: 'cpf', reason: 'Invalid CPF — check digit validation failed', }; }
CNPJ is a branch, not headquarters
A CNPJ has a 4-digit branch identifier (digits 9-12). The headquarters is always 0001, while branches are 0002, 0003, etc. For KYC onboarding, you typically want to verify the headquarters CNPJ. If a branch CNPJ is provided, flag it and request the headquarters number.
// Extract branch number from CNPJ (digits 9-12 in the 14-digit number) const branchCode = cnpjResult.cnpj.substring(8, 12); const isHeadquarters = branchCode === '0001'; if (!isHeadquarters) { return { status: 'needs_correction', reason: `CNPJ ${cnpjResult.formatted} is a branch (unit ${branchCode}). Please provide the headquarters CNPJ.`, }; }
CPF and CNPJ in different states
The CPF's fiscal region identifies the state where the individual was registered, while the CNPJ contains the company's registered state. A mismatch is not necessarily a problem — business owners can live in a different state from their company. Log the mismatch as a data point but do not reject.
if (cpfResult.state !== cnpjResult.entity?.state) { // Owner and company are in different states — log but don't reject logger.info(`State mismatch: CPF=${cpfResult.state}, CNPJ=${cnpjResult.entity.state}`); // This is common for multi-state businesses or remote owners result.riskNotes = result.riskNotes ?? []; result.riskNotes.push('owner_company_state_mismatch'); }
9. Summary checklist
See also
Validate Brazilian tax identifiers with one API
Free tier includes 100 API calls per day. No credit card required. CNPJ, CPF, and VAT validation all included.