Cliente oficial Node.js/TypeScript para a API pública do IntegroBR NFS-e Recebidas — consulte e gerencie, de forma programática, as NFS-e (notas de serviço) monitoradas pela sua conta IntegroBR.
- Documentação completa da API: https://recebidas.integrobr.com/docs
- Requer Node.js 18+ (usa
fetch/FormData/AbortControllernativos — sem dependências de runtime).
npm install @integrobr/nfse-sdkimport { IntegroBRClient } from "@integrobr/nfse-sdk";
const client = new IntegroBRClient({ apiKey: process.env.INTEGROBR_API_KEY! });
const conta = await client.obterConta();
console.log(conta.nome, conta.ambiente); // "Empresa Exemplo LTDA" "PRODUCAO"
const empresas = await client.empresas.listar();
const notas = await client.documentos.listar({ situacao: "AUTORIZADA", limite: 50 });Gere uma chave em Painel → Chaves de API (/painel/chaves-api). Ela só é exibida uma vez — se perder, revogue e crie outra. Existem dois ambientes de chave, que nunca se misturam:
| Prefixo | Ambiente |
|---|---|
ibr_test_... |
Sandbox — dados de teste, nunca reais, nunca geram cobrança. |
ibr_live_... |
Produção — dados fiscais reais da sua conta. |
// Listar (GET /companies não é paginado)
const empresas = await client.empresas.listar();
// Cadastrar um CNPJ novo
const empresa = await client.empresas.criar({ cnpj: "12345678000195", nomeExibicao: "Filial São Paulo" });
// Enviar o certificado A1 (.pfx/.p12)
import { readFileSync } from "node:fs";
const arquivo = readFileSync("./certificado.pfx");
await client.empresas.enviarCertificado(empresa.id, arquivo, "certificado.pfx", "senha-do-certificado");
// Pausar / retomar
await client.empresas.pausar(empresa.id);
await client.empresas.retomar(empresa.id);
// Solicitar remoção (primeiro passo — a confirmação final é feita pelo painel)
await client.empresas.remover(empresa.id);GET /documents usa paginação por cursor — passe proximoCursor de volta em cursor na próxima chamada:
let cursor: string | undefined;
do {
const pagina = await client.documentos.listar({ cursor, limite: 100 });
for (const nota of pagina.itens) {
console.log(nota.numero, nota.valorServicos, nota.situacao);
}
cursor = pagina.proximoCursor ?? undefined;
} while (cursor);Ou use o helper paginarTodos, que faz esse loop por você:
for await (const nota of client.documentos.paginarTodos({ situacao: "AUTORIZADA" })) {
console.log(nota.numero);
}Detalhe de uma nota (inclui XML original e linha do tempo de eventos):
const detalhe = await client.documentos.obter(nota.id);
console.log(detalhe.eventos);const consumo = await client.obterConsumo();
if (consumo.temCicloAtivo) {
console.log(`${consumo.eventosIncluidos}/${consumo.franquiaEventos} eventos usados neste ciclo`);
}Toda chamada que falha lança IntegroBRApiError, com statusCode, messages (sempre um array, mesmo quando a API devolve uma string única) e helpers pros casos mais comuns:
import { IntegroBRApiError } from "@integrobr/nfse-sdk";
try {
await client.empresas.obter("id-que-nao-existe");
} catch (erro) {
if (erro instanceof IntegroBRApiError) {
if (erro.naoEncontrado) {
// 404 — não existe nesta conta, ou existe só no outro ambiente (sandbox/produção)
}
if (erro.rateLimited) {
// 429 — 120 requisições/minuto por chave; espere e tente de novo
}
console.error(erro.statusCode, erro.messages);
}
}Configure webhooks pelo painel (Painel → Webhooks) pra ser avisado em tempo real (NOTA_RECEBIDA, EVENTO_FISCAL_RECEBIDO) em vez de ficar consultando GET /documents. Cada entrega assina o corpo com HMAC-SHA256 no cabeçalho X-IntegroBR-Signature — sempre verifique antes de confiar no payload:
import { verificarAssinaturaWebhook } from "@integrobr/nfse-sdk";
import express from "express";
const app = express();
app.use(express.raw({ type: "application/json" })); // precisa do corpo BRUTO, não parseado
app.post("/webhooks/integrobr", (req, res) => {
const assinatura = req.header("X-IntegroBR-Signature") ?? "";
const valido = verificarAssinaturaWebhook(req.body, assinatura, process.env.INTEGROBR_WEBHOOK_SECRET!);
if (!valido) return res.status(401).send("assinatura inválida");
const payload = JSON.parse(req.body.toString("utf8"));
console.log(payload.tipo, payload.dados);
res.sendStatus(200);
});O segredo do webhook só é exibido uma vez, na criação (ou ao rotacionar) — guarde com o mesmo cuidado de uma senha.
120 requisições por minuto, por chave de API (janela fixa de 60s). Passar do limite devolve 429, exposto como erro.rateLimited.
MIT — veja LICENSE.