# Changelog

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/).

## [Não lançado]

## [6.0.0] - 2026-09-03

> **Major de correção de contrato.** Nada aqui é funcionalidade nova: são bugs provados por
> sonda ao vivo contra a API real (2026-09-01 a 09-03), a maioria em métodos que **nunca
> puderam funcionar**. Em nenhum deles a especificação era a culpada — o SDK é que estava
> errado. Evidência versionada em `tests/fixtures/live-contracts/`.
>
> **É major porque nove pontos da superfície pública mudam de tipo ou de assinatura.** Na
> prática, quase ninguém precisa mexer: as quebras são em superfícies que já estavam
> quebradas — métodos que só lançavam 404, retornos que vinham `undefined`, tipos que
> mentiam sobre o que continham. O roteiro está no
> [`MIGRATION.md`](./MIGRATION.md#v5--v6).
>
> Como esta rodada foi conduzida, porque explica o volume: contrato de API se decide na
> OpenAPI **e** em sonda contra a API real, nunca por inferência. Dois métodos que o
> diagnóstico anterior dava como quebrados **não estavam** — a amostra é que era a exceção.

### Corrigido — identidade do SDK e documentação

- **Toda requisição do SDK mentia sobre quem era.** `src/core/http/client.ts` fixava
  `packageVersion = '3.0.0'` com um `// TODO: Read from package.json`, e o User-Agent saía
  como `@nfe-io/sdk@3.0.0` — nome de pacote que **não existe** (o publicado é `nfe-io`) e
  versão três majors atrás. Medido nos logs de gateway, 30 dias:

  ```
  93.995 requisições | 23 variantes de User-Agent | 5 majors de Node
                     | 1 única versão de SDK reportada
  ```

  As 23 variantes diferem só no Node e na plataforma. O User-Agent é o único sinal de
  adoção que a plataforma tem, e não trazia informação nenhuma sobre a versão. A partir
  desta release dá para medir quem migrou.

  O valor também divergia em quatro lugares: `3.0.0` no User-Agent, `5.1.0` em
  `PACKAGE_VERSION` e em `VERSION`, `5.2.0` no `package.json`. E `PACKAGE_NAME` — constante
  **pública** — dizia `@nfe-io/sdk`.

  Agora há fonte única: `src/version.ts`, gerado do `package.json` por
  `scripts/generate-version.ts` (ligado ao `npm run generate`). Nenhum literal de versão
  sobrou em `src/`, e `tests/unit/version.test.ts` falha se algum voltar — a geração é a
  conveniência, o teste é a garantia.

  Nove exemplos de JSDoc mandavam `import { NfeClient } from '@nfe-io/sdk'`. Corrigidos;
  a skill publicada não precisa mais avisar que o JSDoc mente.

- **A documentação ensinava o wiring de credencial que a API recusa.**
  `docs/multi-host-routing.md` dizia que `productInvoices`, `productInvoicesRtc`,
  `stateTaxes`, `municipalTaxes`, `certificates`, `transportationInvoices` e
  `inboundProductInvoices` usavam a chave **de dados** em `api.nfse.io`. É host **fiscal**:
  responde `403` à chave de dados. Era o mesmo defeito corrigido no roteamento interno em
  `b50bb74`, ainda ensinado como se fosse o certo — quem seguisse a tabela reintroduzia o
  bug na própria aplicação. A tabela também omitia `taxCalculation` e o lado v2 de
  `companies`.

  A nota de fallback deixou de sugerir que as chaves são alternativas: elas são
  **complementares**, e cada uma responde `403` no território da outra.

- **Dois exemplos copiáveis não compilavam.** O README documentava
  `addresses.lookupByTerm()` e `addresses.search()`, removidos na v5. A skill publicada
  chamava `uploadCertificate(companyId, certBuffer, 'password')`, mas a assinatura recebe um
  objeto. Ambos corrigidos, e `tests/unit/docs-drift.test.ts` passa a falhar quando qualquer
  documento cita método que não existe no código — README, `docs/` e a skill.

  A verificação casa **nome de método**, não assinatura: conferir assinatura exigiria
  compilar cada exemplo. Mesmo assim pega os dois casos desta rodada.

  A skill também recebeu as correções de contrato de 01–02/09: rotas não servidas
  (`consumerInvoiceQuery`, `municipalTaxes.getSeries`/`updatePrefecture`), o
  `invoiceId` obrigatório nos downloads de NFS-e, e o envelope real do status de certificado.

- **Um teste existente travava o bug no lugar.** `tests/unit/http-client.test.ts` afirmava
  que o User-Agent continha `@nfe-io/sdk` — quem consertasse o nome quebrava a suíte.
  Corrigido para afirmar o nome real.

### Corrigido — métodos públicos que não alcançavam a API

> Sete métodos públicos foram diagnosticados como quebrados em julho. Reprovando um a um
> com sonda ao vivo, **dois não estavam** — o diagnóstico anterior generalizou a partir de
> uma amostra. A correção do registro está junto das correções de código.

- **`healthCheck()` respondia `error` sempre.** Enviava `pageCount: 1`, e
  `GET /v1/companies?pageCount=1` responde `400 "pageCount must be between 1 and 50"` — o
  limite inferior do servidor está um a mais do que a própria mensagem diz. Agora omite o
  parâmetro (a rota sem query responde `200`), em vez de carregar um número mágico
  contornando defeito alheio. O off-by-one vai para o time de API.

- **`companies.getCertificateStatus()` lia uma forma que a API nunca devolveu.** Esperava
  `{hasCertificate, expiresOn, isValid}`; a resposta é
  `{certificates: [{providerType, resolution, taxPayerId, thumbprint, taxId, subject,
  validUntil, modifiedOn, status}]}`. Nenhum dos três campos existe, então o retorno era
  `{hasCertificate: undefined}` e os derivados nunca eram calculados. Isso derrubava em
  cascata `checkCertificateExpiration()`, `getCompaniesWithCertificates()` e
  `getCompaniesWithExpiringCertificates()` — quatro métodos públicos.

  O resumo mantém `expiresOn` em vez de renomear para `validUntil`: é o mesmo nome que a
  API usa quando o certificado vem embutido na empresa. Os itens crus ficam expostos em
  `certificates`, para quem precisa de `thumbprint` ou `subject`.

  Empresa sem certificado responde `200` com `certificates: []`, não `404`.

- **As duas varreduras de certificado por conta deixaram de fazer N+1.**
  `getCompaniesWithCertificates()` e `getCompaniesWithExpiringCertificates()` chamavam
  `getCertificateStatus()` uma vez por empresa, em série. Enquanto o método estava
  quebrado isso era invisível; consertado, uma conta com 500 empresas faria 500
  requisições sequenciais por chamada.

  A sonda dispensou o pool de concorrência: `GET /v1/companies` **já devolve**
  `certificate` em todo item (`{thumbprint, modifiedOn, expiresOn, status}`). As duas
  passam a ler daí. Medido: as duas varreduras juntas, sobre a conta inteira, em 9,8s.

- **⚠️ BREAKING — `serviceInvoices.downloadPdf()` / `downloadXml()` exigem o `invoiceId`.**
  O parâmetro era opcional e a documentação prometia um ZIP com todas as notas. A rota não
  existe: `/serviceinvoices/pdf` responde `404 "service invoice with id (pdf) was not
  found"`, porque o servidor casa a rota `/{id}` e lê `pdf` como identificador. Não está
  na spec `nf-servico-v1` nem no `nfeio-docs`. Nota de migração em `MIGRATION.md`.

- **O erro da API parava de chegar ao chamador.** `extractErrorMessage` só lia
  `message`/`error`/`detail`/`details`. A plataforma usa quatro envelopes:

  | envelope | onde |
  |---|---|
  | `"pageCount must be between 1 and 50"` | string JSON crua |
  | `{"code":40001,"message":"..."}` | campo `message` |
  | `{"errors":[{"message":"access key is not valid"}]}` | hosts de consulta |
  | `{"title":"...","errors":{"file":["The File field is required."]}}` | ProblemDetails/ModelState |

  Nos dois últimos a mensagem era descartada e o chamador recebia `HTTP 400 error` —
  literalmente o status que ele já tinha. Foi assim que `The File field is required.` ficou
  invisível enquanto o upload de certificado não funcionava.

- **`Accept` dos downloads por chave de acesso.** `productInvoiceQuery.downloadPdf/Xml`
  mandavam só o tipo binário; no caminho de erro o servidor não tem formatter para PDF e
  responde `406` com corpo vazio. Com `Accept: application/pdf, application/json;q=0.9` o
  caminho feliz não muda (mesmo status, mesmo `content-type`, mesmos bytes) e o erro chega
  legível.

  Correção de registro: **esses métodos não estavam quebrados.** O `406` medido em julho
  veio de uma chave de acesso inexistente; com chave real a resposta sempre foi `200`
  com `%PDF-1.4`.

### Deprecado — rotas que a plataforma não serve

Quatro métodos apontam para rotas declaradas na OpenAPI que **não são roteadas** em
produção: `municipalTaxes.getSeries()`, `municipalTaxes.updatePrefecture()`,
`consumerInvoiceQuery.retrieve()` e `consumerInvoiceQuery.downloadXml()`.

A distinção foi feita comparando com um path inventado no mesmo host — `404` de corpo
vazio, sem `content-type`, byte a byte igual — e confirmada de forma independente: rota
servida responde `401` **sem credencial**; estas respondem `404` sem credencial, ou seja, o
middleware de autenticação nem chega a rodar. Noventa dias de log de gateway não têm um
único `200` em `consumerinvoices/coupon`.

Os métodos continuam emitindo a requisição — só o `404` passa a explicar que a rota não é
servida, preservando a classe do erro. Se a rota subir, o `200` passa intacto.

### Corrigido — registro, não código

**`legalPeople` e `naturalPeople` nunca estiveram quebrados.** Os 14 métodos foram
registrados como "400 em toda chamada"; a sonda tinha usado a empresa do `.env`, cujo id
tem 32 caracteres. A rota valida o `company_id` como `ObjectId` de 24 hexadecimais. Sobre
50 empresas da mesma conta: 30 com id de 24 hex respondem `200`, 19 com id de 32
caracteres respondem `400 "company id is not valid"`. Um id de 24 hex sintético responde
`404 "Company not found."` — o validador de formato passa e a busca é que falha.

É limite do servidor: não há conversão possível entre os formatos, e validar localmente só
antecipa a mesma recusa com mensagem pior. Documentado no JSDoc dos dois recursos, com
teste de integração afirmando as duas metades. Pendência aberta com o time de API.

### Manutenção

- **O portão de publicação passou a poder reprovar.** `.github/workflows/publish.yml`
  marcava o passo de testes com `continue-on-error: true`, e um bloco logo abaixo
  justificava por escrito: *"expected for integration tests without API credentials"*.

  A justificativa era falsa. Sem credencial a suíte dá **41 passed | 4 skipped, exit 0** —
  os testes de integração **pulam**, não falham; o guard `shouldRunIntegrationTests()`
  cuida disso desde sempre. Ou seja: o `continue-on-error` protegia contra um modo de
  falha inexistente e, em troca, deixava passar todos os reais. Os três bugs de contrato
  corrigidos nesta mesma versão saíram por esse portão.

  Agora `publish.yml` roda testes, `lint`, `typecheck` e `test:types` antes do build, e
  qualquer um deles reprova a publicação. O `test:types` também entrou no `ci.yml`: eram
  18 assertions — incluindo os guards de alinhamento de contrato — que **nunca executavam**.

- **A suíte de integração voltou a ser executável.** `dotenv` era devDependency e nada
  carregava o `.env`, então `NFE_API_KEY` chegava vazia e a integração pulava sempre,
  inclusive na máquina de quem tinha credencial. Com o `.env` carregado em `tests/setup.ts`,
  a execução local passou de **742 para 779 testes** — 37 que nunca haviam rodado.

  Três assertions de `errors.integration.test.ts` afirmavam `Array.isArray(companies)`
  contra um `ListResponse` (`{ data, page }`), e uma quarta lia `companies.length`
  (`undefined`). Eram de antes da migração para `ListResponse` e nunca falharam porque
  nunca rodaram. Corrigidas.

  O guard não mudou: em CI a integração continua pulando sem `RUN_INTEGRATION_TESTS=true`.
  Credencial de conta compartilhada não vai para runner.

- **`validate:spec` passa a detectar drift entre cópias da mesma seção.** 30 dos 131
  endpoints das specs são declarados em mais de um arquivo (companies, certificates,
  statetaxes, webhooks) e as cópias divergem — algo que o `SOURCES.json` não pegava,
  porque ele compara repo × docs e este drift é *entre* specs do mesmo lado.

  O `SOURCES.json` ganhou `sharedSections`, declarando a fonte canônica de cada grupo,
  e o `validate:spec` agora compara as cópias contra ela **campo a campo**, classificando
  em `type-mismatch` e `enum-mismatch` (falham o build), `enum-subset` (aviso, a cópia
  está atrasada) e presença de campo (informativo). Diferença de prosa ou de forma
  (`$ref` × inline) não conta.

  As 116 divergências existentes entram como baseline declarada, cada uma com motivo e
  referência à pendência upstream — e uma entrada que deixe de reproduzir é reportada
  como obsoleta, para a baseline não virar tapete. O que falha o build é drift **novo**.

  O `discoverSpecs()` do validador também passou a aceitar `.json`: `contribuintes-v2.json`
  — canônica das seções de companies — nunca tinha sido validado.

  Sem efeito em runtime, tipos gerados ou API pública: `dist/index.d.ts` sai byte-idêntico.

### Corrigido

- **Credencial errada em nove recursos fiscais.** As duas chaves da plataforma são
  **complementares, não intercambiáveis** — cada uma responde `403` nos hosts da
  outra família. O cliente HTTP de `api.nfse.io` resolvia a **chave de dados** num
  host **fiscal**, afetando `productInvoices`, `productInvoicesRtc`,
  `transportationInvoices`, `inboundProductInvoices`, `municipalTaxes`,
  `certificates`, `stateTaxes`, `taxCalculation` e o lado v2 de `companies`.

  Esses recursos só funcionavam por acidente: quem configurava **apenas** `apiKey`
  caía no fallback `dataApiKey → apiKey` e nunca via o problema. Quem configurava
  `dataApiKey` — o que a documentação recomenda para consultas — tomava `403`.

  **Como migrar:** se você usa `dataApiKey`, nada a fazer — os nove recursos passam
  a funcionar. Se você configurava **somente** `dataApiKey` e acessava algum deles,
  agora é preciso informar também `apiKey`: o acesso lança `ConfigurationError` na
  hora, em vez de falhar com `403` na chamada.

  O mapa de qual chave vale em qual host está documentado em
  `NfeConfig.apiKey` / `NfeConfig.dataApiKey`.

- **NFC-e: parâmetros da spec não expostos e contrato de download divergente.**

  - `cancel()` aceita `reason` (query definida pela spec) e devolve
    `ConsumerInvoiceCancellationResponse` em vez da nota.
  - `getItems()` / `getEvents()` aceitam paginação cursor (`limit`/`startingAfter`)
    e devolvem envelopes **próprios**, com `hasMore`. O de eventos deixa de reusar
    o tipo do recurso de produto, que tem outra forma.
  - `downloadPdf()` aceita `force`. Os três downloads passam a devolver
    `ConsumerInvoiceFileResource` (`{ uri }`) em vez de `Buffer`: a API devolve
    JSON com URL e **ignora o header `Accept`**. O retorno anterior já era este
    objeto se passando por `Buffer` — nenhum chamador correto quebra.
  - `retrieve()`, `getItems()` e `getEvents()` param de enviar `environment`, que
    a spec não define nessas rotas.

  **Como migrar:** baixe a URL devolvida pelos downloads.

  ```typescript
  const res = await nfe.consumerInvoices.downloadPdf(companyId, invoiceId);
  const bytes = await fetch(res.uri!).then((r) => r.arrayBuffer());
  ```

  Atenção: o envelope da NFC-e usa `uri`; o das rotas de entrada usa
  `publicTemporaryUri`. São tipos distintos de propósito.

  `list()` **continua exigindo** `environment`: a API responde
  `400 environment has to be production or test` sem ele. A spec marca o parâmetro
  como opcional e está errada.

- **Downloads de documentos de entrada (CT-e e NF-e Distribuição) devolviam objeto
  tipado como texto.** As rotas `/inbound/{chave}/xml`, `/pdf` e
  `/inbound/{chave}/events/{evento}/xml` respondem com `{ publicTemporaryUri }` —
  uma URL pré-assinada e temporária. **Binário nunca trafega nessas rotas** e o
  header `Accept` não altera a resposta.

  Os cinco métodos (`inboundProductInvoices.getXml`, `.getPdf`, `.getEventXml`,
  `transportationInvoices.downloadXml`, `.downloadEventXml`) passam a devolver o
  novo tipo `InboundFileResource` em vez de `string`.

  **Como migrar:** baixe a URL devolvida.

  ```typescript
  const res = await nfe.inboundProductInvoices.getPdf(companyId, accessKey);
  const bytes = await fetch(res.publicTemporaryUri!).then((r) => r.arrayBuffer());
  ```

  Nenhum chamador correto quebra: o retorno anterior já era este objeto se passando
  por `string`. O envelope é **diferente** do de NFC-e/NF-e produto, que usa `uri` —
  por isso o tipo é separado de `NfeFileResource`.

- **`companies.uploadCertificate()` nunca funcionou.** O campo multipart era enviado
  como `certificate`; a API faz binding de `file` e respondia
  `400 {"errors":{"file":["The File field is required."]}}` — ou seja, o método não
  tinha como completar. A assinatura pública não mudou.

## [5.2.0] - 2026-07-13

> Correção do contrato de paginação de `companies` contra a API real, provado
> por sonda ao vivo (2026-07-13, duas contas). O request de `GET /companies` é
> **1-based** — a API rejeita `pageIndex: 0` com `"pageIndex must be greater or
> equal to 1"` — igual ao de `serviceInvoices` (rejeição de `pageIndex: 0`
> reconfirmada ao vivo), não o oposto como se supunha.

### Corrigido

- **`companies.listAll()` e `companies.listIterator()` falhavam na primeira
  chamada**, por dois motivos independentes: (1) iniciavam a paginação em
  `pageIndex = 0`, que a API rejeita — agora iniciam em `1`; (2) pediam
  `pageCount: 100`, mas a API limita `GET /companies` a **50 itens por página**
  (sonda ao vivo: aceita 2–50; rejeita 1, apesar da mensagem `"between 1 and
  50"`, e rejeita ≥51) — agora pedem `50`. Consertados transitivamente os
  quatro métodos que dependem de `listAll()`: `findByTaxNumber`, `findByName`,
  `getCompaniesWithCertificates` e `getCompaniesWithExpiringCertificates`.
- `companies.list()` ecoava `pageCount: 100` como default no `page` da resposta;
  o default real da API é **10 itens** quando `pageCount` é omitido.
- ⚠️ **JSDoc de `companies.update()` era ativamente enganoso**: dizia *"only
  fields to update"* (semântica PATCH), mas a API faz **PUT (substituição
  total)** — update parcial seguindo o docblock resulta em 400 ou em campos
  zerados silenciosamente. O JSDoc agora documenta o replace integral com
  exemplo read-modify-write. As assinaturas frouxas (`Partial<Company>`/
  `Omit<Company>`) foram mantidas por compat; o aperto para os schemas
  estritos fica para a próxima major (política de versionamento) ou para a
  migração de CRUD v2.
- JSDoc de `companies.create()` documenta os obrigatórios reais do corpo
  (`name`, `federalTaxNumber`, `taxRegime`, `address`) e que `email` **não**
  faz parte dele; exemplo corrigido (o anterior compilava e falhava com 400).
- Vitest coletava os testes **5×** através dos symlinks `client-php`/
  `client-ruby` (que apontam de volta para este repo), causando flakes por
  corrida no `.test-temp` compartilhado e inflando a suíte (3710 → 698 testes
  reais). O `include` agora é restrito a `tests/**`.

### Deprecado

- **`companies.list()`** (API v1, paginação offset): a API v1 de companies
  está sendo descontinuada. Use `listV2()` (cursor, v2) para listagem
  paginada, ou `listAll()`/`listIterator()` para varredura completa. O método
  continua funcionando (e corrigido — ver acima) durante a convivência.
  **Nota**: `listAll()`/`listIterator()` permanecem no transporte v1 nesta
  release porque a enumeração completa via v2 falha de forma determinística
  em contas com certos registros (HTTP 500 server-side em qualquer janela
  que os contenha — bug reportado ao backend em 2026-07-14), e porque as
  projeções v1/v2 divergem de campos. A troca de transporte fica para quando
  o backend corrigir o 500.
- JSDoc de `companies.list` e `serviceInvoices.list`, `docs/API.md` e `README`
  exemplificavam `pageIndex: 0` — todos corrigidos para a convenção 1-based.
- `PaginationOptions.pageIndex` e `PageInfo.pageIndex` documentados como
  1-based (a primeira página é 1).
- Specs OpenAPI (`nf-servico-v1.yaml`): `pageIndex` agora declara `minimum: 1`
  em `/v1/companies` e na listagem de notas de serviço; `pageCount` de
  `/v1/companies` declara `maximum: 50`.

### Alterado

- ⚠️ **Nota de migração**: `companies.list()` deixou de subtrair 1 da resposta.
  `list({ pageIndex: 1 }).page.pageIndex` agora retorna `1` (antes retornava
  `0`). A convenção do SDK passa a ser **1-based nos dois lados** (request e
  response), fiel ao fio da API. Se seu código lia `page.pageIndex` assumindo
  base 0 (ex.: `pageIndex + 1` para exibir o número da página), remova o ajuste.

### Adicionado

- **`companies.listV2()`** — listagem pela API v2 (`api.nfse.io/v2/companies`,
  contribuintes-v2), **cursor-based**: `{ limit (1–50, default 10),
  startingAfter, endingBefore }` → `{ data, hasMore }` (contrato provado ao
  vivo em 2026-07-14). Os itens seguem a projeção v2 (`CompanyResourceItem`)
  — shape diferente do `Company` v1 (sem campos de configuração NFS-e; com
  `stateTaxes`, `municipalTaxes`, `type`, `version`). `limit` fora de 1–50 é
  rejeitado client-side (a API aceitaria `limit: 0` devolvendo página vazia).
  Tipos novos: `CompanyV2ListOptions`, `CompanyV2ListResponse`.
- Testes de contrato de paginação: mock espelha a rejeição da API a
  `pageIndex: 0` e trava a regressão — `listAll`/`listIterator` são testados
  iniciando em 1 e incrementando 1 → 2; suite equivalente para o contrato
  cursor do `listV2`.
- Teste de alinhamento de tipos para os corpos de escrita de companies
  (`tests/types/company-write-alignment.test-d.ts`): pina os obrigatórios de
  `CreateCompanyResourceItem`/`UpdateCompanyResourceItem` (mesmo conjunto —
  evidência da semântica PUT) e a ausência de `email` no corpo; um sync de
  spec que mude o contrato quebra o `npm run test:types` em vez de driftar.

## [5.1.0] - 2026-07-03

> Correção do contrato de webhooks contra a API real, provado por sonda ao vivo
> (2026-07-02/03, três contas). O contrato correto sempre esteve nos specs oficiais
> (`openapi/spec/nf-servico-v1.yaml` e equivalentes) — o recurso manuscrito havia
> divergido deles.

### Corrigido

- **`createAccountWebhook` funcionava 0% das vezes**: a API exige o request
  envelopado em `{ "webHook": {...} }` (sem ele responde
  `400 "missing required properties including: 'webHook'"`) e devolve a resposta
  também envelopada. O SDK agora envelopa o request (create/update) e desembrulha
  as respostas (create/retrieve/update), com fallback defensivo para corpo cru.
- `listAccountWebhooks`/`retrieveAccountWebhook`/`updateAccountWebhook` agora
  tipados com o shape real do recurso (ver `AccountWebhook` abaixo).

### Adicionado

- Tipo **`AccountWebhook`** com o shape real da API: `uri`, `contentType`,
  `secret` (32–64 caracteres, ecoado no create e omitido nas leituras), `filters`,
  `insecureSsl`, `headers`, `properties`, `status`, `createdOn`, `modifiedOn`.
  Nota: o spec declara `contentType`/`status` como enums inteiros, mas a API
  serializa strings (`"json"`, `"Active"`) — o tipo segue o fio real.
- Tipo **`WebhookEventType`** (união aberta) com os 46 event types reais de
  `GET /v2/webhooks/eventTypes` (`service_invoice.issued_successfully`, etc.).
- Teste de alinhamento (`tests/types/account-webhook-alignment.test-d.ts`)
  amarrando o `AccountWebhook` ao schema gerado do spec oficial — um sync de spec
  que mude o contrato de webhooks quebra o `npm run test:types` em vez de driftar.
- JSDoc do `createAccountWebhook` documenta a verificação de URI na criação
  (a NFE.io faz um ping e exige resposta 2xx).
- JSDoc do `updateAccountWebhook` documenta que o `PUT` é substituição integral
  (confirmado ao vivo em 2026-07-03): campos omitidos voltam ao padrão — update
  sem `status` **desativa o webhook**. Envie o objeto completo (parta do retrieve).

### Deprecado

- Métodos company-scoped de webhooks (`list`, `create`, `retrieve`, `update`,
  `delete`, `test` sobre `/v1/companies/{id}/webhooks`): a rota retorna **404**
  na API atual (confirmado em três contas, 2026-07-02/03). Use os equivalentes
  account-scoped. O comportamento não mudou; remoção fica para a próxima major.
- Tipos `Webhook` e `WebhookEvent`: shapes que a API real rejeita. Use
  `AccountWebhook` e `WebhookEventType`.

## [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

O único requisito novo é **Node.js >= 22** (Node 18 e 20 atingiram fim de vida em abr/2025 e abr/2026). **Não há nenhuma mudança de API** — todo código escrito para a v3 funciona sem alterações. Quem ainda precisa de Node 18/20 deve permanecer no `nfe-io@^3.2`. Veja [MIGRATION.md](MIGRATION.md#v3--v4).

### 🔒 Correções de Segurança

Zeradas todas as vulnerabilidades reportadas pelo Dependabot e pelo Code Scanning (CodeQL). Nenhuma afetava o pacote publicado (zero dependências de runtime) — todas estavam em ferramentas de build/teste e CI. Rastreamento completo em [#31](https://github.com/nfe/client-nodejs/issues/31).

- **vitest** (crítica, GHSA-5xrq-8626-4rwp): leitura/execução arbitrária de arquivos com o servidor do Vitest UI ativo — resolvida pelo upgrade para vitest 4
- **esbuild** (baixa, GHSA-g7r4-m6w7-qqqr): leitura arbitrária de arquivos no dev server em Windows — eliminada da árvore pela migração tsup → tsdown; o esbuild residual (via tsx) já usa a versão corrigida 0.28.1
- **CodeQL** (7 alertas, via [#32](https://github.com/nfe/client-nodejs/pull/32)): permissions mínimas do `GITHUB_TOKEN` no CI, remoção do script morto `scripts/download-openapi.ts` e remoção do código legado v2 (`lib/`)
- **Resultado**: `npm audit` reporta **0 vulnerabilidades** e security tab zerada

### 🔧 Modernização do Toolchain (sem impacto na API)

- **Build**: tsup → **tsdown** (Rolldown/Rust) — mesmo output dual ESM/CJS + declarações de tipos, target `node22`
- **Testes**: vitest 3.2.4 → **4.1.8** (+ `@vitest/ui`, `@vitest/coverage-v8`)
- **CI**: matriz de testes Node 22/24 (antes 18/20/22); actions atualizadas para majors compatíveis com Node 24 (`checkout@v6`, `setup-node@v6`, `upload-artifact@v7`, `github-script@v9`, `codecov@v7`) — runners do GitHub passam a executar actions em Node 24 a partir de 16/06/2026
- **Tipos**: `@types/node` ^20 → ^22
- **vitest.config.ts**: corrigida opção inexistente `testMatch` → `include`; thresholds de coverage no formato achatado do vitest

### 📦 Skill embarcada

- Skill renomeada de `nfeio-sdk` para **`nfeio-node-sdk`** — fixa a convenção cross-SDK (`<org>-<linguagem>-sdk`) antes da publicação no [skills.sh](https://skills.sh)
- Corrigido o path do bloco `agents.skills` no `package.json`, que apontava para diretório inexistente (`./skills/nfe-io-sdk`)
- Requisito documentado na skill atualizado para Node.js 22+

## [3.2.1] - 2026-06-11

### 🔒 Correção crítica de segurança — Validação de assinatura de webhook

`WebhooksResource.validateSignature()` foi reescrita para corresponder ao esquema real usado pela NFE.io em produção. **Todas as versões anteriores rejeitavam silenciosamente qualquer assinatura legítima** — verificado por sondagem ao vivo contra `api.nfse.io`.

**O que estava errado:**
- Documentação e exemplos referenciavam `X-NFE-Signature` — o header correto é **`X-Hub-Signature`**.
- O método computava **HMAC-SHA256** — a NFE.io usa **HMAC-SHA1**.
- O hex era comparado case-sensitive — a NFE.io envia em MAIÚSCULAS.
- O prefixo `sha1=` enviado no header não era removido antes da comparação.
- O `require('crypto')` em runtime falhava (não funciona em ESM nem CJS de módulo), fazendo o método retornar `false` para tudo.

**O que mudou na API:**
- `validateSignature(payload, signature, secret)` agora aceita `payload` como `Buffer | string` (era só `string`) e `signature` como `string | string[] | undefined` (era só `string`).
- A comparação é case-insensitive sobre o hex, com tratamento explícito do prefixo `sha1=`.
- O método **nunca lança exceção** — todo erro/entrada malformada retorna `false`.

**Migração:** use `express.raw()` (ou equivalente) para passar `req.body` como `Buffer` direto. `JSON.stringify(req.body)` NÃO funciona porque a ordem das propriedades e os espaços diferem dos bytes que a NFE.io assinou. Exemplo completo no [docs/API.md](docs/API.md#webhooks) e em `examples/real-world-webhooks.js`.

**Cabeçalhos úteis** que a NFE.io também envia (agora documentados): `X-Hook-Id` (UUID por entrega, ideal para idempotência), `X-Hook-Attempts` (contador de retries), `Content-MD5` (MD5 base64 do body).

## [3.2.0] - 2026-04-25

### 🔒 Correções de Segurança

Atualização de dependências de desenvolvimento para resolver vulnerabilidades reportadas pelo `npm audit`. **Nenhuma alteração no comportamento em runtime** — todas as vulnerabilidades estavam em ferramentas de build/teste, não distribuídas no pacote publicado.

- **Resolvidas 14 vulnerabilidades** (7 high, 7 moderate) em devDependencies:
  - `undici` (alto): GHSA-g9mf-h72j-4rw9, GHSA-2mjp-6q6p-2qxm, GHSA-vrm6-8vpv-qv8q, GHSA-v9p9-hfj2-hcw8, GHSA-4992-7rv2-5pvq (via `openapi-typescript`)
  - `minimatch` (alto): GHSA-3ppc-4f35-3m26, GHSA-7r86-cg39-jmmj, GHSA-23c5-xmqv-rm74 (via `@typescript-eslint`)
  - `esbuild` (moderado): GHSA-67mh-4wv8-2f99 (via `vitest`)
- **Resultado**: `npm audit` agora reporta **0 vulnerabilidades**

### 🔧 Atualizações de Dependências (devDependencies)

- `@typescript-eslint/eslint-plugin`: `^6.21.0` → `^8.59.0`
- `@typescript-eslint/parser`: `^6.21.0` → `^8.59.0`
- `vitest`: `^1.6.1` → `^3.2.4`
- `@vitest/coverage-v8`: `^1.6.1` → `^3.2.4`
- `@vitest/ui`: `^1.6.1` → `^3.2.4`
- `openapi-typescript`: `^6.7.0` → `^7.13.0`

> **Nota**: Vitest foi atualizado para 3.2.4 (não 4.x) para manter compatibilidade com Node 18 — vitest 4 depende do `rolldown`, que requer Node 20+. O esbuild patcheado já está disponível na linha 3.x via Vite.

### 🛠️ Pipeline de Geração de Tipos

Adaptação do script `scripts/generate-types.ts` para a nova API do `openapi-typescript` v7:

- Migração para nova assinatura: `openapiTS()` agora retorna AST e usa `astToString()` para conversão
- Input convertido para `URL` via `pathToFileURL` (exigência do v7)
- Opção `immutableTypes` renomeada para `immutable`; opção `exportType` removida (agora é padrão)
- Adicionada configuração Redocly (`createConfig`) para tolerar specs legados que falhariam na validação estrita do v7

### 🧪 Testes

Ajustes em testes (nenhum teste foi adicionado/removido):

- 30 chamadas em testes de integração migradas da assinatura `it(name, fn, opts)` para a nova `it(name, opts, fn)` (compatível com vitest 3.x e futura migração para 4.x)
- Mock de `FormData` em `tests/unit/companies.test.ts` ajustado para usar `function` ao invés de arrow function (boa prática de mock de construtor)
- **606 testes passando**, 47 skipped (mesma cobertura de antes), validados em Node 18, 20 e 22

### 📝 Spec OpenAPI

- **`openapi/spec/nf-servico-v1.yaml`**: `operationId` do endpoint `GET /v1/companies/{company_id}/serviceinvoices/external/{id}` renomeado de `ServiceInvoices_idGet` para `ServiceInvoices_externalIdGet`. Resolve duplicata real no spec — `openapi-typescript` v6 silenciosamente fundia as duas operações distintas em uma só. Esta mudança é apenas em metadata de geração de código, **não afeta o comportamento da API**.

### ⚠️ Possível Impacto em Tipos Gerados

Usuários que referenciam tipos gerados internos (ex.: `operations["ServiceInvoices_idGet"]` em `src/generated/`) podem precisar de pequenos ajustes:

- Para o endpoint `/serviceinvoices/external/{id}`: usar `operations["ServiceInvoices_externalIdGet"]` (anteriormente fundido com `ServiceInvoices_idGet`)
- A maioria dos consumidores que usa apenas `NfeClient` e seus métodos públicos **não é afetada**

---

## [3.1.0] - 2026-02-22

### 🎉 Expansão Massiva de Recursos - 10 Novos Recursos Implementados

Esta release representa uma expansão significativa do SDK, transformando-o de uma solução focada em NFS-e para uma plataforma completa de gestão de documentos fiscais eletrônicos brasileiros.

### ✨ Novos Recursos

#### 🚚 CT-e - Conhecimento de Transporte Eletrônico (`transportationInvoices`)

- **Consulta via Distribuição DFe**: Acesso a CT-e recebidos automaticamente
- **Habilitação/Desabilitação**: Ativar ou desativar busca automática de CT-e para empresas
- **Configuração de NSU**: Iniciar busca a partir de NSU específico ou data
- **Download de XML**: Baixar XML do CT-e e eventos associados
- **Consulta por Chave**: Buscar CT-e específico por chave de acesso (44 dígitos)
- **Eventos**: Consultar e baixar XML de eventos do CT-e

**Exemplos:** `examples/transportation-invoices.js`

#### 📥 NF-e de Entrada - Distribuição (`inboundProductInvoices`)

- **Consulta de NF-e Recebidas**: Acesso a NF-e via Distribuição DFe
- **Manifestação Automática**: Configurar manifestação automática (Ciência, Confirmação, etc.)
- **Múltiplos Ambientes**: Suporte a Produção e Homologação SEFAZ
- **Download de XML Completo**: Baixar XML completo da NF-e
- **Consulta Detalhada**: Buscar por chave de acesso com informações completas
- **Gestão de Configuração**: Ativar/desativar busca automática por empresa

**Exemplos:** `examples/inbound-product-invoices.js`

#### 📋 Consulta NF-e por Chave (`productInvoiceQuery`)

- **Busca Detalhada**: Consultar NF-e emitida por chave de acesso (44 dígitos)
- **Informações Completas**: Emitente, destinatário, itens, impostos, transporte, pagamento
- **Eventos Associados**: Consultar cancelamento, carta de correção, etc.
- **Validação**: Verificar situação da NF-e na SEFAZ

**Exemplos:** `examples/product-invoice-query.js`

#### 🧾 Consulta CFe-SAT - Cupom Fiscal Eletrônico (`consumerInvoiceQuery`)

- **Consulta por Chave**: Buscar cupom fiscal SAT por chave de acesso
- **Informações de Venda**: Emitente, comprador, itens, pagamento
- **Impostos Detalhados**: ICMS, PIS/PASEP, COFINS, ISSQN por item
- **Validação de Status**: Verificar status do cupom fiscal

**Exemplos:** `examples/consumer-invoice-query.js`

#### 🏢 Consulta CNPJ - Legal Entity Lookup (`legalEntityLookup`)

- **Informações Básicas**: Razão social, nome fantasia, regime tributário, porte, status
- **Inscrições Estaduais por Estado**: Consultar IE específica de qualquer estado
- **IE para Nota Fiscal**: Obter IE válida para emissão de NF-e/NFS-e
- **Dados Cadastrais Completos**: Endereço, telefone, atividades econômicas, sócios
- **Validação de CNPJ**: Verificar se CNPJ está ativo e regular

**Exemplos:** `examples/cnpj-lookup.js`

#### 👤 Consulta CPF - Natural Person Lookup (`naturalPersonLookup`)

- **Validação de CPF**: Verificar situação cadastral na Receita Federal
- **Status Detalhado**: Regular, Pendente de Regularização, Cancelado, Suspenso, etc.
- **Integração com Cadastro**: Validar CPF antes de criar pessoa física

**Exemplos:** `examples/cpf-lookup.js`

#### 📊 NF-e de Produto - Emissão (`productInvoices`)

- **Criação de NF-e**: Emitir Nota Fiscal Eletrônica de Produto
- **Cancelamento**: Cancelar NF-e com justificativa
- **Carta de Correção**: Enviar eventos de correção
- **Download de Documentos**: Baixar PDF e XML da NF-e
- **Consulta de Status**: Verificar situação da NF-e
- **Gestão de Eventos**: Consultar todos os eventos associados

**Exemplos:** `examples/product-invoices.js`

#### 🧮 Cálculo de Impostos (`taxCalculation`)

- **Cálculo Automático**: ICMS, IPI, PIS, COFINS, Imposto de Importação
- **Múltiplos Itens**: Calcular impostos para vários produtos em uma requisição
- **Regimes Tributários**: Suporte a Simples Nacional, Lucro Real, Lucro Presumido
- **Origem e Destino**: Cálculo considerando estados de origem e destino
- **Detalhamento por Item**: Impostos calculados individualmente para cada item

**Exemplos:** `examples/tax-calculation.js`

#### 📖 Códigos Auxiliares de Tributação (`taxCodes`)

- **Lista de CFOP**: Códigos Fiscais de Operações e Prestações
- **Lista de NCM**: Nomenclatura Comum do Mercosul
- **Origens de Mercadoria**: Códigos de origem (0-8)
- **Exportação para CSV**: Exportar listas completas

**API:** Métodos `listCfop()`, `listNcm()`, `listOrigins()`

#### 🗺️ Inscrições Estaduais por Estado (`stateTaxes`)

- **Lista por Estado**: Obter todas as IE de uma empresa em um estado específico
- **Busca por CNPJ e Estado**: Consultar IE específica
- **Validação**: Verificar IE ativas e válidas

**Exemplos:** `examples/state-taxes.js`

### 🔧 Melhorias

#### Configuração Unificada
- **Novo parâmetro `dataApiKey`**: Unifica `addressApiKey` e `cteApiKey` em uma única chave
- **Múltiplos Hosts de API**: Suporte a 4 hosts diferentes (api.nfe.io, api.nfse.io, address.api.nfe.io, nfe.api.nfe.io)
- **Fallback Automático**: `dataApiKey` faz fallback para `apiKey` se não especificado

#### HTTP Client
- **Multi-API Support**: HTTP clients especializados para cada API externa
- **Lazy Loading**: Clientes HTTP criados apenas quando necessários
- **Configuração Flexível**: Base URLs configuráveis por tipo de API

#### TypeScript
- **227+ Novos Tipos**: Tipos completos para todos os 10 novos recursos
- **Enums Abrangentes**: Estados brasileiros, status de documentos, métodos de pagamento, regimes tributários
- **Exportações Públicas**: Todos os tipos disponíveis para consumo externo

### 📝 Documentação

- **README.md**: Adicionadas seções completas para todos os 10 novos recursos (+411 linhas)
- **API.md**: Documentação detalhada de cada método novo (+1,212 linhas)
- **Exemplos Práticos**: 9 novos arquivos de exemplo com casos de uso reais
- **JSDoc Completo**: Documentação inline em todos os métodos públicos

### 🧪 Testes

- **11 Novos Arquivos de Teste**: Cobertura completa dos novos recursos (+3,882 linhas)
- **Testes Unitários**: Validação de parâmetros, tratamento de erros, mocks de API
- **Integração Multi-API**: Testes para diferentes hosts e configurações
- **Validação de Tipos**: Testes de TypeScript para garantir type-safety

### 🐛 Correções

- **CI/CD**: Adicionados triggers para branches `bugfix/*` e `chore/*`
- **.gitignore**: Corrigida entrada do diretório `client-python`
- **.gitignore**: Removidos arquivos de teste obsoletos
- **OpenAPI Specs**: Renomeado `cpf-api.yaml` para `consulta-cpf.yaml` para consistência
- **Generated Files**: Atualizados timestamps de regeneração dos tipos OpenAPI
- **Documentação**: Corrigidos headers de seção para NF-e de Produto e NF-e de Entrada

### ⚠️ Mudanças de Configuração (Deprecação)

#### Parâmetros Deprecados (Ainda Funcionam com Fallback)
- `addressApiKey` → use `dataApiKey`
- `cteApiKey` → use `dataApiKey`

**Nota:** Os parâmetros antigos ainda funcionam, mas é recomendado migrar para `dataApiKey` para unificar a configuração.

#### Exemplo de Migração
```typescript
// ❌ Antes (ainda funciona, mas deprecado)
const nfe = new NfeClient({
  apiKey: 'sua-chave-principal',
  addressApiKey: 'chave-consultas',
  cteApiKey: 'chave-consultas'
});

// ✅ Agora (recomendado)
const nfe = new NfeClient({
  apiKey: 'sua-chave-principal',
  dataApiKey: 'chave-consultas'  // Unificado
});
```

### 📊 Estatísticas da Release

- **Arquivos Modificados**: 58 arquivos
- **Linhas Adicionadas**: +14,176
- **Linhas Removidas**: -102
- **Crescimento de Recursos**: 5 → 15 (+200%)
- **Novos Tipos Exportados**: +227 tipos
- **Commits**: 17 commits
- **Período de Desenvolvimento**: 16/02/2026 - 22/02/2026

### 🚀 Recursos Totais Disponíveis

1. ✅ **Service Invoices** - NFS-e (Notas Fiscais de Serviço)
2. ✅ **Companies** - Gestão de Empresas
3. ✅ **Legal People** - Pessoas Jurídicas (Tomadores/Prestadores)
4. ✅ **Natural People** - Pessoas Físicas
5. ✅ **Webhooks** - Notificações de Eventos
6. ✅ **Addresses** - Consulta de CEP
7. ✅ **Transportation Invoices** - CT-e (Transporte) 🆕
8. ✅ **Inbound Product Invoices** - NF-e de Entrada 🆕
9. ✅ **Product Invoice Query** - Consulta NF-e 🆕
10. ✅ **Consumer Invoice Query** - Consulta CFe-SAT 🆕
11. ✅ **Legal Entity Lookup** - Consulta CNPJ 🆕
12. ✅ **Natural Person Lookup** - Consulta CPF 🆕
13. ✅ **Product Invoices** - NF-e de Produto 🆕
14. ✅ **Tax Calculation** - Cálculo de Impostos 🆕
15. ✅ **Tax Codes** - Códigos Auxiliares 🆕
16. ✅ **State Taxes** - Inscrições Estaduais 🆕

---

## [3.0.2] - 2026-01-19

### 🐛 Correções

- **Build**: Corrigido warning do tsup sobre ordem do campo `types` no package.json exports
- **Errors**: Adicionado getter `statusCode` na classe `NfeError` para total compatibilidade com testes
- **Testes de Integração**: Melhorada lógica de skip para considerar valores de teste como inválidos
- **Testes de Polling**: Corrigidos testes de timeout para evitar unhandled rejections no CI
- **Testes Unitários**: Ajustados testes para usar `.catch()` e prevenir erros assíncronos não tratados
- **CI/CD**: Resolvidos 2 erros de unhandled rejection que causavam falha no GitHub Actions

### 🔧 Melhorias

- **Configuração**: Removido `prepublishOnly` com testes do package.json para evitar falhas por warnings de teste
- **Testes**: Melhorada limpeza de timers falsos no afterEach dos testes de polling
- **Qualidade**: 100% dos testes passando (281 passed, 37 skipped) sem erros assíncronos

---

## [3.0.1] - 2026-01-18

### 🐛 Correções

- **Testes**: Adicionada propriedade `status` como alias de `code` em `NfeError` para compatibilidade
- **Service Invoices**: Corrigida extração de path do location header para preservar prefixo `/v1`
- **Service Invoices**: Corrigido `getStatus` para identificar corretamente status de falha como terminal
- **Testes de Integração**: Agora são pulados gracefully quando `NFE_API_KEY` não está definida
- **Testes Unitários**: Corrigidas múltiplas assertions e timeouts
- **Mensagens de Erro**: Melhoradas mensagens de erro para respostas async sem Location header

### 📝 Documentação

- Melhorada documentação de extração de path do location header

---

## [3.0.0] - 2026-01-18

### 🎉 Lançamento Oficial da Versão 3.0

**Reescrita completa do SDK NFE.io** - SDK TypeScript moderno com zero dependências em runtime e API async/await limpa e intuitiva.

### ✨ Principais Destaques

- 🎯 **TypeScript Nativo** - Segurança de tipos completa com IntelliSense rico
- 🚀 **Zero Dependências em Runtime** - Usa Fetch API nativa do Node.js 18+
- ⚡ **API Moderna Async/Await** - Sem callbacks, código mais limpo e legível
- 🔄 **Retry Automático** - Lógica de retry inteligente com exponential backoff
- 📦 **Suporte Dual ESM/CommonJS** - Funciona com ambos os sistemas de módulos
- 🧪 **Bem Testado** - Mais de 80 testes com 88% de cobertura de código
- 📖 **Documentação Completa** - JSDoc em todas as APIs públicas com exemplos

### 🆕 Adicionado

#### Recursos Principais

- **NfeClient** - Cliente principal com configuração flexível
  - Suporte a ambientes `production` e `development`
  - Configuração de timeout personalizável
  - Retry configurável com exponential backoff
  - Suporte a variáveis de ambiente (`NFE_API_KEY`)
  - Método `updateConfig()` para configuração dinâmica
  - Método `getConfig()` para consultar configuração atual
  - Método `pollUntilComplete()` para polling automático genérico
  - Método estático `isEnvironmentSupported()` para validação

#### Recursos de API Implementados

##### ServiceInvoices (Notas Fiscais de Serviço)
- ✅ `create()` - Criar nota fiscal com suporte a resposta 202 (processamento assíncrono)
- ✅ `createAndWait()` - **NOVO!** Criar e aguardar processamento automaticamente
- ✅ `list()` - Listar notas fiscais com paginação manual
- ✅ `retrieve()` - Buscar nota fiscal específica por ID
- ✅ `cancel()` - Cancelar nota fiscal emitida
- ✅ `sendEmail()` - Enviar nota fiscal por email
- ✅ `downloadPdf()` - Download do PDF da nota fiscal
- ✅ `downloadXml()` - Download do XML da nota fiscal

##### Companies (Empresas)
- ✅ `create()` - Criar nova empresa
- ✅ `list()` - Listar empresas cadastradas
- ✅ `retrieve()` - Buscar empresa específica por ID
- ✅ `update()` - Atualizar dados da empresa
- ✅ `uploadCertificate()` - Upload de certificado digital A1 com suporte a FormData

##### LegalPeople (Pessoas Jurídicas)
- ✅ `create()` - Criar pessoa jurídica
- ✅ `list()` - Listar pessoas jurídicas (scoped por company_id)
- ✅ `retrieve()` - Buscar pessoa jurídica específica
- ✅ `update()` - Atualizar dados da pessoa jurídica
- ✅ `delete()` - Deletar pessoa jurídica
- ✅ `findByTaxNumber()` - **NOVO!** Buscar pessoa jurídica por CNPJ
- ✅ `createBatch()` - **NOVO!** Criar múltiplas pessoas jurídicas em lote

##### NaturalPeople (Pessoas Físicas)
- ✅ `create()` - Criar pessoa física
- ✅ `list()` - Listar pessoas físicas (scoped por company_id)
- ✅ `retrieve()` - Buscar pessoa física específica
- ✅ `update()` - Atualizar dados da pessoa física
- ✅ `delete()` - Deletar pessoa física
- ✅ `findByTaxNumber()` - **NOVO!** Buscar pessoa física por CPF
- ✅ `createBatch()` - **NOVO!** Criar múltiplas pessoas físicas em lote

##### Webhooks
- ✅ `create()` - Criar webhook
- ✅ `list()` - Listar webhooks configurados
- ✅ `retrieve()` - Buscar webhook específico
- ✅ `update()` - Atualizar configuração do webhook
- ✅ `delete()` - Deletar webhook
- ✅ `validateSignature()` - **NOVO!** Validar assinatura de segurança do webhook

#### Sistema de Erros Robusto

Hierarquia completa de erros tipados para melhor tratamento:

- `NfeError` - Classe base de erro com estrutura consistente
- `AuthenticationError` - Erro de autenticação (401)
- `ValidationError` - Erro de validação com detalhes dos campos (400, 422)
- `NotFoundError` - Recurso não encontrado (404)
- `RateLimitError` - Limite de taxa atingido (429) com `retryAfter`
- `ServerError` - Erro no servidor (5xx)
- `ConnectionError` - Erro de conexão de rede
- `TimeoutError` - Timeout na requisição
- `ConfigurationError` - Erro de configuração do cliente
- `PollingTimeoutError` - Timeout no polling de processamento assíncrono
- `ErrorFactory` - Factory inteligente para criar erros apropriados

Todos os erros incluem:
- `message` - Mensagem descritiva
- `statusCode` - Código HTTP
- `requestId` - ID da requisição para suporte
- `details` - Detalhes adicionais
- `fields` - (ValidationError) Campos com erro

#### HTTP Client Avançado

- Fetch API nativa do Node.js 18+
- Retry automático com exponential backoff e jitter
- Suporte a timeout configurável
- Tratamento inteligente de status HTTP (202, 204, 4xx, 5xx)
- Headers customizados por requisição
- Gestão automática de autenticação (Basic Auth)

#### Sistema de Tipos Completo

- Tipos TypeScript para todas as entidades da API
- Tipos de requisição e resposta
- Tipos de configuração
- Tipos de opções de polling
- Tipos de retry config
- Exports públicos bem definidos

#### Testes Abrangentes

- **80+ testes** automatizados
- **88% de cobertura** de código
- Testes unitários para toda lógica de negócio
- Testes de integração com mocks da API
- 32 testes de tratamento de erros
- 55 testes de operações CRUD de recursos
- 13 testes de configuração do cliente
- Factories de mock para todos os tipos de recursos

#### Documentação Completa

- **README.md** - Guia de início rápido atualizado
- **MIGRATION.md** - Guia detalhado de migração v2 → v3 (677 linhas)
- **API.md** - Referência completa da API (1842 linhas)
- **CONTRIBUTING.md** - Guidelines para contribuição
- **CHANGELOG.md** - Histórico de mudanças (este arquivo)
- **RELEASE_NOTES_v3.md** - Release notes completo em português
- JSDoc completo em todas as APIs públicas
- 10+ exemplos práticos em `examples/`

#### Exemplos Práticos

Novos exemplos prontos para uso na pasta `examples/`:

- `basic-usage-esm.js` - Uso básico com ESM
- `basic-usage-cjs.cjs` - Uso básico com CommonJS
- `basic-usage.ts` - Uso básico com TypeScript
- `service-invoice-complete.js` - Fluxo completo de emissão de nota fiscal
- `real-world-invoice.js` - Exemplo real de emissão de nota
- `real-world-list-invoices.js` - Listagem com paginação
- `real-world-manage-people.js` - Gestão de pessoas (legal e natural)
- `real-world-webhooks.js` - Configuração e validação de webhooks
- `all-resources-demo.js` - Demonstração de todos os recursos
- `jsdoc-intellisense-demo.ts` - Demonstração do IntelliSense
- `setup.js` - Script de configuração interativa
- `test-connection.js` - Script de teste de conexão

Scripts NPM para exemplos:
```bash
npm run examples:setup  # Configurar credenciais
npm run examples:test   # Testar conexão
npm run examples        # Executar todos exemplos
```

#### Melhorias de Developer Experience

- **IntelliSense Rico** - Autocompletar completo com documentação inline
- **Type Safety** - Validação de tipos em tempo de desenvolvimento
- **Mensagens de Erro Descritivas** - Erros com contexto completo
- **Validação de Ambiente** - Método `isEnvironmentSupported()`
- **Configuração Flexível** - Múltiplas opções de configuração
- **Exports Organizados** - Exports públicos bem definidos

### 🔄 Mudanças (Breaking Changes)

#### Requisitos do Sistema

- **Node.js:** Aumentado de >= 12.0.0 para >= 18.0.0 (necessário para Fetch API nativo)
- **TypeScript:** Recomendado >= 5.0 para aproveitar tipos completos

#### Inicialização do Cliente

**Antes (v2):**
```javascript
var nfe = require('nfe-io')('api-key');
```

**Agora (v3):**
```javascript
// CommonJS
const { NfeClient } = require('nfe-io');
const nfe = new NfeClient({ apiKey: 'api-key' });

// ESM
import { NfeClient } from 'nfe-io';
const nfe = new NfeClient({ apiKey: 'api-key' });
```

#### API de Callbacks Removida

**Antes (v2):**
```javascript
nfe.serviceInvoices.create('company-id', data, function(err, invoice) {
  if (err) return console.error(err);
  console.log(invoice);
});
```

**Agora (v3 - Async/Await):**
```javascript
try {
  const invoice = await nfe.serviceInvoices.create('company-id', data);
  console.log(invoice);
} catch (error) {
  console.error(error);
}
```

#### Tratamento de Erros

**Antes (v2):**
```javascript
if (err.type === 'AuthenticationError') {
  // tratar erro
}
```

**Agora (v3 - Classes de Erro):**
```javascript
import { AuthenticationError } from 'nfe-io';

if (error instanceof AuthenticationError) {
  // tratar erro
}
```

#### Configuração

**Antes (v2):**
```javascript
var nfe = require('nfe-io')('api-key');
nfe.setTimeout(60000);
```

**Agora (v3):**
```javascript
const nfe = new NfeClient({
  apiKey: 'api-key',
  timeout: 60000,
  environment: 'production',
  retryConfig: {
    maxRetries: 3,
    baseDelay: 1000
  }
});

// Ou atualizar dinamicamente
nfe.updateConfig({ timeout: 90000 });
```

#### Nomes de Métodos

Todos os métodos mantêm a mesma assinatura básica, mas agora retornam Promises:

| Recurso | Método | v2 | v3 | Mudanças |
|---------|--------|----|----|----------|
| ServiceInvoices | `create()` | ✅ | ✅ | Agora async/await |
| ServiceInvoices | `createAndWait()` | ❌ | ✅ | **NOVO!** Polling automático |
| ServiceInvoices | `list()` | ✅ | ✅ | Agora async/await |
| ServiceInvoices | `retrieve()` | ✅ | ✅ | Agora async/await |
| ServiceInvoices | `cancel()` | ✅ | ✅ | Agora async/await |
| ServiceInvoices | `sendEmail()` | ✅ | ✅ | Agora async/await |
| ServiceInvoices | `downloadPdf()` | ✅ | ✅ | Retorna Buffer |
| ServiceInvoices | `downloadXml()` | ✅ | ✅ | Retorna string |
| Companies | `uploadCertificate()` | ✅ | ✅ | Suporte FormData melhorado |
| LegalPeople | `findByTaxNumber()` | ❌ | ✅ | **NOVO!** |
| LegalPeople | `createBatch()` | ❌ | ✅ | **NOVO!** |
| NaturalPeople | `findByTaxNumber()` | ❌ | ✅ | **NOVO!** |
| NaturalPeople | `createBatch()` | ❌ | ✅ | **NOVO!** |
| Webhooks | `validateSignature()` | ❌ | ✅ | **NOVO!** |

### ❌ Removido

#### Dependências

- **when@3.1.0** - Substituído por promises nativas do JavaScript
- **Todas as dependências em runtime** - Agora zero dependencies

#### API Legada

- **Suporte a callbacks** - Removido em favor de async/await
- **API de promises via when.js** - Substituído por promises nativas
- **Suporte ao Node.js < 18** - Requer Node.js 18+ para Fetch API nativo

### 🐛 Corrigido

- Retry logic agora trata corretamente erros 4xx (não retenta)
- Tipos TypeScript completos para todas as respostas da API
- Mensagens de erro mais descritivas com contexto da requisição
- Race conditions no processamento assíncrono de notas fiscais
- Validação de configuração mais robusta
- Tratamento adequado de status HTTP 202 (accepted)
- Tratamento adequado de status HTTP 204 (no content)

### 🔒 Segurança

- Atualizado para TypeScript 5.3+ (última versão estável)
- Zero dependências em runtime = superfície de ataque reduzida
- Nenhuma dependência com vulnerabilidades conhecidas (CVE)
- Validação de entrada via tipos TypeScript
- Suporte a validação de assinatura de webhooks

### 📊 Performance

- ~30% mais rápido que v2 em operações comuns
- Tamanho do bundle reduzido de ~50KB para ~30KB
- Zero overhead de dependências externas
- Fetch API nativo otimizado

### 📚 Migração

Para migrar da v2 para v3, consulte:
- **Guia completo:** [MIGRATION.md](./MIGRATION.md)
- **Release notes:** [RELEASE_NOTES_v3.md](./RELEASE_NOTES_v3.md)

**Checklist rápido:**
1. ✅ Atualizar Node.js para >= 18.0.0
2. ✅ Instalar versão 3: `npm install nfe-io@3`
3. ✅ Atualizar imports/requires
4. ✅ Converter callbacks para async/await
5. ✅ Atualizar tratamento de erros para classes
6. ✅ Testar completamente sua aplicação

---

## [2.0.0] - Versão Legada (Anterior)

SDK JavaScript legado com API baseada em callbacks.

### Recursos da v2

- Companies CRUD
- ServiceInvoices operations
- LegalPeople CRUD
- NaturalPeople CRUD
- Webhooks CRUD
- API dual Promise + callback via biblioteca `when`

### Problemas Conhecidos da v2

- Dependências desatualizadas (`when@3.1.0`)
- API baseada em callbacks (menos intuitiva)
- Sem suporte a TypeScript
- Sem mecanismo de retry integrado
- Polling manual necessário para operações assíncronas
- Sem testes automatizados

---

## Suporte

- 📧 Email: suporte@nfe.io
- 📖 Documentação: https://nfe.io/docs/
- 🐛 Issues: https://github.com/nfe/client-nodejs/issues
- 💬 Discussões: https://github.com/nfe/client-nodejs/discussions

---

## Links

[Unreleased]: https://github.com/nfe/client-nodejs/compare/v5.2.0...HEAD
[5.2.0]: https://github.com/nfe/client-nodejs/compare/v5.1.0...v5.2.0
[5.1.0]: https://github.com/nfe/client-nodejs/compare/v5.0.0...v5.1.0
[5.0.0]: https://github.com/nfe/client-nodejs/releases/tag/v5.0.0
[3.0.0]: https://github.com/nfe/client-nodejs/releases/tag/v3.0.0
[2.0.0]: https://github.com/nfe/client-nodejs/releases/tag/v2.0.0
