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
| Formato | Content-Type | Extensão |
|---|---|---|
pdf | application/pdf | .pdf |
html | text/html | .html |
csv | text/csv | .csv |
excel | application/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):
{
"params": { "startDate": "2026-01-01" },
"dataPayload": { "sales": [{ "product": "A", "amount": 100 }] }
}| Campo | Descrição |
|---|---|
params | Parâmetros do relatório (substituem variáveis no template) |
dataPayload | Dados inline (sobrescrevem datasources externos) |
Resposta 200: Binário do arquivo com Content-Disposition: inline; filename="report-name.pdf".
| Status | Erro | Quando |
|---|---|---|
404 | not_found | Relatório não encontrado |
413 | output_too_large | Output excede MAX_OUTPUT_SIZE (default 200 MB) |
422 | invalid_report | Definição inválida |
422 | generation_failed | Erro durante renderização |
Gerar inline — POST /api/v1/generate
Permissão: canGenerate
Body:
{
"definition": { "reportName": "Inline Report", "bands": [...] },
"format": "pdf",
"params": { "key": "value" },
"dataPayload": { "key": [...] }
}| Campo | Obrigatório | Descrição |
|---|---|---|
definition | Sim | Objeto JSON ou string JSON da definição |
format | Não | Formato de saída (default: pdf) |
params | Não | Parâmetros do relatório |
dataPayload | Não | Dados inline |
| Status | Erro | Quando |
|---|---|---|
400 | validation_error | definition ausente |
413 | output_too_large | Output excede limite |
422 | invalid_definition | Definição malformada |
422 | generation_failed | Erro durante renderização |
Gerar de pacote — POST /api/v1/generate/package
Permissão: canGenerate
Content-Type: multipart/form-data
| Part | Tipo | Obrigatório | Descrição |
|---|---|---|---|
package ou file | binário | Sim | Bytes do pacote .sulfite |
format | texto | Não | Formato (default: pdf) |
params | JSON string | Não | Parâmetros ({"key":"value"}) |
Limite de upload: 50 MB.
| Status | Erro | Quando |
|---|---|---|
400 | validation_error | Pacote ausente |
413 | output_too_large | Output excede limite |
422 | invalid_package | Pacote malformado |
422 | generation_failed | Erro durante renderização |
Modo assíncrono
Para relatórios grandes ou lentos, use o modo assíncrono enviando o header:
Prefer: respond-asyncO servidor retorna imediatamente com um job ID e o relatório é processado em background.
Resposta 202
{
"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âmetro | Valor |
|---|---|
| Máximo de jobs simultâneos | 4 |
| Máximo de jobs pendentes | 100 |
| Máximo pendentes por IP | 10 |
| Memória máxima de resultados | 512 MB |
| Retenção de resultados | 30 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:
{
"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"
}| Campo | Presença |
|---|---|
status | Sempre: queued, running, done, failed |
progress | Sempre: 0–100 |
completedAt | Apenas quando terminal |
error | Apenas quando failed |
outputSize | Apenas 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.
| Status | Erro | Quando |
|---|---|---|
404 | not_found | Job não encontrado |
409 | not_ready | Job não está done (retorna status atual) |
Exemplo: geração assíncrona com SSE
// 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);
});