integrobr-nfse-sdk (Ruby)

Cliente oficial Ruby 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.

Instalação

gem install integrobr-nfse-sdk

Ou no Gemfile:

gem "integrobr-nfse-sdk"

Uso rápido

require "integrobr/nfse_sdk"

client = Integrobr::NfseSdk::Client.new(api_key: ENV.fetch("INTEGROBR_API_KEY"))

conta = client.obter_conta
puts "#{conta['nome']} #{conta['ambiente']}" # "Empresa Exemplo LTDA PRODUCAO"

empresas = client.listar_empresas
notas = client.listar_documentos(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.

Empresas (CNPJs monitorados)

# Listar (GET /companies não é paginado)
empresas = client.listar_empresas

# Cadastrar um CNPJ novo
empresa = client.criar_empresa(cnpj: "12345678000195", nomeExibicao: "Filial São Paulo")

# Enviar o certificado A1 (.pfx/.p12)
client.enviar_certificado(empresa["id"], "/caminho/para/certificado.pfx", "senha-do-certificado")

# Pausar / retomar
client.pausar_empresa(empresa["id"])
client.retomar_empresa(empresa["id"])

# Solicitar remoção (primeiro passo — a confirmação final é feita pelo painel)
client.remover_empresa(empresa["id"])

Documentos (notas fiscais)

GET /documents usa paginação por cursor — passe proximoCursor de volta em cursor na próxima chamada:

cursor = nil
loop do
  pagina = client.listar_documentos(cursor: cursor, limite: 100)
  pagina["itens"].each do |nota|
    puts "#{nota['numero']} #{nota['valorServicos']} #{nota['situacao']}"
  end
  break if pagina["proximoCursor"].nil?
  cursor = pagina["proximoCursor"]
end

Ou use paginar_documentos, que faz esse loop por você (com bloco, ou devolve um Enumerator sem bloco):

client.paginar_documentos(situacao: "AUTORIZADA") do |nota|
  puts nota["numero"]
end

# ou, como Enumerator:
primeiras_10 = client.paginar_documentos.first(10)

Detalhe de uma nota (inclui XML original e linha do tempo de eventos):

detalhe = client.obter_documento(nota["id"])
pp detalhe["eventos"]

Consumo do ciclo atual

consumo = client.obter_consumo
if consumo["temCicloAtivo"]
  puts "#{consumo['eventosIncluidos']}/#{consumo['franquiaEventos']} eventos usados neste ciclo"
end

Tratamento de erros

Toda chamada que falha levanta Integrobr::NfseSdk::ApiError, com status_code, mensagens (sempre um array, mesmo quando a API devolve uma string única) e predicados pros casos mais comuns:

begin
  client.obter_empresa("id-que-nao-existe")
rescue Integrobr::NfseSdk::ApiError => e
  if e.nao_encontrado?
    # 404 — não existe nesta conta, ou existe só no outro ambiente (sandbox/produção)
  end
  if e.rate_limited?
    # 429 — 120 requisições/minuto por chave; espere e tente de novo
  end
  warn "#{e.status_code}: #{e.mensagens.join(' ')}"
end

Webhooks

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-Signaturesempre verifique antes de confiar no payload:

require "integrobr/nfse_sdk"

# Exemplo com Sinatra — o importante é ler o corpo BRUTO, não decodificado
post "/webhooks/integrobr" do
  corpo_bruto = request.body.read
  assinatura = request.env["HTTP_X_INTEGROBR_SIGNATURE"] || ""

  valido = Integrobr::NfseSdk::Webhooks.verificar_assinatura(corpo_bruto, assinatura, ENV.fetch("INTEGROBR_WEBHOOK_SECRET"))
  halt 401, "assinatura inválida" unless valido

  payload = JSON.parse(corpo_bruto)
  puts payload["tipo"]
  status 200
end

O segredo do webhook só é exibido uma vez, na criação (ou ao rotacionar) — guarde com o mesmo cuidado de uma senha.

Limite de requisições

120 requisições por minuto, por chave de API (janela fixa de 60s). Passar do limite devolve 429, exposto como e.rate_limited?.

Desenvolvimento

bundle install
bundle exec rspec

Licença

MIT — veja LICENSE.