Skip to main content
Banco de Horas é dos relatórios mais usados em integrações de RH e contabilidade — mostra saldos, movimentações, datas de prescrição por colaborador no período. Útil para:
  • Conferir banco antes de quitação em folha
  • Auditar banco vencido (Lei 13.467/2017)
  • Anexar em acordo coletivo

Padrão geral aplicável a qualquer relatório

Este fluxo serve para qualquer endpoint POST /relatorio/<tipo>, não só banco-horas:
1. POST /relatorio/<tipo>          → retorna { relatorioId }
2. GET /relatorio/{id}             → polling até "CONCLUIDO" ou "ERRO"
3. POST /relatorio/download        → retorna URL ou base64 do arquivo
4. (Cliente persiste arquivo localmente para auditoria histórica)

Implementação completa

async function gerarRelatorioBancoHoras(token, opts = {}) {
  const TOKEN = token || process.env.PONTUA_API_TOKEN
  const headers = {
    Authorization: `Bearer ${TOKEN}`,
    'Content-Type': 'application/json',
  }

  const params = {
    dataInicio: opts.dataInicio,        // ex: '2026-04-01'
    dataFim: opts.dataFim,              // ex: '2026-04-30'
    formato: opts.formato || 'PDF',     // PDF ou CSV
    // demais filtros opcionais conforme schema
  }

  console.log('1. Disparando geração...')
  const dispatchResp = await fetch(
    'https://api.pontua.com.br/relatorio/banco-horas',
    { method: 'POST', headers, body: JSON.stringify(params) },
  )
  if (!dispatchResp.ok) {
    const erro = await dispatchResp.json()
    throw new Error(`Dispatch falhou: ${erro.message}`)
  }
  const { relatorioId } = await dispatchResp.json()
  console.log(`   relatorioId: ${relatorioId}`)

  console.log('2. Polling até concluir (timeout 30 min)...')
  let status
  for (let attempt = 0; attempt < 60; attempt++) {
    const statusResp = await fetch(
      `https://api.pontua.com.br/relatorio/${relatorioId}`,
      { headers },
    )
    status = await statusResp.json()

    if (status.status === 'CONCLUIDO') {
      console.log(`   pronto após ${attempt + 1} tentativa(s)`)
      break
    }
    if (status.status === 'ERRO') {
      throw new Error(`Geração falhou: ${status.mensagemErro || JSON.stringify(status)}`)
    }

    // Backoff: 5s, 7.5s, 11s, 17s, ..., capped 60s
    const delay = Math.min(5000 * Math.pow(1.5, attempt), 60000)
    await new Promise((r) => setTimeout(r, delay))
  }

  if (status?.status !== 'CONCLUIDO') {
    throw new Error('Timeout aguardando geração de relatório')
  }

  console.log('3. Baixando...')
  const downloadResp = await fetch(
    'https://api.pontua.com.br/relatorio/download',
    { method: 'POST', headers, body: JSON.stringify({ relatorioId }) },
  )
  const download = await downloadResp.json()
  console.log(`   download disponível em: ${download.url || '[base64]'}`)

  return download
}

// Uso
const arquivo = await gerarRelatorioBancoHoras(TOKEN, {
  dataInicio: '2026-04-01',
  dataFim: '2026-04-30',
  formato: 'PDF',
})

Persistir o arquivo localmente

Você deve guardar uma cópia local do arquivo gerado por questões de auditoria (Portaria 671 exige retenção de 5 anos para registros, 10 anos para folha):
import fs from 'node:fs/promises'

async function persistirRelatorio(download, caminhoLocal) {
  // Se download.url é URL S3
  if (download.url) {
    const fileResp = await fetch(download.url)
    const buffer = Buffer.from(await fileResp.arrayBuffer())
    await fs.writeFile(caminhoLocal, buffer)
  }
  // Se download.conteudoBase64
  else if (download.conteudoBase64) {
    const buffer = Buffer.from(download.conteudoBase64, 'base64')
    await fs.writeFile(caminhoLocal, buffer)
  }

  console.log(`Arquivo salvo em ${caminhoLocal}`)
}

Filtros úteis

POST /relatorio/banco-horas aceita filtros que reduzem o tamanho e o tempo de geração:
FiltroQuando usar
colaboradorIdsRelatório de demitido ou subset específico
departamentoIdsRelatório por departamento (folha de gerente)
formato: 'CSV'Análise em planilha em vez de PDF
dataInicio / dataFimSempre — define o período de cálculo
Schema completo do payload em Referência da API.

Quando usar PDF vs CSV

CasoFormato
Anexar em recibo / arquivo do colaboradorPDF (assinado PAdES)
Análise em Excel / BICSV
Auditoria oficial / fiscalizaçãoPDF (tem assinatura digital)
Importar para outro sistemaCSV

Boas práticas

Rode em horário noturno

Geração de relatório do mês todo pode levar minutos. Faça em horário de baixa carga (22h-6h BRT).

Cache do relatorioId

Se o download falhar, não regenere. Reuse o mesmo relatorioId para tentar baixar de novo. Geração custa caro.

Filtre máximo possível

Não puxe banco-horas do ano todo se só precisa do mês corrente. Filtros antes = relatório menor + mais rápido.

Guarde 5+ anos

Por questões fiscais e Portaria 671, mantenha cópia local de cada relatório por no mínimo 5 anos. Não dependa só do Pontua.

Troubleshooting

Status fica em PROCESSANDO indefinidamente

Tipicamente significa fila acumulada ou erro na geração. Após 30 min sem mudar, abra chamado em tecnologia@pontua.com.br mencionando o relatorioId.

Relatório veio vazio

Filtros muito restritivos (ex.: período sem movimentação, departamento sem colaboradores). Repita sem filtros para confirmar que dados existem.

Erro “fechamento aberto”

Banco de Horas se baseia em saldos consolidados — se o período tem fechamento aberto, alguns valores podem não estar definitivos. Para relatório oficial, gere após POST /fechamento/concluir/{id}. Ver Fechamento.

Veja também