open-nfse
v0.9.1
Published
Biblioteca TypeScript/Node.js para o Padrão Nacional de NFS-e (nfse.gov.br): consulta, distribuição, emissão síncrona, cancelamento e substituição — direto na API oficial da Receita Federal.
Maintainers
Readme
open-nfse
Cliente TypeScript/Node.js para o Padrão Nacional de NFS-e (nfse.gov.br) — a API unificada da Receita Federal, obrigatória em todo o Brasil a partir de 1º de janeiro de 2026 (LC 214/2025). Fala direto na API oficial, sem gateway intermediário.
📚 Documentação: fm-s.github.io/open-nfse · API cheat sheet · API completa
Status
v0.9.x — alinhamento ao schema oficial RTC v1.01 + NT-007. Ciclo fiscal completo: consulta (chave + NSU), emissão segura com DpsCounter + RetryStore, cancelamento e substituição com máquina de 5 estados, validações locais (XSD / CPF+CNPJ / CEP), parâmetros municipais com cache, DANFSe em PDF (online + fallback local), NfseClientFake em open-nfse/testing. Pipeline de retry consciente de 429 (TooManyRequestsError → retry_pending com notBefore honrando Retry-After; RetryPolicy pluggable).
Novidades em v0.9 — o validador passou a ser gerado dos schemas oficiais schemas/1.01/ e a lib foi auditada campo a campo contra eles: domínio completo do CST PIS/COFINS e TipoRetPisCofins (NT-007, em produção desde 2026-02-09), cNBS opcional, remoção de elementos inexistentes no RTC v1.01 (lsadppu/explRod), correção do parsing de eventos de rejeição/anulação e de campos minOccurs="0" lidos como obrigatórios. Inclui breaking renames pré-1.0 (fetchDanfse→consultarDanfse, EmitManyOptions→EmitLoteOptions, InvalidIdDpsError→InvalidDpsIdError, enums de PIS/COFINS, xOutInf movido para InfNFSe). Veja CHANGELOG para a lista completa.
v0.9.1 — segunda auditoria de conformidade (o bundle oficial não mudou): confirmada conformante, com o valor faltante RegimeEspecialTributacao.Outros ('9') e novos enums para campos antes string (SituacaoNfse/cStat, IndicadorDestinatario/indDest, MecanismoApoioComExPrestador/Tomador/mecAFComex*), verAplic de evento obrigatório, e cinco correções no cheat sheet da API.
Foco até 1.0 é estabilização; a API pública pode receber ajustes, sem breaking changes sem aviso em CHANGELOG.
Instalar
npm install open-nfseRequer Node.js 20+ e certificado digital A1 (ICP-Brasil) em .pfx ou .p12 do CNPJ emitente habilitado no Emissor Nacional.
Exemplo mínimo
Setup do cliente com counter atômico de nDPS e store de retry (in-memory para início rápido; produção usa Postgres):
import {
NfseClient, Ambiente,
createInMemoryDpsCounter, createInMemoryRetryStore,
} from 'open-nfse';
import { readFileSync } from 'node:fs';
const cliente = new NfseClient({
ambiente: Ambiente.ProducaoRestrita,
certificado: { pfx: readFileSync('./cert.pfx'), password: process.env.CERT_PASSWORD! },
dpsCounter: createInMemoryDpsCounter(),
retryStore: createInMemoryRetryStore(),
});Emissão com resultado discriminado (ok autorizada, retry_pending salvo no store, throw em rejeição permanente):
import {
OpcaoSimplesNacional, RegimeApuracaoSimplesNacional, RegimeEspecialTributacao,
ReceitaRejectionError,
} from 'open-nfse';
try {
const r = await cliente.emitir({
emitente: {
cnpj: '00574753000100',
codMunicipio: '2111300',
regime: {
opSimpNac: OpcaoSimplesNacional.MeEpp,
regApTribSN: RegimeApuracaoSimplesNacional.FederalEMunicipalPeloSN,
regEspTrib: RegimeEspecialTributacao.Nenhum,
},
},
serie: '1',
servico: { cTribNac: '010101', cNBS: '123456789', descricao: 'Consultoria' },
valores: { vServ: 1500.0, aliqIss: 2.5 },
// Simples Nacional? informe a alíquota aproximada:
// valores: { vServ: 1500.0, pTotTribSN: 6.0 },
tomador: { documento: { CNPJ: '11222333000181' }, nome: 'Acme Ltda' },
});
if (r.status === 'ok') {
console.log('autorizada:', r.nfse.chaveAcesso);
} else {
console.warn('pendente no store:', r.pending.id);
}
} catch (err) {
if (err instanceof ReceitaRejectionError) {
console.error(`rejeitada [${err.codigo}]: ${err.descricao}`);
} else {
throw err;
}
}Cron de retry (mesma função cobre emitir, cancelar, substituir):
// schedule: a cada 1-5 minutos, um worker só
const items = await cliente.replayPendingEvents();
for (const item of items) {
if (item.status === 'success_emission') await persistirNfse(item.emission);
if (item.status === 'success') await persistirEvento(item.evento);
if (item.status === 'failed_permanent') alertar(item);
// still_pending fica no store para próxima rodada
}Exemplos runnables em examples/emit-nfse/. Padrão completo de produção (persistência, bulk com counter+retry, reconciliação) em Emitir NFS-e.
O que a lib cobre
| Escopo | Guia | |-----------------------------------------|----------------------------------------------------------------------| | Consulta por chave + distribuição por NSU | Consultar | | Emissão segura com counter + retry store | Emitir | | Cancelamento e substituição (5 estados) | Substituir e cancelar | | Validações XSD + CPF/CNPJ + CEP | Validações | | Parâmetros municipais com cache | Parâmetros | | DANFSe em PDF (online + local) | DANFSe | | Dublê em memória para testes | Testing | | Schema SQL sugerido para integração | Integração em serviços |
Arquitetura
┌────────────────────────────────────────────────────────────┐
│ API pública (NfseClient + open-nfse/testing) │
├────────────────────────────────────────────────────────────┤
│ Leitura │ Emissão │ Eventos │
│ fetch-by-chave │ emitir │ cancelar │
│ fetch-by-nsu │ emitir-em-lote │ substituir (4-st) │
│ │ emitirDpsPronta│ replayPendingEvents│
│ │
│ Retry pipeline (429-aware): PendingEvent + RetryStore + │
│ RetryPolicy (notBefore + attempts + safe-wrap) │
│ │
│ parse-xml ↔ build-xml + sign-xml (RTC v1.01 ↔ DTO) │
│ build-dps (helper) + dps-id + validate-xml (XSD WASM) │
├────────────────────────────────────────────────────────────┤
│ Validações: CPF/CNPJ DV · CEP (ViaCEP) │
│ Parâmetros municipais (6× consultar + cache) │
│ DANFSe: fetch (ADN) + gerar (pdfkit local) │
├────────────────────────────────────────────────────────────┤
│ HTTP client (undici + mTLS + HTTP/1.1, GZip/Base64, PDF) │
│ · TooManyRequestsError (429) + getRetryAfterMs() │
├────────────────────────────────────────────────────────────┤
│ Certificado A1 (node-forge, ICP-Brasil, pluggable) │
└────────────────────────────────────────────────────────────┘A API oficial está dividida em quatro hosts distintos (SEFIN Nacional, ADN Contribuintes, ADN DANFSe, ADN Parâmetros Municipais) com contratos wire diferentes — camelCase vs PascalCase, tipoAmbiente int vs string. O NfseClient resolve o host por chamada; DTOs públicos normalizam.
Princípios
DTO in, DTO out. Erros tipados em 3 níveis. Sem estado interno, com primitives de orquestração (emitirEmLote, máquina de 5 estados de substituir) e retry (RetryStore + RetryPolicy + replayPendingEvents). Types derivados dos XSDs RTC v1.01 oficiais; xs:choice vira discriminated union. Identificadores fiscais como string para preservar zeros. Builder de DPS separável do transporte (dry-run). Detalhes em Princípios de design.
Ambientes
| Serviço | Produção Restrita (homologação) | Produção (notas válidas) |
|--------------------------------|----------------------------------------------------|-------------------------------------------|
| SEFIN Nacional | sefin.producaorestrita.nfse.gov.br/SefinNacional | sefin.nfse.gov.br/SefinNacional |
| ADN Contribuintes | adn.producaorestrita.nfse.gov.br/contribuintes | adn.nfse.gov.br/contribuintes |
| ADN DANFSe | adn.producaorestrita.nfse.gov.br/danfse | adn.nfse.gov.br/danfse |
| ADN Parâmetros Municipais | adn.producaorestrita.nfse.gov.br/parametrizacao | adn.nfse.gov.br/parametrizacao |
Status dos municípios
Todo município é obrigado a aderir ao Padrão Nacional, mas a migração é gradual ao longo de 2026. A lib funciona com qualquer município aderente — a API é a mesma. Municípios ainda não aderentes continuam no sistema municipal até a migração.
Escopo explícito
Suportado: consulta (chave + NSU), emissão segura (emitir(params)), eventos 101101 e 105102 (cancelamento + substituição) como emissão; parse de todos os 16 tipos de eventos via NSU distribuição (inclusive confirmação/rejeição P/T/I, cancelamento por ofício); validações locais; parâmetros municipais; DANFSe PDF; fetchDpsStatus para reconciliação.
Fora de escopo (intencional):
POST /decisao-judicial/nfse— backs the Emissor Público Web UI per Guia v1.2 §4.3, não é uma API de contribuinte. Emissão por decisão judicial acontece via UI Web da Receita, não via lib.- CNC (Cadastro Nacional de Contribuintes) — schemas
CNC_v1.00.xsd/tiposCnc_v1.00.xsdnão são referenciados pelo fluxo NFS-e; fora do escopo. - Emissão dos 14 tipos de evento "não-cancelamento" (202201 etc.) — parseamos quando chegam via NSU, mas não há builders públicos. Município-only em sua maioria; abra uma issue se precisar.
- ADN Parâmetros Municipais — endpoints POST (
beneficiomunicipal/{idManut},regimes_especiais/{idManut},retencoes/{idManut}) — admin-only da municipalidade.
Backlog (worth wrapping antes de 1.0):
GET /nfse/{chave}/eventos/{tipoEvento}/{numSeqEvento}(SEFIN) — fetch específico de eventoGET /NFSe/{chave}/Eventos(ADN Contribuintes) — event list por chave sem passar por NSU
Contribuindo
Bugs e sugestões: issues. PRs bem-vindos — rode npm run lint && npm run typecheck && npm test antes de abrir. Histórico de versões no CHANGELOG.md.
Links
- Portal oficial NFS-e Nacional
- Documentação técnica da Receita
- Manual do Contribuinte v1.2
- Lei Complementar 214/2025
Aviso legal
Biblioteca não oficial, sem vínculo com a Receita Federal do Brasil. Software open source, distribuído sob licença MIT, sem garantias. Homologação e conformidade fiscal são responsabilidade de quem utiliza.
