Skip to content

Geração de Relatórios

O servidor oferece três formas de gerar relatórios, cada uma com suporte a modo síncrono e assíncrono.

Formatos suportados

FormatoContent-TypeExtensão
pdfapplication/pdf.pdf
htmltext/html.html
csvtext/csv.csv
excelapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet.xlsx

Formatos inválidos retornam 400 invalid_format.

Endpoints de geração

Gerar de relatório salvo — POST /api/v1/reports/:id/generate

Permissão: canGenerate

Query params: ?format=pdf (default: pdf)

Body (opcional):

json
{
  "params": { "startDate": "2026-01-01" },
  "dataPayload": { "sales": [{ "product": "A", "amount": 100 }] }
}
CampoDescrição
paramsParâmetros do relatório (substituem variáveis no template)
dataPayloadDados inline (sobrescrevem datasources externos)

Resposta 200: Binário do arquivo com Content-Disposition: inline; filename="report-name.pdf".

StatusErroQuando
404not_foundRelatório não encontrado
413output_too_largeOutput excede MAX_OUTPUT_SIZE (default 200 MB)
422invalid_reportDefinição inválida
422generation_failedErro durante renderização

Gerar inline — POST /api/v1/generate

Permissão: canGenerate

Body:

json
{
  "definition": { "reportName": "Inline Report", "bands": [...] },
  "format": "pdf",
  "params": { "key": "value" },
  "dataPayload": { "key": [...] }
}
CampoObrigatórioDescrição
definitionSimObjeto JSON ou string JSON da definição
formatNãoFormato de saída (default: pdf)
paramsNãoParâmetros do relatório
dataPayloadNãoDados inline
StatusErroQuando
400validation_errordefinition ausente
413output_too_largeOutput excede limite
422invalid_definitionDefinição malformada
422generation_failedErro durante renderização

Gerar de pacote — POST /api/v1/generate/package

Permissão: canGenerate

Content-Type: multipart/form-data

PartTipoObrigatórioDescrição
package ou filebinárioSimBytes do pacote .sulfite
formattextoNãoFormato (default: pdf)
paramsJSON stringNãoParâmetros ({"key":"value"})

Limite de upload: 50 MB.

StatusErroQuando
400validation_errorPacote ausente
413output_too_largeOutput excede limite
422invalid_packagePacote malformado
422generation_failedErro durante renderização

Modo assíncrono

Para relatórios grandes ou lentos, use o modo assíncrono enviando o header:

Prefer: respond-async

O servidor retorna imediatamente com um job ID e o relatório é processado em background.

Resposta 202

json
{
  "jobId": "a1b2c3d4e5f6...",
  "status": "queued",
  "poll": "/api/v1/jobs/a1b2c3d4e5f6...",
  "stream": "/api/v1/jobs/a1b2c3d4e5f6.../stream",
  "output": "/api/v1/jobs/a1b2c3d4e5f6.../output"
}

Quando usar modo assíncrono

  • Relatórios com muitos dados (milhares de linhas)
  • Datasources externos lentos
  • Quando a UI precisa mostrar progresso em tempo real

Configuração da fila de jobs

ParâmetroValor
Máximo de jobs simultâneos4
Máximo de jobs pendentes100
Máximo pendentes por IP10
Memória máxima de resultados512 MB
Retenção de resultados30 minutos

Fila cheia — 429 queue_full

Se a fila de jobs está cheia, retorna 429 com error: "queue_full".

Jobs API

Propriedade de jobs

Jobs armazenam o subject do access token. Um requester só pode acessar seus próprios jobs. Superusers (sem subject) acessam todos. Acesso não autorizado retorna 404 (não 403) para evitar enumeração.

Status do job — GET /api/v1/jobs/:id

Resposta 200:

json
{
  "id": "hex-id",
  "status": "running",
  "progress": 45,
  "format": "pdf",
  "createdAt": "2026-04-06T12:00:00.000Z",
  "completedAt": null,
  "error": null,
  "reportName": "Sales Report",
  "outputSize": null,
  "subject": "user@example.com"
}
CampoPresença
statusSempre: queued, running, done, failed
progressSempre: 0–100
completedAtApenas quando terminal
errorApenas quando failed
outputSizeApenas quando done (bytes)

Stream SSE — GET /api/v1/jobs/:id/stream

Content-Type: text/event-stream

Recebe eventos em tempo real conforme o job progride:

event: queued
data: {"id":"...","status":"queued","progress":0}

event: running
data: {"id":"...","status":"running","progress":45}

event: done
data: {"id":"...","status":"done","progress":100}

Os nomes dos eventos correspondem ao status: queued, running, done, failed. O stream fecha quando o job atinge estado terminal.

Se o job já está completo quando o stream é aberto, um único evento é enviado e o stream fecha imediatamente.


Download do output — GET /api/v1/jobs/:id/output

Resposta 200: Binário do arquivo com o Content-Type do formato.

StatusErroQuando
404not_foundJob não encontrado
409not_readyJob não está done (retorna status atual)

Exemplo: geração assíncrona com SSE

javascript
// 1. Iniciar geração assíncrona
const res = await fetch('/api/v1/reports/abc123/generate?format=pdf', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ...',
    'Prefer': 'respond-async'
  }
});
const { jobId, stream } = await res.json();

// 2. Acompanhar progresso via SSE
const events = new EventSource(stream);
events.addEventListener('running', (e) => {
  const { progress } = JSON.parse(e.data);
  console.log(`Progresso: ${progress}%`);
});
events.addEventListener('done', async () => {
  events.close();
  // 3. Baixar resultado
  const output = await fetch(`/api/v1/jobs/${jobId}/output`, {
    headers: { 'Authorization': 'Bearer ...' }
  });
  const blob = await output.blob();
});
events.addEventListener('failed', (e) => {
  events.close();
  const { error } = JSON.parse(e.data);
  console.error('Falha:', error);
});

Sulfite do 🇧🇷 para o mundo © 2026 Rafael S. Pinheiro