Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f1123f3
fix: corrigir bugs de repo (cert validator, updateConfig, README webh…
andrenfe Jun 27, 2026
be64ca9
feat(pipeline): fail-loud com allowlist + descoberta .json + remover …
andrenfe Jun 27, 2026
70e5119
feat(rtc): emissão RTC de NFS-e e NF-e/NFC-e (Reforma Tributária)
andrenfe Jun 27, 2026
b02a86c
feat(types): adotar contribuintes-v2 + repoint aditivo de Company (se…
andrenfe Jun 27, 2026
2b71d46
feat(webhooks): operações de conta (/v2/webhooks) + fetchEventTypes a…
andrenfe Jun 27, 2026
860fbea
feat(municipal-taxes): MunicipalTaxesResource + verbo HTTP PATCH
andrenfe Jun 27, 2026
a293789
feat(nfc-e): ConsumerInvoicesResource (emissão NFC-e + ciclo de vida)
andrenfe Jun 27, 2026
2093679
feat(certificates): CertificatesResource (get/delete por thumbprint)
andrenfe Jun 27, 2026
5774694
feat(resources): companies.exists (HEAD) + notifications + external-i…
andrenfe Jun 27, 2026
9dc2707
fix(resources): corrige address lookup e cancelamento async de NFS-e
andrenfe Jun 29, 2026
64d4c10
chore(openspec): marca tasks concluidas (changes ja rastreadas)
andrenfe Jun 29, 2026
8d8100b
chore(release): v5.0.0
andrenfe Jun 30, 2026
24443b0
chore(openspec): arquiva changes concluidas (move pro archive ignorado)
andrenfe Jun 30, 2026
fb3dfc1
fix(resources): corrige 3 bugs HIGH achados em smoke test ao vivo (v5)
andrenfe Jun 30, 2026
0fd314a
docs(skill,examples): alinha skill publicada e exemplos com a API v5
andrenfe Jul 1, 2026
a392bb9
docs(examples,skill): adiciona cobertura dos recursos novos da v5
andrenfe Jul 1, 2026
7eb0844
test: cobre branches do codigo novo da v5 (destrava threshold de 80%)
andrenfe Jul 1, 2026
93504a5
chore(.gitignore): add dir to ignored files
andrenfe Jul 1, 2026
8103b03
docs: adiciona docs Docusaurus "copia-e-cola" pro nfeio-docs (estilo …
andrenfe Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bower_components/
jspm_packages/
openspec/
nfeio-docs
client-ruby
# ----------------------------------------------------------------------------
# Build Outputs
# ----------------------------------------------------------------------------
Expand Down
81 changes: 81 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,87 @@ Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/),
e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).

## [5.0.0] - 2026-06-30

> Primeira release de **funcionalidades** desde a v3 — a v4 foi apenas o bump de runtime
> (Node 22), sem mudanças de API. A v5 reúne os novos recursos (RTC, NFC-e, inscrições
> municipais, certificados, notificações, webhooks de conta) e correções de contrato
> descobertas testando contra a API ao vivo. Guia: [MIGRATION.md](MIGRATION.md#v4--v5).

### ⚠️ BREAKING CHANGES

Todas as quebras são em superfícies que **já estavam quebradas** (métodos que só lançavam
404, retornos que vinham `undefined`); nenhum código correto deixa de funcionar, mas
usuários TypeScript devem revisar estes pontos no upgrade:

- **`addresses.lookupByPostalCode()`** agora retorna um `Address` (antes retornava o
envelope cru tipado como `{ addresses: Address[] }`). Acesse os campos direto:
`address.street` em vez de `result.addresses[0].street`.
- **`addresses.search()` e `addresses.lookupByTerm()` removidos** — os endpoints não
existem no host real (404). Use `lookupByPostalCode()`. O tipo `AddressSearchOptions`
foi removido e `AddressLookupResponse` agora descreve o envelope `{ address: Address }`.
- **`serviceInvoices.cancel()`** agora retorna `CancelInvoiceResponse` (união discriminada
`{ status: 'async', response } | { status: 'immediate', invoice }`), pois o cancelamento
é assíncrono. Antes retornava um stub tipado como `ServiceInvoiceData`. Use
`cancelAndWait()` para bloquear até concluir.
- **`CertificateValidator.validate()`** não retorna mais `metadata` fabricada (subject/
issuer/validade falsos). O pré-flight local é só de formato; os dados do certificado são
verificados no servidor durante o upload.

### ✨ Adicionado — Companies v2, notificações e avulsos

- **`companies.exists(companyId)`**: checagem de existência via `HEAD /v2/companies/{id}` (api.nfse.io; 404 → `false`). HTTP client ganhou o verbo **`head`**.
- **`nfe.notifications`** (`NotificationsResource`, api.nfe.io): `list`/`retrieve`/`delete`/`sendEmail` de notificações da empresa.
- **`serviceInvoices.retrieveByExternalId(companyId, externalId)`**: busca por id externo (idempotência/reconciliação).
- **`stateTaxes.switchAuthorizer(companyId, stateTaxId, data?)`**: troca de autorizador NF-e (`POST .../switch-authorizer`).

### ✨ Adicionado — Gestão de certificados por thumbprint

- **`nfe.certificates`** (`CertificatesResource`, api.nfse.io): `list`, `getByThumbprint`/`deleteByThumbprint` (v2) e variantes v1 (`getByThumbprintV1`/`deleteByThumbprintV1`). Cobre o gap da consulta/remoção de certificado por thumbprint. Complementa o `companies.uploadCertificate` legado (host api.nfe.io) — recurso dedicado por estar em host diferente (contribuintes-v2 @ api.nfse.io).

### ✨ Adicionado — NFC-e (consumer invoices)

- **`nfe.consumerInvoices`** (`ConsumerInvoicesResource`, api.nfse.io): ciclo de vida da NFC-e company-scoped — `create` (**webhook-driven**, sem polling), `list`, `retrieve`, `cancel`, `getItems`, `getEvents`, `downloadPdf`/`downloadXml`/`downloadRejectionXml`, e `disable` (inutilização). Distinto do `consumerInvoiceQuery` (consulta de cupom, somente leitura). Tipos: `ConsumerInvoiceData`, `ConsumerInvoice`, `ConsumerInvoiceDisablementData`.

### ✨ Adicionado — Inscrições Municipais (municipal taxes)

- **`nfe.municipalTaxes`** (`MunicipalTaxesResource`, api.nfse.io): CRUD de inscrições municipais — pré-requisito para emissão de NFS-e —, espelhando `stateTaxes`. Inclui `updatePrefecture` (HTTP **PATCH** `.../updateprefecture`) e `getSeries` (`.../series/{serie}`). Tipos: `MunicipalTax`, `CreateMunicipalTaxData`, `UpdateMunicipalTaxData`.
- HTTP client ganhou o verbo **`patch`** (mesma cadeia de retry/timeout/erro dos demais).

### ✨ Adicionado — Webhooks de conta

- `WebhooksResource` ganhou operações **de conta** (`/v2/webhooks`, sem `companyId`): `listAccountWebhooks`, `createAccountWebhook`, `retrieveAccountWebhook`, `updateAccountWebhook`, `deleteAccountWebhook`, `pingAccountWebhook`, e `deleteAllAccountWebhooks` (método distinto e marcado como destrutivo). Os métodos company-scoped existentes seguem inalterados.
- **`fetchEventTypes()`**: lista de tipos de evento **ao vivo** (`GET /v2/webhooks/eventTypes`), com retorno em união aberta. `getAvailableEvents()` (lista hardcoded) foi marcado `@deprecated`.

### ✨ Adicionado — Empresas (contribuintes-v2) e tipagem de domínio

- Spec **`contribuintes-v2`** (Empresas, OpenAPI 3.x) adotada na geração — destrava tipos reais de empresa/certificado/endereço.
- **`Company`** enriquecido de forma **aditiva** (minor, sem quebra): mantém os campos atuais e o índice `[key: string]: unknown`, e adiciona os campos documentados do spec (`address`, `taxRegime`, `tradeName`, `stateTaxes`, …) como **opcionais** — melhora o autocomplete sem apertar o tipo nem o input do `create()`.
- Novos tipos exportados (opt-in, estritos): `CompanyResourceItem`, `CompanyResourceV1`, `CreateCompanyResourceItem`, `UpdateCompanyResourceItem`, `CertificateMetadataResource`, `CompanyAddress`.
- Guard de regressão de tipo executável: `npm run test:types` (vitest typecheck) garante que leituras de campo arbitrário continuam compilando e que `federalTaxNumber` segue `number`.

### ✨ Adicionado — Reforma Tributária do Consumo (RTC)

- **`nfe.serviceInvoicesRtc`**: emissão de NFS-e no leiaute RTC (grupo `ibsCbs`), com `create`/`createAndWait` (polling) e `downloadCancellationXml` (XML do evento de cancelamento — Ambiente Nacional). Mesmo endpoint da emissão atual; RTC é selecionado pelo payload. Tipo de request: `NFSeRtcRequest` (schema `NFSeRequest`).
- **`nfe.productInvoicesRtc`**: emissão de NF-e/NFC-e no leiaute RTC (IBS estadual/municipal, CBS, IS), `create` **webhook-driven** (não faz polling), espelhando `productInvoices`. Tipo de request: `ProductInvoiceRtcRequest` (schema `ProductInvoiceRequest`).
- Recursos de emissão existentes (`serviceInvoices`/`productInvoices`) permanecem inalterados; RTC é opt-in. Proveniência das specs registrada em `openapi/spec/SOURCES.json` (NT_2025.002_v1.30).
- Pipeline de geração agora descobre specs `.json` e falha em skip inesperado (allowlist dos 5 Swagger 2.0 legados).

### 🐛 Correções

- **CertificateValidator**: `validate()` deixou de **fabricar metadata** (subject/issuer/validade falsos) e de reportar validade de 1 ano para qualquer arquivo bem-formado. Agora é um pré-flight **só de formato** (buffer, senha presente, magic bytes PKCS#12); subject/issuer/validade e a senha são verificados no servidor durante o upload. `metadata` passa a ser ausente no pré-flight local.
- **`updateConfig()`**: agora invalida **todos** os resources e HTTP clients em cache (via `resetCaches()`). Antes, resetava apenas ~10 — `productInvoices`, `stateTaxes`, `taxCalculation`, `taxCodes`, lookups e os clients de query/legalEntity/naturalPerson permaneciam com a configuração antiga após `updateConfig`.
- **README**: exemplo de webhook corrigido para o header `x-hub-signature` (HMAC-SHA1), alinhado ao fix de verificação de assinatura; antes referenciava `x-nfe-signature`.
- **`addresses.lookupByPostalCode()`**: passou a **desempacotar** o envelope `{ address }` da API e retornar um único `Address`. Antes retornava o envelope cru tipado como `{ addresses: Address[] }` — quem seguia o JSDoc (`result.addresses[0].street`) recebia `undefined`. Verificado contra a API ao vivo (`address.api.nfe.io/v2`).
- **`addresses.search()` e `addresses.lookupByTerm()` removidos**: os endpoints que chamavam (`/v2/addresses` e `/v2/addresses/{term}`) respondem **404** no host real — os métodos só lançavam `NotFoundError`. Como nunca funcionaram, a remoção não quebra nenhum consumidor real. O tipo `AddressSearchOptions` foi removido e `AddressLookupResponse` agora descreve o envelope real (`{ address: Address }`). Se o backend confirmar um endpoint de busca, ele volta como change separada com contrato verificado.
- **`serviceInvoices.cancel()`**: o cancelamento de NFS-e é **assíncrono** (HTTP `202` + `Location`). Antes, `cancel()` retornava o stub de polling `{ code, status, location }` **tipado como `ServiceInvoiceData`** (então `cancelled.id`/`flowStatus` vinham `undefined`). Agora retorna uma **união discriminada** `CancelInvoiceResponse` (`{ status: 'async', response }` ou `{ status: 'immediate', invoice }`), espelhando `create()`. Novo método **`cancelAndWait()`** faz polling até o cancelamento concluir (espelha `createAndWait`). Verificado contra a API ao vivo. Tipos exportados: `CreateInvoiceResponse`, `CancelInvoiceResponse`.

#### Correções de recursos novos (descobertas em smoke test ao vivo de toda a SDK)

- **`taxCodes.*`**: passou a usar a **chave principal** (`apiKey`) contra `api.nfse.io`. Antes estava ligado ao cliente CT-e (chave de dados), o que retornava **403** em setups com `dataApiKey` separada. Os 4 endpoints (`/tax-codes/*`) agora respondem 200.
- **`consumerInvoices.list()`**: agora exige `environment` (`Production`/`Test`), obrigatório pela API — antes a chamada saía sem o parâmetro e retornava **400**. Também passou a usar a **chave principal** (antes 403 com chave de dados separada). Os métodos de leitura (`retrieve`/`getItems`/`getEvents`/downloads) aceitam `environment` opcional.
- **`webhooks` de conta**: os métodos de conta (`listAccountWebhooks`, `createAccountWebhook`, `fetchEventTypes`, etc.) montavam o caminho `/v1/v2/webhooks` (duplo prefixo de versão) → **404**. Agora usam um cliente dedicado em `api.nfe.io/v2`. Além disso, `listAccountWebhooks` desempacota o envelope `{ webHooks }` para `{ data }` e `fetchEventTypes` extrai os ids de `{ eventTypes }`.

## [4.0.0] - 2026-06-12

### ⚠️ BREAKING CHANGE — Node.js >= 22
Expand Down
82 changes: 82 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,87 @@
# Guia de Migração

## v4 → v5

A v5 é a primeira release de **funcionalidades** desde a v3 (a v4 foi apenas o bump para
Node 22). Ela traz muitos recursos novos — todos **aditivos** — e um conjunto pequeno de
correções de contrato que são tecnicamente _breaking_.

```bash
npm install nfe-io@^5
```

> **Na prática, a maioria dos projetos não precisa mudar nada.** As quebras abaixo são em
> superfícies que já estavam quebradas (métodos que só lançavam 404, retornos que vinham
> `undefined`). Se você usa essas APIs, ajuste conforme a seguir.

### 1. `addresses.lookupByPostalCode()` retorna um `Address`

Antes devolvia um envelope tipado como `{ addresses: Address[] }` (mas a API real retorna
um único endereço, então `result.addresses` vinha `undefined`). Agora retorna o `Address`
direto.

```ts
// Antes (v4) — não funcionava como documentado
const result = await nfe.addresses.lookupByPostalCode('01310-100');
const street = result.addresses[0].street; // undefined

// Agora (v5)
const address = await nfe.addresses.lookupByPostalCode('01310-100');
const street = address.street; // 'Paulista'
```

### 2. `addresses.search()` e `addresses.lookupByTerm()` foram removidos

Os endpoints (`/v2/addresses` e `/v2/addresses/{term}`) respondem **404** no host real —
os métodos só lançavam `NotFoundError`. Use `lookupByPostalCode()`. O tipo
`AddressSearchOptions` foi removido e `AddressLookupResponse` agora descreve o envelope
real (`{ address: Address }`).

> Busca de endereço por texto livre só volta se/quando o backend expuser um endpoint real
> — aí como adição não-breaking.

### 3. `serviceInvoices.cancel()` retorna uma união discriminada

O cancelamento de NFS-e é **assíncrono** (HTTP 202 + `Location`). Antes, `cancel()`
retornava um stub de polling tipado como `ServiceInvoiceData` (então `cancelled.id` /
`cancelled.flowStatus` vinham `undefined`). Agora retorna `CancelInvoiceResponse`.

```ts
// Antes (v4)
const cancelled = await nfe.serviceInvoices.cancel(companyId, invoiceId);
console.log(cancelled.flowStatus); // undefined

// Agora (v5) — união discriminada (espelha create())
const result = await nfe.serviceInvoices.cancel(companyId, invoiceId);
if (result.status === 'async') {
console.log(result.response.invoiceId);
}

// ou bloqueie até concluir:
const invoice = await nfe.serviceInvoices.cancelAndWait(companyId, invoiceId);
console.log(invoice.flowStatus); // 'Cancelled'
```

### 4. `CertificateValidator.validate()` não fabrica mais `metadata`

O pré-flight local valida apenas o **formato** (buffer, senha presente, magic bytes
PKCS#12). Não retorna mais `subject`/`issuer`/`validade` inventados — esses dados são
verificados no servidor durante o upload do certificado. Se você lia `result.metadata`,
ele agora é ausente no pré-flight.

### Novidades (aditivas, sem ação necessária)

- Emissão RTC (Reforma Tributária): `nfe.serviceInvoicesRtc`, `nfe.productInvoicesRtc`
- NFC-e: `nfe.consumerInvoices`
- Inscrições municipais: `nfe.municipalTaxes`
- Certificados por thumbprint: `nfe.certificates`
- Notificações: `nfe.notifications`
- Webhooks de conta + `fetchEventTypes()`
- `companies.exists()`, `serviceInvoices.retrieveByExternalId()`, `serviceInvoices.cancelAndWait()`, `stateTaxes.switchAuthorizer()`
- `Company` enriquecido com campos do spec (opcionais)

---

## v3 → v4

A v4.0.0 **não contém mudanças de API** — todo código escrito para a v3 funciona sem alterações.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# NFE.io SDK para Node.js (v4)
# NFE.io SDK para Node.js (v5)

[![npm version](https://img.shields.io/npm/v/nfe-io.svg)](https://www.npmjs.com/package/nfe-io)
[![Node.js Version](https://img.shields.io/node/v/nfe-io.svg)](https://nodejs.org)
Expand All @@ -8,7 +8,7 @@

**SDK Oficial NFE.io para Node.js 22+** - SDK TypeScript moderno para emissão de notas fiscais de serviço eletrônicas (NFS-e).

> ✨ **Versão 4** - TypeScript nativo, zero dependências em runtime e API moderna async/await.
> ✨ **Versão 5** - TypeScript nativo, zero dependências em runtime e API moderna async/await. Inclui emissão RTC (Reforma Tributária), NFC-e, inscrições municipais, certificados, notificações e webhooks de conta. Veja a [migração v4 → v5](MIGRATION.md#v4--v5).

## 📋 Índice

Expand Down Expand Up @@ -971,9 +971,9 @@ const webhook = await nfe.webhooks.create(empresaId, {
active: true
});

// No seu endpoint de webhook
// No seu endpoint de webhook (capture o corpo cru: app.use(express.raw({ type: '*/*' })))
app.post('/api/webhooks/nfe', (req, res) => {
const assinatura = req.headers['x-nfe-signature'];
const assinatura = req.headers['x-hub-signature']; // header correto (HMAC-SHA1), não 'x-nfe-signature'
const ehValido = nfe.webhooks.validateSignature(
req.body,
assinatura,
Expand Down
75 changes: 75 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: Biblioteca NFE.io em Node.js para Emissão de Notas Fiscais (NFS-e, NF-e, NFC-e, CT-e)
description: SDK Node.js oficial da NFE.io — TypeScript nativo, Node 22+, zero dependências de runtime, ESM + CommonJS e respostas discriminadas.
sidebar_label: Biblioteca Node.js
slug: /desenvolvedores/bibliotecas/node
provider: NFE.io
badge: SDK
layout_type: IntegrationLayout
heroImage: /docs/img/bibliotecas/nodejs.svg
ctaLabel: GitHub NFE.io Node.js
ctaUrl: https://github.com/nfe/client-nodejs
---

# Biblioteca Node.js NFE.io

SDK oficial da [NFE.io](https://nfe.io) para Node.js: emissão e gestão de
documentos fiscais eletrônicos brasileiros (NFS-e, NF-e, NFC-e, CT-e) com
ergonomia moderna e **zero dependências de runtime**.

- **Cliente único** `NfeClient` com acessores `camelCase` por recurso, inicializados sob demanda (lazy).
- **TypeScript nativo** — tipos gerados das specs OpenAPI oficiais; autocompletar e checagem no editor.
- **Respostas discriminadas** para emissão assíncrona (`{ status: 'immediate' | 'async' }`) e utilitários de polling.
- **ESM + CommonJS** no mesmo pacote; alvo **Node.js 22+**.

## Requisitos

- Node.js **22** ou superior.
- **Zero dependências de runtime** — apenas APIs nativas do Node (fetch, AbortController, Buffer).

## Instalação

```sh
npm install nfe-io
# ou: pnpm add nfe-io / yarn add nfe-io
```

## Primeiros passos

```typescript
import { NfeClient } from 'nfe-io';

const nfe = new NfeClient({ apiKey: process.env.NFE_API_KEY });

const result = await nfe.serviceInvoices.create('55df4dc6b6cd9007e4f13ee8', {
cityServiceCode: '2690',
description: 'Manutenção e suporte técnico',
servicesAmount: 100.0,
borrower: { federalTaxNumber: 191, name: 'Banco do Brasil SA' },
});
```

O fluxo completo (retorno discriminado + polling) está em
[Primeiros passos](./getting-started.md).

## Guias

- [Primeiros passos](./getting-started.md) — instalação, cliente e primeira NFS-e.
- [Configuração](./configuration.md) — chaves de API, ambiente, timeout e retry.
- [Emissão assíncrona e polling](./async-and-polling.md) — retorno discriminado e `createAndWait`.
- [Roteamento multi-host / multi-chave](./multi-host-routing.md) — quais recursos usam qual host e chave.
- [Paginação](./pagination.md) — listas page-style e cursor.
- [Downloads (PDF/XML)](./downloads.md) — bytes binários e ZIPs.
- [Webhooks](./webhooks.md) — assinatura, eventos e webhooks de conta.
- [Erros](./errors.md) — hierarquia de erros e type guards.
- [Emissão RTC (Reforma Tributária)](./rtc-emission.md) — leiautes IBS/CBS/IS.

## Recursos (cookbook)

Um guia prático por recurso em [Recursos](./recursos/). Destaques:

- [Notas de serviço (NFS-e)](./recursos/service-invoices.md)
- [Notas de produto (NF-e)](./recursos/product-invoices.md)
- [NFC-e (consumidor)](./recursos/consumer-invoices.md)
- [Empresas](./recursos/companies.md) · [Inscrições municipais](./recursos/municipal-taxes.md) · [Inscrições estaduais](./recursos/state-taxes.md)
- [Consulta CNPJ](./recursos/legal-entity-lookup.md) · [Consulta CPF](./recursos/natural-person-lookup.md) · [Consulta CEP](./recursos/addresses.md)
5 changes: 5 additions & 0 deletions docs/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "Biblioteca Node.js",
"position": 3,
"collapsed": true
}
Loading
Loading