---
title: "CNPJ Alfanumérico — Documentação Técnica"
description: "Guia técnico para desenvolvedores e arquitetos: contratos JSON antes/depois, comportamento por produto, webhooks, códigos de status e checklist de migração para o CNPJ alfanumérico."
source_url: https://nfe.io/docs/release-notes/2026-3-cnpj-alfanumerico/documentacao-tecnica
last_updated: 2026-06-14
---

# CNPJ Alfanumérico — Documentação Técnica

Este guia é o companheiro técnico do [Comunicado](/docs/release-notes/2026-3-cnpj-alfanumerico). Se você é desenvolvedor ou arquiteto e precisa adaptar uma integração, é aqui que estão os contratos JSON, os comportamentos por produto e o checklist de migração.

> **Base regulatória:** IN RFB 2.229/2024 · NT Conjunta ENCAT 2025.001 v1.00.

---

## Índice

1. [Visão geral em 30 segundos](#1-visão-geral-em-30-segundos)
2. [O novo formato do CNPJ](#2-o-novo-formato-do-cnpj)
3. [O princípio de versionamento da NFE.io](#3-o-princípio-de-versionamento-da-nfeio)
4. [O que muda no contrato da API](#4-o-que-muda-no-contrato-da-api)
5. [Mudanças por produto](#5-mudanças-por-produto)
6. [Webhooks](#6-webhooks)
7. [Códigos de status e mensagens de erro](#7-códigos-de-status-e-mensagens-de-erro)
8. [Checklist de preparação](#8-checklist-de-preparação)
9. [Cenários de teste em homologação](#9-cenários-de-teste-em-homologação)
10. [Perguntas frequentes](#10-perguntas-frequentes)

---

## 1. Visão geral em 30 segundos

A partir de julho de 2026, a Receita Federal passa a emitir CNPJs que podem conter letras. Isso afeta diretamente quem já integra com a gente — então a pergunta que guiou todo o nosso desenho foi: **como dar suporte ao formato novo sem que ninguém precise mexer no que já funciona hoje?**

A resposta foi criar uma **V3** das APIs, com uma regra simples de entender:

| Versão | Status | Tipo de `federalTaxNumber` na request | Tipo na response/webhook |
|---|---|---|---|
| V1 | congelada | `number` | `number` |
| V2 | congelada (boundary defensivo) | `number` | `number` |
| **V3** | **nova** | `string` (aceita `number` por compatibilidade na entrada) | **sempre `string`** |

Se você guardar só uma frase deste documento, que seja esta:

> **Mantra:** *"V2 nunca enxerga CNPJ alfa. V3 é alfa-aware."*

---

## 2. O novo formato do CNPJ

### 2.1 Especificação

O CNPJ continua com 14 posições, mas agora as 12 primeiras podem misturar letras e números. Só os dois dígitos verificadores no final permanecem estritamente numéricos:

```
14 posições totais
   ├── 12 alfanuméricas (raiz 8 + ordem 4) — pode conter [A-Z0-9]
   └── 2 numéricas (dígitos verificadores) — sempre [0-9]

Letras vedadas: I, O, U, Q, F
   (evitam confusão visual com 0 e 1)

Regex efetiva: ^[A-HJ-NPRT-Z0-9]{12}[0-9]{2}$
```

### 2.2 Exemplos

Lado a lado, fica fácil ver a diferença:

```text
CNPJ numérico legado:  12345678000199    (14 dígitos)
CNPJ alfanumérico:     12ABC345000ABC    (12 alfanum + 2 dígitos)
```

### 2.3 Algoritmo do dígito verificador

O cálculo continua sendo o bom e velho **Módulo 11** — a novidade é que os pesos passam a ser aplicados sobre o **valor ASCII menos 48** de cada caractere. Na prática, isso faz os dígitos `0`–`9` continuarem valendo `0`–`9` e dá um valor numérico para cada letra:

```
'0' (ASCII 48) →  0
'9' (ASCII 57) →  9
'A' (ASCII 65) → 17
'Z' (ASCII 90) → 42
```

Se você valida o dígito verificador no seu lado, precisa atualizar esse algoritmo. Nossa recomendação, porém, é **não reinventar a roda: delegue para uma biblioteca oficial de validação de CNPJ** da sua linguagem. Assim você não precisa se preocupar com os detalhes do cálculo.

---

## 3. O princípio de versionamento da NFE.io

### 3.1 Tabela canônica

Esta tabela resume, camada por camada, o que acontece quando um CNPJ alfanumérico encontra cada versão da API. A leitura é direta: na coluna legado, o sistema **protege** você do formato novo; na coluna V3, ele o **entrega** de forma consistente.

| Camada | V1 / V2 (legado) | V3 (alfa-aware) |
|---|---|---|
| API de Empresas (cadastro) | empresa alfa → **404 Not Found** | aceita, response `string` |
| Emissão de NF-e/NFC-e | issuer alfa → **400 Bad Request** | aceita, response `string` |
| Emissão de NFS-e | issuer alfa → **400 Bad Request** | aceita, response `string` |
| Distribuição de Documentos Fiscais (inbound) | nota alfa → **ignorada silenciosamente** | aparece normalmente, response `string` |
| Webhook | nota alfa → **não envia** para subscriber V2 | envia `string` |
| API de Consulta de Notas | CNPJ alfa → **404 Not Found** | aceita, response `string` |
| Response JSON | **sempre `number`** | **sempre `string`** |

### 3.2 Por que desenhamos assim

Três objetivos guiaram esse desenho — e vale a pena entender cada um, porque eles explicam praticamente todo o comportamento descrito neste documento:

1. **Zero regressão.** Para clientes V1/V2, nada muda — nem no schema, nem no tipo do campo, nem no comportamento. Quem está integrado hoje pode ignorar tranquilamente toda essa mudança.
2. **Sem flags, sem espera.** Não existe feature flag, whitelist nem código `412` para "habilitar" o alfa. Quem decide quando o alfa entra em produção é a SEFAZ (e as prefeituras): no instante em que elas liberam o XSD alfa, o sistema NFE.io passa a autorizar (`cStat=100`) automaticamente, sem nenhuma intervenção manual da nossa parte.
3. **Determinístico.** A V3 não escolhe o tipo do JSON conforme o valor do CNPJ — ele é **sempre `string`**. Isso significa que seu parser nunca precisa lidar com union types nem adivinhar o formato; é texto, ponto.

---

## 4. O que muda no contrato da API

### 4.1 Request — V3 aceita os dois tipos

Para facilitar a sua vida na migração, a V3 é flexível na entrada: ela aceita `federalTaxNumber` como **string** (o jeito recomendado) ou como **number** (por compatibilidade, útil enquanto você ainda tem código antigo). Internamente, tudo é normalizado para `string`.

```jsonc
// ✅ Recomendado (V3)
{
  "federalTaxNumber": "12ABC345000ABC"
}

// ✅ Também aceito (V3, compat — para CNPJ numérico)
{
  "federalTaxNumber": 12345678000199
}

// ❌ V2 rejeita string com letras
{
  "federalTaxNumber": "12ABC345000ABC"  // → 400 Bad Request
}
```

### 4.2 Response — V3 sempre string

Na saída, a V3 é rígida de propósito: **sempre `string`**, seja o CNPJ numérico ou alfanumérico. Essa consistência é o que permite que o seu parser não tenha surpresas.

```jsonc
// V2 (hoje e amanhã — congelada)
{
  "id": "...",
  "federalTaxNumber": 12345678000199,    // number
  "name": "Empresa Exemplo S.A."
}

// V3 — CNPJ numérico
{
  "id": "...",
  "federalTaxNumber": "12345678000199",  // string
  "name": "Empresa Exemplo S.A."
}

// V3 — CNPJ alfanumérico
{
  "id": "...",
  "federalTaxNumber": "12ABC345000ABC",  // string
  "name": "Empresa Nova Ltda."
}
```

### 4.3 Selecionando a versão

A versão é escolhida por **path** na URL (`/v2/` ou `/v3/`) ou por **header**, dependendo de cada produto. Os detalhes estão na seção [Mudanças por produto](#5-mudanças-por-produto).

---

## 5. Mudanças por produto

A regra geral é sempre a mesma, mas cada produto tem suas particularidades. Veja a sua abaixo.

### 5.1 API de Empresas (cadastro)

**Endpoint base:** `https://api.nfe.io/v{version}/companies`

| Operação | V2 (legado) | V3 (alfa-aware) |
|---|---|---|
| `POST /companies` com CNPJ numérico | `201 Created`, response `number` | `201 Created`, response `string` |
| `POST /companies` com CNPJ alfa | n/a — não pode mandar `string` com letras (`400`) | `201 Created`, response `string` |
| `GET /companies/{id}` de empresa numérica | `200 OK`, `federalTaxNumber: number` | `200 OK`, `federalTaxNumber: string` |
| `GET /companies/{id}` de empresa alfa | **`404 Not Found`** | `200 OK`, `federalTaxNumber: string` |
| `GET /companies?federalTaxNumber=12ABC...` | `404` | `200 OK` |

> **Importante:** uma empresa alfa cadastrada via V3 **continua existindo no banco** — ela só é **invisível** para a V2. Se o cliente da V2 tentar consultá-la, recebe `404`. Isso não é bug: é exatamente o princípio "V2 nunca vê alfa" em ação.

### 5.2 API de NF-e / NFC-e

**Endpoint base:** `https://api.nfe.io/v{version}/companies/{companyId}/productinvoices`

| Cenário | V2 | V3 |
|---|---|---|
| Emissão com `issuer.federalTaxNumber` numérico | ✅ normal | ✅ normal, response `string` |
| Emissão com `issuer.federalTaxNumber` alfa | **`400 Bad Request`**: `{"message":"Use V3 for alphanumeric CNPJ"}` | ✅ chave de acesso gerada com DV ASCII−48, XML enviado à SEFAZ |
| Consulta de NF-e emitida em V3 (CNPJ alfa) via endpoint V2 | **`404`** | `200 OK`, response `string` |

Alguns pontos que costumam gerar dúvida:

- **Chave de acesso (44 chars)** — quando o emissor tem letras no CNPJ, a chave passa a usar **Módulo 11 com ASCII−48**. Você não precisa se preocupar com isso: a NFE.io gera a chave correta automaticamente.
- **DANFE (PDF)** — foi atualizado para imprimir o barcode da chave alfa. Se você só consome o PDF, não precisa mudar nada.
- **GNRE (PE/ES)** — sem nenhuma mudança de contrato para o cliente.

### 5.3 API de NFS-e

**Endpoint base:** `https://api.nfe.io/v{version}/companies/{companyId}/serviceinvoices`

| Cenário | V2 | V3 |
|---|---|---|
| Emissão de NFS-e com emissor numérico | ✅ normal | ✅ normal, response `string` |
| Emissão de NFS-e com emissor alfa | **`400 Bad Request`** ("Use V3...") | ✅ depende da prefeitura aceitar (gate natural) |
| Consulta de NFS-e emitida em V3 alfa via V2 | **`404`** | `200 OK`, response `string` |

> **Atenção, prefeituras:** a adoção do CNPJ alfa varia bastante de um município para outro. Algumas prefeituras já atualizaram seus webservices; outras ainda não. A NFE.io **não filtra** essa resposta — manda a nota para a prefeitura e devolve exatamente o que ela responder. Se a prefeitura ainda rejeita, você recebe o erro como ele vem, sem mascaramento.

### 5.4 Distribuição de Documentos Fiscais (recebimento de DF-e)

**Endpoint base:** `https://api.nfe.io/v{version}/dfe/inbound`

| Cenário | V2 | V3 |
|---|---|---|
| Nota inbound recebida com CNPJ emissor numérico | ✅ entregue, `number` | ✅ entregue, `string` |
| Nota inbound recebida com CNPJ emissor alfa | 🔇 **ignorada silenciosamente** (não aparece no inbox V2) | ✅ entregue normalmente, `string` |
| Webhook de nota numérica | ✅ disparado, `number` | ✅ disparado, `string` |
| Webhook de nota alfa | 🚫 **não disparado** para subscribers V2 | ✅ disparado, `string` |

> Vale tranquilizar: notas alfa não desaparecem. Elas **existem** no nosso sistema e ficam disponíveis para consumidores V3. Quando você quiser deixar de filtrá-las, basta migrar para a V3.

### 5.5 API de Consulta de Notas (consulta de NF-e)

**Endpoint base:** `https://api.nfe.io/v{version}/lookup`

| Cenário | V2 | V3 / V4 |
|---|---|---|
| Lookup por chave de acesso numérica | ✅ normal | ✅ normal |
| Lookup por chave alfa | **`404`** | `200 OK`, response `string` |
| Scan / busca por CNPJ alfa | **`404`** | `200 OK` |

---

## 6. Webhooks

### 6.1 Regra de envio

Aqui o critério é importante e às vezes contraintuitivo, então vale destacar:

> **O webhook segue a versão do contrato que o subscriber assinou** — e não o valor do CNPJ da nota.

| Subscriber assinado em | CNPJ numérico | CNPJ alfanumérico |
|---|---|---|
| **V2** | ✅ entregue, payload com `federalTaxNumber: number` | 🚫 **não entregue** |
| **V3** | ✅ entregue, payload com `federalTaxNumber: string` | ✅ entregue, payload com `federalTaxNumber: string` |

### 6.2 Migração de webhook

A migração de webhook é tranquila e pode ser feita sem downtime:

1. Cadastre uma **nova subscription V3** apontando para um endpoint já preparado para receber `string`.
2. Com as duas rodando em paralelo e a V3 confirmada, **desligue a V2**.
3. Lembre-se: não há "duplicação" automática — se você mantiver as duas subscriptions ativas, vai receber o mesmo evento numérico nas duas versões.

### 6.3 Exemplo de payload — webhook V3

```jsonc
{
  "event": "invoice.authorized",
  "data": {
    "id": "5f9...",
    "issuer": {
      "federalTaxNumber": "12ABC345000ABC",   // string sempre
      "name": "Empresa Nova Ltda."
    },
    "accessKey": "35260512ABC345000ABCxx...",
    "status": "Authorized"
  }
}
```

---

## 7. Códigos de status e mensagens de erro

### 7.1 Tabela canônica

Quando algo dá errado, a resposta é sempre clara sobre o motivo — nada de `500` genérico:

| Status | Quando ocorre | Mensagem típica |
|---|---|---|
| `400 Bad Request` | Tentativa de **emitir** nota com CNPJ alfa em V2 (NF-e/NFC-e/NFS-e) | `{"message":"Use V3 for alphanumeric CNPJ","code":"ALPHANUMERIC_CNPJ_REQUIRES_V3"}` |
| `400 Bad Request` | `federalTaxNumber` com formato inválido (tamanho ≠ 14, letras vedadas I/O/U/Q/F, DV inválido) | `{"message":"Invalid federalTaxNumber"}` |
| `404 Not Found` | Consulta de entidade alfa via V1/V2 (API de Empresas, API de Consulta de Notas) | `{"message":"Not Found"}` |
| `cStat=215` (resposta SEFAZ) | XML chegou à SEFAZ, mas SEFAZ rejeitou (XSD antigo) | resposta real da SEFAZ, sem mascaramento NFE.io |
| `cStat=100` | SEFAZ aceitou (caminho feliz, alfa autorizado) | `Autorizado o uso da NF-e` |

### 7.2 O que **não** existe

Para evitar que você procure por algo que não vai encontrar:

- ❌ Não há `412 Precondition Failed` — não usamos esse código para isto.
- ❌ Não há feature flag de "habilitar CNPJ alfa para minha conta".
- ❌ Não há whitelist por AccountId.

---

## 8. Checklist de preparação

### 8.1 Para quem só usa CNPJ numérico

A boa notícia: é uma lista curtíssima.

- [ ] Documentar internamente que **`federalTaxNumber` em V2 continua `number`** — nada precisa mudar.
- [ ] Treinar suporte/atendimento: quando um cliente final pedir suporte a CNPJ alfa, **direcionar para a migração V3**.

### 8.2 Para quem vai suportar CNPJ alfanumérico

Aqui vale ser metódico. Separamos por frente para você não esquecer nenhum ponto.

#### Código

- [ ] Tipo de `federalTaxNumber` em modelos / DTOs / entities → `string` (não `long`, não `int64`, não `bigint`).
- [ ] Validação de formato → aceita `[A-HJ-NPRT-Z0-9]{12}[0-9]{2}` ou usar a lib oficial da sua linguagem para CNPJ.
- [ ] Validação de DV → Módulo 11 com **ASCII−48**.
- [ ] Remover qualquer `Long.parseLong(cnpj)`, `int64(cnpj)`, `cnpj.toFixed(0)`, `cnpj.padStart(14, '0')` aplicado **antes de saber se é numérico**.
- [ ] Trocar máscara de display de `"00.000.000/0000-00"` para algo tolerante a letras (ou exibir sem máscara).

#### Banco de dados

- [ ] Coluna `cnpj` / `federal_tax_number` → `VARCHAR(14)` (não `BIGINT`, não `NUMERIC(14)`).
- [ ] Índices de igualdade → continuam funcionando, mas **revise índices que dependiam de ordenação numérica**.
- [ ] Considere `COLLATE` ou normalização para `UPPER` no armazenamento (CNPJs alfa são `[A-Z]`, mas inputs podem vir minúsculos).

#### Integração

- [ ] Trocar endpoint `/v2/...` por `/v3/...` no fluxo onde precisar.
- [ ] Atualizar a webhook subscription para V3.
- [ ] Atualizar parsers JSON para esperar `string` (a maioria das libs lida nativamente, mas linguagens de tipagem forte podem precisar de cast).
- [ ] Logs / observabilidade → garantir que o CNPJ não está sendo serializado como número em algum sink (ex.: mapping do Elasticsearch).

#### Teste

- [ ] Cenário positivo numérico em V3.
- [ ] Cenário positivo alfa em V3.
- [ ] Cenário V2 com payload `string` contendo letras → confirmar `400`.
- [ ] Cenário V2 com `GET` de empresa alfa → confirmar `404`.
- [ ] Webhook V3 com CNPJ alfa → confirmar entrega e parse.

---

## 9. Cenários de teste em homologação

A SEFAZ disponibiliza CNPJs alfanuméricos de teste no ambiente de homologação desde **06/04/2026** — use-os para validar a integração de ponta a ponta antes de julho.

### 9.1 Exemplos didáticos (não reais — substitua pelos publicados pela Receita)

```text
CNPJ alfa de homologação:         12ABC345000ABC
CNPJ numérico de homologação:     12345678000199
```

### 9.2 Fluxo recomendado

Um roteiro que cobre os principais caminhos:

1. Cadastre a empresa alfa via `POST /v3/companies` (homologação).
2. Tente emitir uma NF-e/NFS-e de teste pela V3.
3. Observe a resposta da SEFAZ:
   - `cStat=100` → autorizada (caminho feliz, alfa funcionando).
   - `cStat=215` ou similar → SEFAZ ainda não liberou (esperado em alguns períodos).
4. Confira o webhook V3 chegando com `string`.
5. Por fim, tente a mesma operação via V2 → confirme os `400`/`404` esperados.

---

## 10. Perguntas frequentes

**P1. Vocês vão descontinuar a V2?**
Não por causa dessa mudança. A V2 continua suportada por tempo indeterminado, com o contrato congelado. Se houver depreciação no futuro, ela será comunicada com antecedência pelos canais oficiais.

**P2. Preciso pedir liberação para emitir com CNPJ alfa?**
Não. Não existe whitelist, feature flag nem aprovação manual. Quando você usa a V3 com CNPJ alfa, a requisição vai até a SEFAZ/prefeitura, e a emissão só funciona se a autoridade aceitar.

**P3. O que acontece se eu mandar uma `string` "12345678000199" (numérico, mas como texto) na V3?**
Funciona normalmente. A V3 aceita os dois tipos na entrada — numérico ou alfanumérico, como `string` ou `number`.

**P4. E se eu mandar `number` na V2 com um CNPJ que deveria existir como alfa?**
JSON `number` não consegue representar letras. Se você tentar mandar `"12ABC345000ABC"` em V2 (que espera número), a request falha no parser/validador → `400 Bad Request`.

**P5. Os CNPJs numéricos atuais vão virar alfa em algum momento?**
Não. CNPJs já emitidos pela Receita Federal **continuam numéricos**. Só os novos cadastros (a partir de julho/2026) é que podem vir alfanuméricos.

**P6. Como sei se um CNPJ é alfa ou numérico programaticamente?**
Verifique se há algum caractere fora do conjunto `[0-9]` nas 12 primeiras posições. Se houver, é alfa. Os 2 dígitos finais (DV) são sempre numéricos.

```js
function isAlphanumericCnpj(cnpj) {
  return /[A-Z]/.test(cnpj.substring(0, 12));
}
```

**P7. O DANFE / PDF da nota vai mudar?**
Internamente, sim — o barcode da chave de acesso passa a aceitar caracteres alfanuméricos (CODE-128A). Visualmente, o layout permanece o mesmo. Se você consome o PDF, **nada muda no seu lado**.

**P8. E o cliente que tem rotina de batch processando milhares de notas?**
Comece fazendo um inventário dos pontos onde `federalTaxNumber` é parseado. Migre o tipo e os parsers para `string` e rode tudo em homologação antes de subir para produção. Nossa recomendação é migrar de **forma incremental**: comece pelo fluxo de consulta (o mais barato), depois cadastro e, por último, emissão.

**P9. E os meus dados históricos com CNPJ numérico no Mongo/Postgres?**
Eles permanecem como estão. A NFE.io **não exige reescrita** dos dados antigos. Internamente, lemos o campo de forma polimórfica (`number` ou `string`) e sempre o expomos como `string` na V3.

**P10. Onde abrir dúvidas?**
- Suporte técnico: portal de integradores NFE.io.
- Documentação pública: https://nfe.io/docs.
- Fontes regulatórias oficiais: IN RFB 2.229/2024 e NT Conjunta ENCAT 2025.001 v1.00.

---

> **Resumo técnico em 3 linhas:**
>
> 1. Trate `federalTaxNumber` como `string` em qualquer integração nova.
> 2. Use endpoints `/v3/` para consumir/produzir CNPJ alfa.
> 3. Não invente whitelist nem flag — a SEFAZ é o único gate real.
