@useinvio/nfse-sdk 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/README.en.md +146 -0
  3. package/README.md +76 -576
  4. package/dist/agent.d.ts +46 -1
  5. package/dist/agent.js +320 -4
  6. package/dist/agent.js.map +1 -1
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +520 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/cliBin.d.ts +2 -0
  11. package/dist/cliBin.js +6 -0
  12. package/dist/cliBin.js.map +1 -0
  13. package/dist/cliExample.d.ts +6 -0
  14. package/dist/cliExample.js +29 -0
  15. package/dist/cliExample.js.map +1 -0
  16. package/dist/client.d.ts +47 -2
  17. package/dist/client.js +36 -6
  18. package/dist/client.js.map +1 -1
  19. package/dist/config.d.ts +3 -0
  20. package/dist/config.js +8 -0
  21. package/dist/config.js.map +1 -1
  22. package/dist/consultas.d.ts +34 -0
  23. package/dist/consultas.js +61 -0
  24. package/dist/consultas.js.map +1 -0
  25. package/dist/dpsJson.js +1 -41
  26. package/dist/dpsJson.js.map +1 -1
  27. package/dist/emissaoNota.d.ts +8 -3
  28. package/dist/emissaoNota.js +6 -3
  29. package/dist/emissaoNota.js.map +1 -1
  30. package/dist/eventoJson.d.ts +76 -0
  31. package/dist/eventoJson.js +128 -0
  32. package/dist/eventoJson.js.map +1 -0
  33. package/dist/eventosNfse.d.ts +38 -0
  34. package/dist/eventosNfse.js +73 -0
  35. package/dist/eventosNfse.js.map +1 -0
  36. package/dist/gzipB64.d.ts +5 -0
  37. package/dist/gzipB64.js +12 -0
  38. package/dist/gzipB64.js.map +1 -1
  39. package/dist/index.d.ts +17 -7
  40. package/dist/index.js +9 -4
  41. package/dist/index.js.map +1 -1
  42. package/dist/mcpCli.d.ts +2 -0
  43. package/dist/mcpCli.js +7 -0
  44. package/dist/mcpCli.js.map +1 -0
  45. package/dist/mcpServer.d.ts +49 -0
  46. package/dist/mcpServer.js +159 -0
  47. package/dist/mcpServer.js.map +1 -0
  48. package/dist/nfseXml.d.ts +48 -0
  49. package/dist/nfseXml.js +86 -0
  50. package/dist/nfseXml.js.map +1 -0
  51. package/dist/rejeicoes.d.ts +50 -0
  52. package/dist/rejeicoes.js +62 -0
  53. package/dist/rejeicoes.js.map +1 -0
  54. package/dist/rejeicoesCatalog.d.ts +10 -0
  55. package/dist/rejeicoesCatalog.js +4449 -0
  56. package/dist/rejeicoesCatalog.js.map +1 -0
  57. package/dist/sefinClient.d.ts +100 -6
  58. package/dist/sefinClient.js +211 -19
  59. package/dist/sefinClient.js.map +1 -1
  60. package/dist/signXml.js +9 -1
  61. package/dist/signXml.js.map +1 -1
  62. package/dist/testing.d.ts +80 -0
  63. package/dist/testing.js +285 -0
  64. package/dist/testing.js.map +1 -0
  65. package/dist/xmlBuild.d.ts +11 -0
  66. package/dist/xmlBuild.js +44 -0
  67. package/dist/xmlBuild.js.map +1 -0
  68. package/dist/xmlInspection.d.ts +1 -1
  69. package/dist/xmlInspection.js.map +1 -1
  70. package/dist/xsdValidation.d.ts +7 -0
  71. package/dist/xsdValidation.js +37 -15
  72. package/dist/xsdValidation.js.map +1 -1
  73. package/docs/README.md +47 -0
  74. package/docs/REGRAS_NEGOCIO_EMISSAO.md +8 -8
  75. package/docs/conceitos/como-funciona.md +87 -0
  76. package/docs/guias/adn.md +51 -0
  77. package/docs/guias/agentes-ia.md +111 -0
  78. package/docs/guias/cancelar.md +70 -0
  79. package/docs/guias/certificado.md +88 -0
  80. package/docs/guias/cli.md +68 -0
  81. package/docs/guias/consultar.md +54 -0
  82. package/docs/guias/emitir.md +172 -0
  83. package/docs/guias/recuperar-emissao.md +33 -0
  84. package/docs/guias/rejeicoes.md +70 -0
  85. package/docs/guias/testes.md +79 -0
  86. package/docs/guias/timeouts.md +33 -0
  87. package/docs/referencia/ambientes.md +35 -0
  88. package/docs/referencia/api.md +137 -0
  89. package/docs/referencia/erros.md +55 -0
  90. package/docs/tutoriais/primeira-nfse.md +130 -0
  91. package/examples/README.md +2 -0
  92. package/examples/cancel-nfse.ts +29 -0
  93. package/examples/recover-emission.ts +29 -0
  94. package/llms.txt +97 -92
  95. package/package.json +23 -3
package/README.md CHANGED
@@ -1,52 +1,35 @@
1
1
  # @useinvio/nfse-sdk
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@useinvio/nfse-sdk.svg)](https://www.npmjs.com/package/@useinvio/nfse-sdk)
4
- [![npm access](https://img.shields.io/badge/npm-public-brightgreen.svg)](https://www.npmjs.com/package/@useinvio/nfse-sdk)
4
+ [![CI](https://github.com/useinvio/nfse-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/useinvio/nfse-sdk/actions/workflows/ci.yml)
5
5
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-339933.svg)](https://nodejs.org/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
7
 
7
- [Website](https://sdk.useinvio.com/)
8
+ [Website](https://sdk.useinvio.com/) · [Playground](https://useinvio.github.io/nfse-sdk/) · [Documentação](./docs/README.md) · [English](./README.en.md)
8
9
 
9
- [English version](./ENGLISH.md)
10
+ <img width="959" height="296" alt="NFS-e SDK" src="https://github.com/user-attachments/assets/d6c45737-e7ca-4ea7-a06f-d93b7c16e4f0" />
10
11
 
11
- <img width="959" height="296" alt="image" src="https://github.com/user-attachments/assets/d6c45737-e7ca-4ea7-a06f-d93b7c16e4f0" />
12
+ **Emita, consulte e cancele NFS-e Nacional em TypeScript, sem escrever XML fiscal na mão.**
12
13
 
13
- ---
14
+ Você descreve a nota em JSON; o SDK monta a DPS, valida contra o XSD oficial,
15
+ assina com o certificado A1, compacta, envia por mTLS para a SEFIN e devolve a
16
+ resposta oficial em formato estruturado. Sem binários nativos e sem `xmllint`.
14
17
 
15
- SDK TypeScript para integração com a **NFS-e Nacional** (SEFIN).
18
+ ## O que ele faz
16
19
 
17
- Transforme JSON em DPS/NFS-e, assine com XMLDSIG, compacte em GZip/Base64 e transmita por mTLS sem escrever XML fiscal na mão.
18
-
19
- O SDK cuida da camada protocolar: certificado A1, montagem de DPS, validações estruturais, assinatura XML, compactação, envio, consulta e normalização de rejeições da SEFIN. A aplicação [...]
20
-
21
- **Para quem é:** ERPs, SaaS financeiros, plataformas de cobrança, contabilidades digitais e times Node.js/TypeScript que precisam integrar com a NFS-e Nacional.
22
-
23
- **O que ele não é:** um motor fiscal, consultor tributário ou substituto da validação contábil do seu produto.
24
-
25
- ---
26
-
27
- ## Índice
28
-
29
- - [Instalação](#instalação)
30
- - [Quick start](#quick-start)
31
- - [Por que usar](#por-que-usar)
32
- - [Escolhendo o ponto de entrada](#escolhendo-o-ponto-de-entrada)
33
- - [Cliente orientado a recursos](#cliente-orientado-a-recursos)
34
- - [Certificado A1](#certificado-a1)
35
- - [Emitir a partir de XML pronto](#emitir-a-partir-de-xml-pronto)
36
- - [Emitir a partir de JSON declarativo](#emitir-a-partir-de-json-declarativo)
37
- - [Inspecionar o XML antes de enviar](#inspecionar-o-xml-antes-de-enviar)
38
- - [Tratar rejeições da SEFIN](#tratar-rejeições-da-sefin)
39
- - [Consultar uma NFS-e emitida](#consultar-uma-nfs-e-emitida)
40
- - [Enviar evento](#enviar-evento-ex-cancelamento)
41
- - [Série e número da DPS](#série-e-número-da-dps)
42
- - [Ambientes](#ambientes)
43
- - [Agentes de IA e MCP](#agentes-de-ia-e-mcp)
44
- - [Referência da API](#referência-da-api)
45
- - [Desenvolvimento](#desenvolvimento)
46
- - [Publicação no npm](#publicação-no-npm)
47
- - [Exemplos](#exemplos)
48
-
49
- ---
20
+ | Operação | Função | No `NfseClient` |
21
+ |---|---|---|
22
+ | Emitir a partir de JSON ou XML | `emitirNfse` | `invoices.create` |
23
+ | Validar sem enviar (JSON + XSD) | `validateDpsJsonRequest`, `buildDpsFromJson` | `invoices.buildDpsJson` |
24
+ | Consultar com resultado tipado | `obterNfse` | `invoices.retrieve` |
25
+ | Recuperar uma emissão após timeout | `recuperarNfsePorDps` | `invoices.recover` |
26
+ | Cancelar / cancelar por substituição | `cancelarNfse`, `cancelarNfsePorSubstituicao` | `invoices.cancel`, `invoices.cancelBySubstitution` |
27
+ | Baixar o DANFSe em PDF | `baixarDanfse` | `invoices.danfse` |
28
+ | Distribuição por NSU e parâmetros municipais (ADN) | `distribuirDfePorNsu`, `consultarAliquota`… | `distribution`, `municipalParameters` |
29
+ | Explicar rejeições com o texto oficial | `explicarRejeicao`, `explicarErros`, [uma página por código](./docs/rejeicoes/README.md) | – |
30
+ | Usar pelo terminal | `npx nfse init`, `doctor`, `validate`, `emit`, `explain` | – |
31
+ | Testar sem certificado nem rede | `@useinvio/nfse-sdk/testing` | – |
32
+ | Servidor MCP e ferramentas para agentes de IA | `npx -y @useinvio/nfse-mcp`, `@useinvio/nfse-sdk/agent` | – |
50
33
 
51
34
  ## Instalação
52
35
 
@@ -54,22 +37,23 @@ O SDK cuida da camada protocolar: certificado A1, montagem de DPS, validações
54
37
  npm install @useinvio/nfse-sdk
55
38
  ```
56
39
 
57
- > Node.js ≥ 20 é obrigatório. Não precisa de `xmllint` nem de binários nativos: a validação XSD roda em WebAssembly.
40
+ Node.js 20 ou mais recente. Nada para instalar no sistema: a validação XSD roda
41
+ em WebAssembly.
58
42
 
59
- ---
43
+ ## Em 60 segundos
60
44
 
61
- ## Quick start
62
-
63
- O caminho mais comum: emitir uma nota a partir de JSON declarativo.
45
+ ```bash
46
+ NFSE_CERT_PATH=./certificado.pfx
47
+ NFSE_CERT_PASSWORD=sua-senha
48
+ ```
64
49
 
65
50
  ```ts
66
51
  import { emitirNfse, EmitirNotaError, NfseCertificate } from '@useinvio/nfse-sdk';
67
52
 
68
- // NFSE_CERT_PATH=./certificado.pfx e NFSE_CERT_PASSWORD=... (veja Certificado A1)
69
- const certificado = NfseCertificate.fromEnv();
53
+ const certificado = NfseCertificate.fromEnv(); // a chave privada nunca aparece em logs
70
54
 
71
55
  const nota = {
72
- ambiente: 'restrita' as const,
56
+ ambiente: 'restrita' as const, // produção restrita: o sandbox oficial
73
57
  prestador: {
74
58
  cnpj: '12345678000195',
75
59
  cLocEmi: '4106902',
@@ -88,563 +72,79 @@ const nota = {
88
72
  dhEmi: '2026-06-15T10:30:00-03:00',
89
73
  dCompet: '2026-06-01',
90
74
  valores: { vServ: '1500.00' },
91
- tributacaoMunicipal: {
92
- tribISSQN: '1',
93
- tpRetISSQN: '1',
94
- },
95
- tributacaoFederal: {
96
- piscofins: { CST: '07' },
97
- },
98
- totTrib: {
99
- pTotTribFed: '0.00',
100
- pTotTribEst: '0.00',
101
- pTotTribMun: '5.00',
102
- },
103
- },
104
- };
105
-
106
- try {
107
- const resultado = await emitirNfse(nota, certificado);
108
- console.log(resultado.chaveAcesso); // chave de 50 dígitos
109
- console.log(resultado.nfseXml); // XML autorizado pela SEFIN
110
- } catch (error) {
111
- if (error instanceof EmitirNotaError) {
112
- for (const rejeicao of error.erros) {
113
- console.error(rejeicao.Codigo, rejeicao.Descricao);
114
- }
115
- }
116
- }
117
- ```
118
-
119
- ---
120
-
121
- ## Por que usar
122
-
123
- - Entrada JSON tipada para construir DPS no layout nacional.
124
- - Assinatura XMLDSIG, GZip/Base64 e mTLS encapsulados em uma API Node.js.
125
- - Erros oficiais da SEFIN preservados em formato estruturado.
126
- - Builder separado para inspecionar ou salvar o XML antes de transmitir.
127
- - Cliente `NfseClient` para configurar certificado, ambiente e defaults uma vez.
128
- - Validação explícita de campos fiscais obrigatórios, sem defaults silenciosos.
129
-
130
- ---
131
-
132
- ## Escolhendo o ponto de entrada
133
-
134
- | Situação | O que usar |
135
- |---|---|
136
- | Quer uma API de cliente com `client.invoices.create(...)` | `new NfseClient(...)` |
137
- | Outro sistema já gera o XML da DPS | `emitirNfse(xmlString, certificado)` |
138
- | Você monta os dados e quer que a SDK construa o XML | `emitirNfse(notaJson, certificado)` |
139
- | Precisa inspecionar ou salvar o XML antes de enviar | `buildDpsFromJson(nota)` |
140
- | Fluxo customizado (assinar, compactar ou enviar separadamente) | funções de baixo nível |
141
-
142
- ---
143
-
144
- ## Cliente orientado a recursos
145
-
146
- Use `NfseClient` quando preferir configurar ambiente e certificado uma vez e
147
- emitir notas por recursos, como `client.invoices.create(...)`.
148
-
149
- O cliente usa os mesmos campos do JSON declarativo (`prestador`, `servico` e
150
- `emissao`). Os `defaults` seguem o mesmo formato e podem ser sobrescritos em
151
- cada chamada.
152
-
153
- ```ts
154
- import { NfseCertificate, NfseClient } from '@useinvio/nfse-sdk';
155
-
156
- const client = new NfseClient({
157
- environment: 'sandbox',
158
- certificate: NfseCertificate.fromEnv(),
159
- defaults: {
160
- prestador: {
161
- cnpj: '12345678000195',
162
- cLocEmi: '4106902',
163
- serie: '1601',
164
- opSimpNac: '1',
165
- regEspTrib: '0',
166
- },
167
- servico: {
168
- cTribNac: '010201',
169
- xDescServ: 'Servico ficticio de desenvolvimento de software',
170
- cLocPrestacao: '4106902',
171
- },
172
- },
173
- });
174
-
175
- const invoice = await client.invoices.create({
176
- emissao: {
177
- nDPS: '1',
178
- valores: { vServ: '1000.00' },
179
- tomador: {
180
- CPF: '00000000000',
181
- xNome: 'Cliente Exemplo',
182
- end: {
183
- endNac: { cMun: '4106902', CEP: '80010000' },
184
- xLgr: 'Rua Exemplo', nro: '100', xBairro: 'Centro',
185
- },
186
- },
187
- tributacaoMunicipal: {
188
- tribISSQN: '1',
189
- tpRetISSQN: '1',
190
- },
191
- tributacaoFederal: {
192
- piscofins: { CST: '07' },
193
- },
194
- totTrib: {
195
- pTotTribFed: '0.00',
196
- pTotTribEst: '0.00',
197
- pTotTribMun: '5.00',
198
- },
199
- },
200
- });
201
-
202
- console.log(invoice.chaveAcesso);
203
- console.log(invoice.nfseXml);
204
- ```
205
-
206
- `environment: 'sandbox'` é alias de `restrita`, e `production` é alias de
207
- `producao`. `certificate` aceita um `NfseCertificate` ou, por compatibilidade,
208
- `{ content, password }` com o PFX em `Buffer` ou base64.
209
-
210
- Para inspecionar a DPS gerada sem assinar nem enviar:
211
-
212
- ```ts
213
- const dps = client.invoices.buildDpsJson({
214
- prestador: {
215
- serie: '1701',
216
- },
217
- emissao: {
218
- nDPS: '2',
219
- valores: { vServ: '2500.00' },
220
75
  tributacaoMunicipal: { tribISSQN: '1', tpRetISSQN: '1' },
221
- totTrib: { pTotTribFed: '0', pTotTribEst: '0', pTotTribMun: '5' },
222
- },
223
- });
224
- ```
225
-
226
- ---
227
-
228
-
229
- ### Validacao e compatibilidade
230
-
231
- `validateDpsJsonRequest(entrada)` retorna `valid`, `issues` (erros), `warnings` (avisos) e uma copia `normalizedPayload`. Campos opcionais devem ser omitidos ou `undefined`; strings vazias, `null` e tipos incorretos sao rejeitados. Consulte [o mapeamento JSON](JSON_MAPPING.md) para os limites de datas e valores.
232
-
233
- Divergencias entre `vServ` e a conversao cambial geram avisos, preservando o valor explicito. O mesmo vale para o valor estrangeiro do bloco de comercio exterior. Todos os valores monetarios sao arredondados para duas casas, inclusive `comExt.vServMoeda`; valores de servico que arredondam para zero sao rejeitados. Leia `warnings` antes da emissao quando quiser apresentar esses avisos ao usuario.
234
-
235
- `prepararNota` verifica a vigencia do certificado antes de assinar; `transmitirNotaPreparada` verifica novamente antes do envio. `assertCertificateValidity(certificado, now?)` expoe essa verificacao. A checagem de titularidade continua explicita via `assertCertificateForProvider(certificado, documento, now?)`; o SDK nao resolve representacao fiscal.
236
-
237
- O ambiente para JSON segue `options.ambiente` → `request.ambiente` → `restrita`, tanto no XML quanto no destino. XML externo usa `tpAmb` e rejeita uma opcao divergente. Notas preparadas tambem precisam ter o mesmo ambiente no XML assinado e no envio.
238
-
239
- `client.invoices.get(chaveAcesso, { ambiente: 'producao' })` permite consultar uma emissao feita com sobrescrita de ambiente. Sem opcao, usa `defaults.ambiente` ou o ambiente do construtor. O cliente nao memoriza o ambiente da ultima emissao.
240
-
241
- HTTP 2xx sem XML NFS-e bem formado e chave de 50 digitos consistente gera `EmitirNotaError` com codigo `INVALID_AUTHORIZATION_RESPONSE`, preservando corpo, status e DPS. Isso nao confirma rejeicao fiscal: concilie a emissao antes de reenviar. Nao ha retentativa automatica nem verificacao da assinatura da NFS-e recebida.
242
-
243
- Veja [as correcoes e mudancas de comportamento](docs/CORRECOES_EMISSAO.md) e os [payloads nacional e exterior testados](examples/payloads.ts). Os exemplos declaram cenarios ilustrativos; a aplicacao escolhe o enquadramento aplicavel.
244
-
245
- > **Migracao para 3.0.0:** esta versao tem breaking changes de validacao e resposta de autorizacao. Leia o guia curto em [CORRECOES_EMISSAO.md](docs/CORRECOES_EMISSAO.md) antes de atualizar.
246
-
247
- ## Certificado A1
248
-
249
- Use `NfseCertificate`. Ele lê o `.pfx`/`.p12` uma vez, mantém a chave privada fora do objeto e descarta a senha e os bytes do arquivo. `console.log(certificado)` e `JSON.stringify(certificado)` mostram só CNPJ/CPF, validade e fingerprint.
250
-
251
- ### Variáveis de ambiente (recomendado)
252
-
253
- ```bash
254
- NFSE_CERT_PATH=./certificado.pfx # ou NFSE_CERT_BASE64=<PFX em base64>
255
- NFSE_CERT_PASSWORD=sua-senha # ou NFSE_CERT_PASSWORD_FILE=/run/secrets/nfse_cert_password
256
- ```
257
-
258
- ```ts
259
- import { NfseCertificate } from '@useinvio/nfse-sdk';
260
-
261
- const certificado = NfseCertificate.fromEnv();
262
- console.log(certificado.cnpj, certificado.notAfter, certificado.daysUntilExpiry());
263
- ```
264
-
265
- `NFSE_CERT_PASSWORD_FILE` lê a senha de um arquivo, o formato de secrets do Docker e do Kubernetes. Nunca versione o `.pfx` nem a senha.
266
-
267
- ### Outras origens
268
-
269
- ```ts
270
- NfseCertificate.fromFile('./certificado.pfx', senha);
271
- NfseCertificate.fromBase64(pfxBase64, senha); // aceita quebras de linha e data: URLs
272
- NfseCertificate.fromPfx(pfxBuffer, senha); // ex.: PFX vindo do seu cofre de segredos
273
- NfseCertificate.fromPem({ key, cert, passphrase }); // par extraído com openssl
274
- ```
275
-
276
- ### Vários emitentes (multi-tenant)
277
-
278
- Crie um `NfseCertificate` por emitente e reaproveite-o. Cada certificado tem o próprio agente mTLS, então conexões nunca são compartilhadas entre emitentes. Guarde o PFX cifrado (KMS, Vault, Secrets Manager) e a senha em um segredo separado, e descriptografe só ao criar o objeto.
279
-
280
- ```ts
281
- import { assertCertificateForProvider, NfseCertificate } from '@useinvio/nfse-sdk';
282
-
283
- const cache = new Map<string, NfseCertificate>();
284
-
285
- async function certificadoDoEmitente(cnpj: string): Promise<NfseCertificate> {
286
- const cached = cache.get(cnpj);
287
- if (cached?.isValidAt()) return cached;
288
- const { pfx, senha } = await cofre.buscarCertificado(cnpj); // seu secret manager
289
- const certificado = NfseCertificate.fromPfx(pfx, senha);
290
- assertCertificateForProvider(certificado, cnpj);
291
- cache.set(cnpj, certificado);
292
- return certificado;
293
- }
294
- ```
295
-
296
- ### Erros de certificado
297
-
298
- Todos lançam `CertificateValidationError` com um `code` estável:
299
-
300
- | `code` | Quando |
301
- |---|---|
302
- | `CERTIFICATE_INVALID_PASSWORD` | Senha incorreta. |
303
- | `CERTIFICATE_UNREADABLE` | O conteúdo não é um PFX/PEM válido. |
304
- | `CERTIFICATE_MISSING`, `CERTIFICATE_PASSWORD_MISSING` | Variáveis de ambiente ausentes em `fromEnv`. |
305
- | `CERTIFICATE_NOT_YET_VALID`, `CERTIFICATE_EXPIRED` | Fora da vigência. Verificado antes de assinar e antes de enviar. |
306
- | `CERTIFICATE_HOLDER_MISMATCH` | `assertCertificateForProvider` com CPF/CNPJ diferente do titular. |
307
-
308
- > `loadPfx` e `loadPfxFromBuffer` continuam funcionando, mas retornam a chave privada em PEM dentro de um objeto comum. Prefira `NfseCertificate`.
309
-
310
- ---
311
-
312
- ## Emitir a partir de XML pronto
313
-
314
- Use quando outro sistema (ERP, sistema legado) já gera o XML da DPS no layout nacional.
315
-
316
- ```ts
317
- import { emitirNfse, NfseCertificate } from '@useinvio/nfse-sdk';
318
-
319
- const certificado = NfseCertificate.fromEnv();
320
-
321
- const resultado = await emitirNfse(dpsXml, certificado, { ambiente: 'restrita' });
322
-
323
- console.log(resultado.chaveAcesso);
324
- console.log(resultado.nfseXml);
325
- ```
326
-
327
- > O XML precisa ter `Id` valido em `<infDPS>` e `tpAmb` consistente com o destino. `options.dpsId` apenas seleciona a referencia de assinatura; nao corrige um XML incompleto nem dispensa o XSD.
328
-
329
- ---
330
-
331
- ## Emitir a partir de JSON declarativo
332
-
333
- Use quando a aplicação mantém os perfis de prestador e serviço e quer que a SDK monte o XML.
334
-
335
- Para a referência completa de campos aceitos no JSON, consulte [JSON_MAPPING.md](./JSON_MAPPING.md).
336
- O builder valida formatos e combinações mínimas antes de gerar XML, mas não escolhe códigos fiscais por você. Campos como `tributacaoMunicipal` e `totTrib` precisam ser informados explicita[...]
337
-
338
- ```ts
339
- import { emitirNfse, NfseCertificate, type DpsJsonRequest } from '@useinvio/nfse-sdk';
340
-
341
- const nota: DpsJsonRequest = {
342
- ambiente: 'restrita',
343
- prestador: {
344
- cnpj: '12345678000195',
345
- cLocEmi: '4106902',
346
- serie: '1601',
347
- opSimpNac: '1',
348
- regEspTrib: '0',
349
- },
350
- servico: {
351
- cTribNac: '010201',
352
- xDescServ: 'Desenvolvimento de software',
353
- cLocPrestacao: '4106902',
354
- cNBS: '115022000',
355
- },
356
- emissao: {
357
- nDPS: '1',
358
- dhEmi: '2026-06-15T10:30:00-03:00',
359
- dCompet: '2026-06-01',
360
- valores: { vServ: '1500.00' },
361
- tributacaoMunicipal: {
362
- tribISSQN: '1',
363
- tpRetISSQN: '1',
364
- },
365
- tributacaoFederal: {
366
- piscofins: { CST: '07' },
367
- },
368
- totTrib: {
369
- pTotTribFed: '0.00',
370
- pTotTribEst: '0.00',
371
- pTotTribMun: '5.00',
372
- },
76
+ tributacaoFederal: { piscofins: { CST: '07' } },
77
+ totTrib: { pTotTribFed: '0.00', pTotTribEst: '0.00', pTotTribMun: '5.00' },
373
78
  },
374
79
  };
375
80
 
376
- const certificado = NfseCertificate.fromEnv();
377
- const resultado = await emitirNfse(nota, certificado);
378
- ```
379
-
380
- > `pTotTribFed`, `pTotTribEst` e `pTotTribMun` são percentuais com duas casas decimais (ex.: `"5.00"`), diferentes das alíquotas com quatro casas (`pAliq`).
381
- > O shape simplificado legado de `tribNac` é rejeitado: IBS/CBS no layout nacional `v1.01` exige o bloco RTC `IBSCBS`, ainda não implementado nesta SDK.
382
-
383
- ---
384
-
385
- ## Inspecionar o XML antes de enviar
386
-
387
- Útil para comparar com um XML de referência, salvar snapshots ou depurar rejeições de schema.
388
-
389
- ```ts
390
- import { buildDpsFromJson } from '@useinvio/nfse-sdk';
391
-
392
- const { id, xml } = buildDpsFromJson(nota);
393
-
394
- console.log(id); // ex.: "DPS..."
395
- console.log(xml); // XML não assinado da DPS
396
- ```
397
-
398
- ---
399
-
400
- ## Tratar rejeições da SEFIN
401
-
402
- `emitirNfse` lança `EmitirNotaError` quando a SEFIN retorna HTTP fora de 2xx ou resposta sem XML autorizado.
403
-
404
- ```ts
405
- import { EmitirNotaError, emitirNfse } from '@useinvio/nfse-sdk';
406
-
407
81
  try {
408
- await emitirNfse(nota, certificado);
82
+ const { chaveAcesso, nfseXml } = await emitirNfse(nota, certificado);
83
+ console.log(chaveAcesso); // chave de 50 dígitos
409
84
  } catch (error) {
410
85
  if (!(error instanceof EmitirNotaError)) throw error;
411
-
412
- console.log('HTTP:', error.status);
413
- console.log('DPS ID:', error.dpsId);
414
-
415
- for (const rejeicao of error.erros) {
416
- console.log(rejeicao.Codigo); // código oficial SEFIN
417
- console.log(rejeicao.Descricao); // descrição da rejeição
418
- console.log(rejeicao.Complemento); // detalhe adicional (quando presente)
419
- }
86
+ for (const rejeicao of error.erros) console.error(rejeicao.Codigo, rejeicao.Descricao);
420
87
  }
421
88
  ```
422
89
 
423
- ---
424
-
425
- ## Consultar uma NFS-e emitida
426
-
427
- ```ts
428
- import { consultarNfse } from '@useinvio/nfse-sdk';
429
-
430
- const resposta = await consultarNfse(chaveAcesso, certificado, 'restrita');
431
-
432
- if (resposta.status === 200) {
433
- console.log(resposta.body);
434
- } else {
435
- console.error('Falha na consulta:', resposta.status, resposta.body);
436
- }
437
- ```
438
-
439
- ---
440
-
441
- ## Enviar evento (ex.: cancelamento)
442
-
443
- ```ts
444
- import { enviarEvento, gzipBase64, signEnveloped } from '@useinvio/nfse-sdk';
445
-
446
- const signedEventXml = signEnveloped(pedRegXml, pedRegId, 'infPedReg', certificado);
447
- const pedRegXmlGZipB64 = gzipBase64(signedEventXml);
448
-
449
- const resposta = await enviarEvento(pedRegXmlGZipB64, certificado, chaveAcesso, 'restrita');
450
- ```
451
-
452
- ---
453
-
454
- ## Série e número da DPS
90
+ Primeira vez? Siga o [tutorial](./docs/tutoriais/primeira-nfse.md): emitir,
91
+ consultar, baixar o PDF e cancelar na produção restrita, do zero. Ou experimente
92
+ sem certificado, pelo terminal:
455
93
 
456
- A SEFIN identifica uma DPS pela combinação de prestador, município, `serie` e `nDPS`.
457
-
458
- ```ts
459
- // série no perfil do prestador (padrão para todas as DPS)
460
- prestador: {
461
- serie: '1601',
462
- }
463
-
464
- // ou por emissão (sobrescreve prestador.serie apenas para esta DPS)
465
- emissao: {
466
- serie: '1601',
467
- nDPS: '4',
468
- }
469
- ```
470
-
471
- O padrão usual é manter uma `serie` fixa e incrementar `nDPS` em cada nova DPS:
472
-
473
- ```
474
- serie 1601, nDPS 1
475
- serie 1601, nDPS 2
476
- serie 1601, nDPS 3
477
- ```
478
-
479
- > O Emissor Web usa séries como `70000`. Para emissão via API, use uma série válida para API (ex.: `1601`) e controle o `nDPS` na sua aplicação.
480
-
481
- ---
482
-
483
- ## Ambientes
484
-
485
- | Chave | URL |
486
- |---|---|
487
- | `restrita` | `https://sefin.producaorestrita.nfse.gov.br/SefinNacional` |
488
- | `producao` | `https://sefin.nfse.gov.br/SefinNacional` |
489
-
490
- ```ts
491
- import { resolveSefinBaseUrl } from '@useinvio/nfse-sdk';
492
-
493
- const baseUrl = resolveSefinBaseUrl('restrita');
494
- ```
495
-
496
- ---
497
-
498
- ## Observabilidade da SEFIN
499
-
500
- O SDK pode emitir métricas de latência das chamadas mTLS feitas contra a SEFIN quando `NFSE_SEFIN_LATENCY_METRICS=1` está configurada. Por padrão, percentis como `p50Ms`, `p95Ms` e `p99Ms` ficam desligados e precisam de `includePercentiles: true`. Essas métricas medem o round-trip externo da SEFIN, separado da latência total da sua API.
501
-
502
- Leia o guia completo em [docs/METRICS.md](./docs/METRICS.md).
503
-
504
- ---
505
-
506
- ## Agentes de IA e MCP
507
-
508
- O subpath `@useinvio/nfse-sdk/agent` expõe ferramentas neutras de framework para servidores MCP e agentes (OpenAI, LangChain, Vercel AI SDK). Cada ferramenta traz `name`, `description`, `inputSchema`/`outputSchema` em JSON Schema, `annotations` no formato MCP e um handler `run`.
509
-
510
- | Ferramenta | Efeito | Certificado |
511
- | --- | --- | --- |
512
- | `validate_dps` | Valida a requisição; nenhum efeito colateral. | Não |
513
- | `build_dps_xml` | Gera o XML não assinado e confere contra o XSD v1.01. | Não |
514
- | `get_nfse` | Consulta uma NFS-e pela chave de acesso. | Sim |
515
-
516
- ```ts
517
- import { nfseAgentTools } from '@useinvio/nfse-sdk/agent';
518
-
519
- const context = { pfx: certificado, ambiente: 'restrita' as const }; // configuração do host
520
- for (const tool of nfseAgentTools) {
521
- // registre tool.name, tool.description, tool.inputSchema e tool.annotations no seu framework
522
- // e chame tool.run(argumentos, context) quando o modelo usar a ferramenta
523
- }
94
+ ```bash
95
+ npx nfse init && npx nfse emit nota.json --mock
524
96
  ```
525
97
 
526
- Certificado e ambiente ficam no `context` do host, nunca na entrada da ferramenta: o modelo não escolhe `producao` nem vê material de chave. Uma requisição com `ambiente` diferente do configurado gera `AMBIENTE_NOT_ALLOWED`. Falhas esperadas retornam `issues` com `code`, `path`, `severity`, `source` e `suggestion`; falhas de infraestrutura (rede, motor XSD) rejeitam a promise. `toAgentIssues(error)` converte erros do SDK (`DpsFiscalValidationError`, `DpsXsdValidationError`, `CertificateValidationError`, `EmitirNotaError`) para esse mesmo formato.
98
+ ## Documentação
527
99
 
528
- `DPS_JSON_REQUEST_SCHEMA`, exportado também pela raiz do pacote, descreve o `DpsJsonRequest` canônico em JSON Schema 2020-12 com um subconjunto portável de palavras-chave. Ele cobre formatos e domínios de códigos, mas não as regras entre campos (regime × `totTrib`, nacional × exportação); `validateDpsJsonRequest` continua sendo a autoridade. Atalhos legados (`emissao.vServ`, `comExt`) e campos rejeitados pela validação ficam fora do schema; `validate_dps` aponta campos desconhecidos como `UNSUPPORTED_FIELD` em vez de ignorá-los.
529
-
530
- A emissão ainda não é exposta como ferramenta: ela exigirá confirmação explícita antes do envio.
531
-
532
- ---
533
-
534
- ## Referência da API
535
-
536
- ### Funções principais
537
-
538
- | Função | Descrição |
100
+ | Guia | |
539
101
  |---|---|
540
- | `new NfseClient(options)` | Cria cliente com ambiente, certificado e defaults no mesmo formato do JSON declarativo. |
541
- | `emitirNfse(input, certificado, options?)` | Emite uma NFS-e a partir de XML ou JSON. Retorna `ResultadoEmissaoNota`. |
542
- | `consultarNfse(chaveAcesso, certificado, ambiente?)` | Consulta uma NFS-e pela chave de acesso. |
543
- | `enviarEvento(xmlGzipB64, certificado, chaveAcesso, ambiente?)` | Envia evento fiscal (cancelamento, etc.). |
544
- | `buildDpsFromJson(nota)` | Monta o XML da DPS sem assinar nem enviar. |
545
- | `setSefinRequestObserver(observer)` | Registra um hook para observar latência/status das chamadas à SEFIN. |
546
- | `createSefinLatencyTracker(options?)` | Agrega amostras recentes e pode calcular percentis quando `includePercentiles` estiver habilitado. |
547
-
548
- ### Certificado
549
-
550
- | Função | Descrição |
551
- |---|---|
552
- | `assertCertificateValidity(certificado, now?)` | Verifica vigencia do certificado; aplicada na preparacao e no envio. |
553
- | `assertCertificateForProvider(certificado, documento, now?)` | Verifica explicitamente vigencia e correspondencia do titular. |
554
- | `NfseCertificate.fromEnv()` / `fromFile()` / `fromBase64()` / `fromPfx()` / `fromPem()` | Carrega o certificado A1 sem expor a chave privada. |
555
- | `loadPfx(path, password)` | Legado: retorna a chave em PEM. Prefira `NfseCertificate`. |
556
- | `loadPfxFromBuffer(buffer, password)` | Legado: retorna a chave em PEM. Prefira `NfseCertificate`. |
557
-
558
- ### Funções de baixo nível
559
-
560
- | Função | Descrição |
561
- |---|---|
562
- | `signDps(xml, dpsId, certificado)` | Assina XML de DPS com XMLDSIG. |
563
- | `signEnveloped(xml, id, ref, certificado)` | Assina XML genérico (eventos). |
564
- | `verifyDps(xml, certPem)` | Verifica assinatura de uma DPS. |
565
- | `gzipBase64(xml)` | Compacta XML em GZip e codifica em Base64. |
566
- | `gunzipBase64(b64)` | Descompacta resposta GZip/Base64. |
567
- | `extrairErros(body)` | Normaliza erros oficiais de uma resposta da SEFIN. |
568
- | `resolveSefinBaseUrl(ambiente)` | Retorna a URL base do ambiente. |
569
-
570
- ### Tipos exportados
571
-
572
- ```ts
573
- import type {
574
- Ambiente,
575
- CreateInvoiceInput,
576
- GetInvoiceOptions,
577
- DpsJsonInput,
578
- DpsJsonRequest,
579
- EmitirNotaOptions,
580
- NfseClientDpsDefaults,
581
- NfseClientOptions,
582
- NotaInput,
583
- PfxMaterial,
584
- PrestadorProfile,
585
- ResultadoEmissaoNota,
586
- SefinErro,
587
- SefinResposta,
588
- ServicoProfile,
589
- } from '@useinvio/nfse-sdk';
590
- ```
102
+ | [Certificado A1](./docs/guias/certificado.md) | Variáveis de ambiente, cofres de segredo, vários emitentes. |
103
+ | [Emitir](./docs/guias/emitir.md) | JSON, `NfseClient`, XML pronto, série e número. |
104
+ | [Consultar e DANFSe](./docs/guias/consultar.md) | Resultado tipado e PDF. |
105
+ | [Recuperar após timeout](./docs/guias/recuperar-emissao.md) | Sem notas duplicadas quando a conexão cai. |
106
+ | [Cancelar](./docs/guias/cancelar.md) | e101101 e substituição (e105102). |
107
+ | [Erros e rejeições](./docs/guias/rejeicoes.md) | O que cada erro significa e o que fazer. |
108
+ | [Linha de comando](./docs/guias/cli.md) | `nfse doctor`, `emit`, `cancel`, `explain`. |
109
+ | [Testar sem certificado](./docs/guias/testes.md) | SEFIN simulada para o seu CI. |
110
+ | [Agentes de IA e MCP](./docs/guias/agentes-ia.md) | Servidor MCP, plugin do Claude Code, ferramentas para o seu agente. |
111
+ | [Referência da API](./docs/referencia/api.md) | Todas as funções. |
112
+ | [Mapeamento do JSON](./JSON_MAPPING.md) | Cada campo de `DpsJsonRequest`. |
591
113
 
592
- ### Constantes
114
+ Exemplos executáveis em [`examples/`](./examples).
593
115
 
594
- ```ts
595
- import { SEFIN_BASE_URL, TP_AMB, DEFAULT_AMBIENTE } from '@useinvio/nfse-sdk';
596
- ```
116
+ ## Com agentes de IA
597
117
 
598
- ---
599
-
600
- ## Desenvolvimento
118
+ Emita uma NFS-e conversando com o Claude. Para experimentar sem certificado, o
119
+ modo de teste usa uma SEFIN simulada:
601
120
 
602
121
  ```bash
603
- npm install
604
- npm run typecheck # verifica tipos sem compilar
605
- npm run build # compila para dist/
606
- npm test # build + tipos + testes unitários
122
+ claude mcp add nfse --env NFSE_MCP_MOCK=1 --env NFSE_MCP_ALLOW_WRITE=1 -- npx -y @useinvio/nfse-mcp
607
123
  ```
608
124
 
609
- `npm test` também valida as DPS geradas contra os XSD oficiais em `schemas/nfse/v1.01/Schemas/1.01` com o motor embutido (libxml2 em WebAssembly). Se o `xmllint` estiver instalado, como no CI, os testes também o usam como verificação cruzada.
610
-
611
- ### Uso local em outro projeto
125
+ Ou instale o plugin do Claude Code, que inclui o servidor e uma skill com o fluxo:
612
126
 
613
- ```json
614
- {
615
- "dependencies": {
616
- "@useinvio/nfse-sdk": "file:../nfse-sdk"
617
- }
618
- }
619
- ```
620
-
621
- ---
622
-
623
- ## Publicação no npm
624
-
625
- Pacote publicado publicamente no npm como [`@useinvio/nfse-sdk`](https://www.npmjs.com/package/@useinvio/nfse-sdk).
626
-
627
- ```bash
628
- npm install
629
- npm run typecheck
630
- npm run build
631
- npm pack --dry-run # verifica o que será publicado
632
- npm publish
127
+ ```text
128
+ /plugin install nfse-nacional --marketplace useinvio/nfse-sdk
633
129
  ```
634
130
 
635
- O pacote usa `publishConfig.access = public`, necessário para publicar um pacote escopado público no npm.
131
+ Emitir e cancelar sempre passam por um resumo que você aprova antes do envio, e
132
+ o certificado nunca chega ao modelo. Veja o [guia de agentes e MCP](./docs/guias/agentes-ia.md).
133
+ Para assistentes de código, o pacote inclui [`llms.txt`](./llms.txt).
636
134
 
637
- ---
135
+ ## O que fica com a sua aplicação
638
136
 
639
- ## Exemplos
137
+ O SDK cuida do protocolo; ele não é um motor fiscal. Códigos de serviço, CST,
138
+ alíquotas, retenções, regime tributário e a numeração da DPS são decisões da
139
+ sua aplicação e de quem cuida da parte tributária. Campos fiscais obrigatórios
140
+ nunca recebem valores padrão silenciosos. O grupo IBS/CBS da reforma tributária
141
+ ainda não é suportado.
640
142
 
641
- Exemplos copiáveis ficam em [`examples/`](./examples).
143
+ ## Contribuir
642
144
 
643
- ---
145
+ Issues e PRs são bem-vindos. Veja [CONTRIBUTING.md](./CONTRIBUTING.md) e o
146
+ [CHANGELOG](./CHANGELOG.md).
644
147
 
645
- ## Princípios de design
148
+ ## Licença
646
149
 
647
- - Sem dependência de banco de dados, config de aplicação ou estado de tenant.
648
- - Funções pequenas, testáveis e reutilizáveis.
649
- - Erros oficiais da SEFIN são preservados na íntegra, não engolidos.
650
- - O SDK transporta declarações fiscais; não valida se estão contabilmente corretas.
150
+ [MIT](./LICENSE) © Invio