Desde julho de 2026, as novas inscrições no Cadastro Nacional da Pessoa Jurídica saem no formato alfanumérico: a raiz e a ordem do estabelecimento aceitam letras de A a Z além dos algarismos. Foi a maior alteração na estrutura do documento desde a criação do cadastro, e continua derrubando integrações em sistemas que trataram CNPJ como número. Este guia mostra o que mudou, como o dígito verificador passou a ser calculado e o que ainda precisa ser revisado num sistema legado.
A estrutura do CNPJ, posição por posição
São catorze posições em três blocos. As oito primeiras formam a raiz, que identifica a empresa. As quatro seguintes são a ordem do estabelecimento — 0001 na matriz, valores crescentes nas filiais. As duas últimas são os dígitos verificadores, calculados por módulo 11 sobre as doze anteriores. Matriz e filiais compartilham a raiz e diferem só na ordem.
Essa divisão não mudou, e a máscara também não: continua XX.XXX.XXX/XXXX-XX, com os mesmos pontos, a mesma barra e o mesmo hífen. O que mudou foi o alfabeto aceito em cada posição.
O que mudou na prática
As doze posições da base passaram a aceitar 0-9 e A-Z maiúsculo. Os dois dígitos verificadores permaneceram sempre numéricos. Um CNPJ alfanumérico tem esta cara: 12.ABC.345/01DE-35.
Nenhuma empresa trocou de número. Os CNPJs emitidos antes da mudança seguem válidos por tempo indeterminado e nunca serão convertidos, o que tem uma consequência prática desconfortável: não existe data de corte depois da qual o formato numérico deixa de circular. Os dois convivem em produção, misturados no mesmo banco, na mesma fila e no mesmo arquivo de integração — o suporte ao legado é permanente, não transitório.
Vale registrar o que ficou igual, porque é o que evita retrabalho: o comprimento seguiu em catorze caracteres, a máscara é a mesma, a separação entre raiz e ordem é a mesma, filiais continuam diferindo da matriz apenas no bloco da ordem e os dois últimos caracteres continuam sendo dígitos. Quem já guardava o documento como texto de catorze posições, sem máscara, tem quase nada a fazer além do cálculo do DV.
O motivo da mudança é aritmético. Com oito posições numéricas, a raiz oferece 108 combinações, cerca de cem milhões, das quais boa parte já foi consumida em décadas de aberturas, baixas e reservas. Com 36 símbolos por posição, o mesmo campo passa a 368, algo em torno de 2,8 trilhões de combinações. O formato do documento foi preservado e o espaço de numeração deixou de ser um problema de curto prazo.
A tabela de conversão de caracteres
Para que o módulo 11 continuasse funcionando, cada caractere precisou de um valor numérico. A regra adotada é o código ASCII menos 48. Como '0' vale 48 no ASCII, os algarismos mapeiam para eles mesmos — 0 continua 0, 9 continua 9. As letras começam em A, que é 65 no ASCII e portanto vale 17, e seguem sem interrupção até Z, que vale 42. Repare no salto de 9 para 17: as sete posições ASCII entre 9 e A são ocupadas por outros símbolos e ficam de fora da tabela.
| Caractere | Valor | Caractere | Valor | Caractere | Valor |
|---|---|---|---|---|---|
| 0 | 0 | C | 19 | O | 31 |
| 1 | 1 | D | 20 | P | 32 |
| 2 | 2 | E | 21 | Q | 33 |
| 3 | 3 | F | 22 | R | 34 |
| 4 | 4 | G | 23 | S | 35 |
| 5 | 5 | H | 24 | T | 36 |
| 6 | 6 | I | 25 | U | 37 |
| 7 | 7 | J | 26 | V | 38 |
| 8 | 8 | K | 27 | W | 39 |
| 9 | 9 | L | 28 | X | 40 |
| A | 17 | M | 29 | Y | 41 |
| B | 18 | N | 30 | Z | 42 |
A elegância da escolha está aí: como os algarismos mapeiam para si mesmos, a mesma rotina serve para os dois formatos. Não é preciso um caminho de código para CNPJ antigo e outro para CNPJ novo.
O exemplo oficial resolvido
O exemplo divulgado pela Receita Federal e pelo SERPRO é 12.ABC.345/01DE-35. A base é 12ABC34501DE e os pesos são os mesmos de sempre: 2 a 9 ciclicamente, da direita para a esquerda. Para os doze caracteres da base, isso resulta em 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3 e 2 na leitura da esquerda para a direita.
| Caractere | Valor | Peso | Produto |
|---|---|---|---|
| 1 | 1 | 5 | 5 |
| 2 | 2 | 4 | 8 |
| A | 17 | 3 | 51 |
| B | 18 | 2 | 36 |
| C | 19 | 9 | 171 |
| 3 | 3 | 8 | 24 |
| 4 | 4 | 7 | 28 |
| 5 | 5 | 6 | 30 |
| 0 | 0 | 5 | 0 |
| 1 | 1 | 4 | 4 |
| D | 20 | 3 | 60 |
| E | 21 | 2 | 42 |
| Soma | 459 | ||
459 ÷ 11 → resto 8
Resto 8, maior ou igual a 2, então o dígito é 11 − 8.
Dígito verificador = 3
O segundo dígito repete a conta sobre treze caracteres — a base mais o dígito recém-obtido —, o que empurra todos os pesos uma casa e adiciona o peso 6 no início:
| Caractere | Valor | Peso | Produto |
|---|---|---|---|
| 1 | 1 | 6 | 6 |
| 2 | 2 | 5 | 10 |
| A | 17 | 4 | 68 |
| B | 18 | 3 | 54 |
| C | 19 | 2 | 38 |
| 3 | 3 | 9 | 27 |
| 4 | 4 | 8 | 32 |
| 5 | 5 | 7 | 35 |
| 0 | 0 | 6 | 0 |
| 1 | 1 | 5 | 5 |
| D | 20 | 4 | 80 |
| E | 21 | 3 | 63 |
| 3 | 3 | 2 | 6 |
| Soma | 424 | ||
424 ÷ 11 → resto 6
Resto 6, maior ou igual a 2, então o dígito é 11 − 6.
Dígito verificador = 5
Daí o sufixo -35. A regra do resto continua idêntica à do CPF e dos demais documentos de módulo 11: resto 0 ou 1 produz dígito 0, qualquer outro resto produz 11 − resto.
Validando os dois formatos em TypeScript
A implementação abaixo aceita numérico e alfanumérico com um único cálculo. Os dois detalhes que costumam passar despercebidos são a normalização (remover a máscara e passar para maiúsculas, já que 'a' vale 97 no ASCII e produziria 49 em vez de 17) e a distinção entre os dois formatos apenas na expressão regular de estrutura.
const NUMERIC = /^\d{14}$/;
const ALPHANUMERIC = /^[0-9A-Z]{12}\d{2}$/;
const WEIGHTS = [2, 3, 4, 5, 6, 7, 8, 9];
// '0'-'9' -> 0-9 ; 'A'-'Z' -> 17-42
const charValue = (char: string) => char.charCodeAt(0) - 48;
function checkDigit(base: string): number {
let sum = 0;
for (let i = base.length - 1, w = 0; i >= 0; i--, w++) {
sum += charValue(base[i]) * WEIGHTS[w % 8];
}
const remainder = sum % 11;
return remainder < 2 ? 0 : 11 - remainder;
}
export function isValidCnpj(input: string): boolean {
const cnpj = input.replace(/[.\-/]/g, "").toUpperCase();
if (!NUMERIC.test(cnpj) && !ALPHANUMERIC.test(cnpj)) return false;
if (/^(\d)\1{13}$/.test(cnpj)) return false; // 00000000000000 e afins
const base = cnpj.slice(0, 12);
const first = checkDigit(base);
const second = checkDigit(`${base}${first}`);
return cnpj.slice(12) === `${first}${second}`;
}
isValidCnpj("12.ABC.345/01DE-35"); // true
isValidCnpj("11.222.333/0001-81"); // true
isValidCnpj("12.ABC.345/01DE-34"); // falseVersões comentadas em outras linguagens, com os casos de teste equivalentes, estão nos guias de validação em JavaScript e TypeScript e de validação em Python, Java e C#.
Mensagens de erro que vale separar
Um booleano resolve a validação, mas atende mal quem está digitando. Com dois formatos em circulação, quatro situações de recusa têm causas diferentes e merecem mensagens diferentes:
- Comprimento errado depois de remover a máscara. Quase sempre é um caractere a mais ou a menos na digitação, não um documento inexistente.
- Caractere fora do conjunto permitido. Acentos, cedilha ou espaço no meio indicam texto colado de outro sistema. Aqui a mensagem útil diz o conjunto aceito:
0-9eA-Znas doze primeiras posições. - Letra em um dos dois dígitos verificadores. Esse é o erro novo, e o mais confuso para o usuário, que viu letras no começo do número e supôs que elas valessem em qualquer posição.
- Dígito verificador incorreto. Estrutura certa, conta errada. Vale dizer isso explicitamente, porque muda o que a pessoa deve conferir: o número em si, e não o jeito de digitá-lo.
No formato numérico ainda cabe a recusa explícita das sequências repetidas, do tipo 00.000.000/0000-00, rejeitadas por convenção mesmo quando a conta fecha. É uma guarda extra, não parte do módulo 11, e continua valendo só para o formato antigo.
Exibição e conferência humana
O conjunto de caracteres vai de A a Z sem exclusões, então O e 0, I e 1, S e 5 podem aparecer no mesmo documento. Quem lê um CNPJ em voz alta ao telefone ou confere um número impresso perde a pista que os catorze algarismos davam. Em telas de conferência, uma fonte monoespaçada com zero cortado e a máscara sempre aplicada reduzem bastante o ruído; em comprovantes, separar visualmente os blocos ajuda mais do que aumentar o corpo da fonte.
Do lado da entrada, aceite o que o usuário colar e normalize por conta própria: minúsculas, espaços nas pontas, máscara parcial, máscara completa. Rejeitar por causa de um espaço colado junto é a forma mais fácil de transformar uma mudança de formato em chamado de suporte.
Checklist de adequação para sistemas legados
A adaptação costuma ser rasa em dificuldade e larga em superfície: são muitos pontos pequenos espalhados pela aplicação. Os que mais aparecem:
- Tipo da coluna. CNPJ guardado em
integer,bigintounumericjá era arriscado por causa dos zeros à esquerda; com letras, simplesmente não entra. A coluna precisa serchar(14)ouvarchar(14). O guia de validação no banco de dados traz as funções e as restriçõesCHECKcorrespondentes. - Conversões implícitas para número. Procure por
parseInt,Number(),::biginte equivalentes aplicados ao documento.parseIntem12ABC34501DE35devolve12em silêncio, sem erro e sem aviso — é o tipo de falha que só aparece em produção. - Expressões regulares. Todo
\d{14}precisa virar uma alternativa entre os dois formatos, com os dois últimos caracteres presos a dígitos. Padrões prontos para os demais documentos estão no guia de expressões regulares de documentos brasileiros. - Normalização de entrada. Antes de calcular o DV, remova a máscara e aplique
toUpperCase(). Sem isso, um usuário que digita minúsculas recebe "CNPJ inválido" para um documento correto. - Campos de formulário. Máscaras que bloqueiam teclas não numéricas passam a rejeitar letras legítimas, e
inputmode="numeric"abre o teclado errado no celular. Os dois últimos caracteres continuam numéricos, então a máscara pode ser mista. - Ordenação e comparação. Uma coluna de texto ordena por ASCII, e os algarismos vêm antes das letras. Relatórios que agrupavam por faixa numérica de CNPJ precisam de outro critério; comparações devem ser feitas sempre sobre a forma normalizada, nunca sobre a mascarada.
- Arquivos de largura fixa e integrações. O tamanho não mudou, então o leiaute cabe. O que quebra são os parsers que interpretam o campo como inteiro e os bancos intermediários com coluna numérica no caminho. Vale rodar um lote alfanumérico ponta a ponta, incluindo os sistemas de terceiros.
- Buscas e índices. Se a coluna passar de numérica para texto, o índice muda de natureza e consultas por prefixo de raiz precisam usar comparação de texto. Guardar também a forma normalizada, sem máscara e em maiúsculas, em coluna própria evita busca com
LIKEsobre o valor formatado. - Chaves de cache e deduplicação. Se em algum ponto o sistema usa o CNPJ como chave, a normalização precisa ser idêntica em todos eles. Uma rotina que passa para maiúsculas e outra que não passa produzem duas entradas para a mesma empresa.
- Testes. Fixtures que só contêm CNPJs numéricos passam a dar falsa sensação de cobertura. Cada caso de teste de CNPJ merece um par alfanumérico ao lado, e ao menos um caso negativo com letra nos dígitos verificadores.
Gerando massa de teste com os dois formatos
Nenhum desses pontos é verificável sem dados. O gerador de CNPJ do bateCarimbo produz lotes nos dois formatos, com dígitos verificadores calculados pela mesma regra descrita aqui, e valida uma lista colada de uma vez informando qual formato foi detectado em cada linha. Para cenários que precisam de razão social, endereço e demais campos coerentes, o gerador de empresas monta a ficha inteira. Como organizar essas fixtures no repositório é assunto do guia de massa de dados de teste. Todos os documentos gerados são fictícios e existem para exercitar código, não para representar empresas reais.