Executando relatórios personalizados pela API
Os relatórios personalizados que você executa no menu Relatórios também podem ser executados pela API. Isso permite extrair os mesmos números em integrações e agentes de IA, sem abrir o sistema.
A geração é assíncrona: uma mutation apenas enfileira o processamento e retorna vazio. O resultado fica disponível para consulta em uma query separada, que você repete até ele ficar pronto (a técnica conhecida como polling). O fluxo completo tem três passos:
- Descubra o ID do relatório com a query
getReports. - Gere um identificador único (UUID) e dispare a geração com a mutation
prepareReportViewerReport. - Consulte
getReportViewerResultcom esse UUID a cada 2 segundos até o resultado chegar.
Antes de começar
Token: funciona com token de API e com token pessoal. Um token pessoal Somente leitura não serve aqui: prepareReportViewerReport é uma mutation e é bloqueada para esse nível de acesso, como qualquer outra. Use um token com Leitura e escrita.
Empresa: o relatório executa no contexto da empresa atual do usuário do token. Com um token pessoal que acessa mais de uma empresa, confirme (ou troque) a empresa antes de executar, como mostrado em Trocando de empresa. O token de API é vinculado a uma única empresa e não troca de contexto.
Permissões: o usuário do token só executa relatórios que ele mesmo enxerga no menu Relatórios.
Descobrindo o relatório e seus filtros
A query getReports lista os relatórios visíveis para o usuário do token. O campo parameters descreve os filtros que cada relatório aceita:
curl -X POST https://app.facil123.com.br/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{"query": "query minhaEmpresa_getReports { getReports { id title parameters } }"}'
Guarde o id do relatório desejado: ele é o report_id do próximo passo.
Disparando a geração
Gere um UUID v4 (uuidgen no terminal, crypto.randomUUID() em Node). Ele identifica esta execução: é por ele que você buscará o resultado no passo seguinte, e deve ser novo a cada execução.
curl -X POST https://app.facil123.com.br/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{
"query": "mutation minhaEmpresa_prepareReport($stream: String!, $filters: JSON!, $report_id: ID!, $skip_cache: Boolean!) { prepareReportViewerReport(stream: $stream, filters: $filters, report_id: $report_id, skip_cache: $skip_cache) }",
"variables": {
"stream": "3f8a2c1e-5b7d-4e9a-8c3f-1d2e4b6a8c0f",
"filters": "{\"parameters\":{\"start_date\":\"2026-07-01\",\"end_date\":\"2026-07-31\"}}",
"report_id": "123",
"skip_cache": false
}
}'
stream: o UUID gerado por você. Guarde-o: é a chave da consulta do resultado.filters: uma string contendo JSON (não um objeto JSON). Para relatórios SQL, use{"parameters": {...}}, com as chaves descritas emparametersna querygetReports. Para relatórios por conjunto de dados, use{"parameters_for_dataset": {...}}. Relatório sem filtros: envie"{}".report_id: o ID descoberto emgetReports.skip_cache:trueforça o recálculo, ignorando o cache de 15 minutos do visualizador. Normalmente usefalse.
A resposta de sucesso é sempre {"data": {"prepareReportViewerReport": null}}. Isso é o esperado: a mutation retorna Void e o resultado vem no passo seguinte.
Buscando o resultado
Consulte getReportViewerResult com o mesmo UUID, repetindo a cada 2 segundos até sair do estado pendente. Esse ritmo (30 requisições por minuto) fica confortável dentro do limite de requisições, e a maioria dos relatórios fica pronta em poucos segundos.
curl -X POST https://app.facil123.com.br/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{
"query": "query minhaEmpresa_getReportResult($uuid: String!) { getReportViewerResult(uuid: $uuid) }",
"variables": { "uuid": "3f8a2c1e-5b7d-4e9a-8c3f-1d2e4b6a8c0f" }
}'
O campo retorna JSON livre, com quatro respostas possíveis.
Ainda processando (continue consultando):
{ "data": { "getReportViewerResult": { "status": "pending" } } }
Pronto (um item em data por painel do relatório):
{
"data": {
"getReportViewerResult": {
"data": [
{
"data": [
{ "produto": "Camiseta", "total": "49.90" }
],
"configuration": { "theme": "table" }
}
],
"generated_at": "2026-07-08T14:32:10.123-03:00"
}
}
}
Painéis podem falhar individualmente: no lugar de data e configuration, o item vem como { "error": ... } (por exemplo, um painel sem dados no período ou com SQL inválido). O restante do relatório é entregue mesmo assim, então trate painel a painel.
Erro de acesso (o relatório não existe ou o usuário do token não tem permissão sobre ele):
{
"data": {
"getReportViewerResult": {
"error": { "errors": { "base": ["Você não possui acesso a esse relatório ou ele foi excluído."] } }
}
}
}
NOT_FOUND (não existe nada gravado sob esse UUID para o seu usuário):
{
"errors": [
{ "message": "Record not found", "extensions": { "code": "NOT_FOUND" } }
],
"data": { "getReportViewerResult": null }
}
Entendendo a janela de 15 minutos
- O resultado (e o marcador
pending) fica disponível por 15 minutos a partir da gravação. Depois disso, a consulta devolveNOT_FOUND. - O resultado é visível apenas para o usuário do token que disparou a geração: outro token consultando o mesmo UUID recebe
NOT_FOUND, mesmo dentro da janela. generated_atpode ser anterior ao momento da mutation: execuções com os mesmos filtros nos últimos 15 minutos são servidas do cache do visualizador. Para forçar dados recém-calculados, envieskip_cache: true.
Reexecutando quando o resultado se perde
A entrega usa um cache com expiração, não uma fila com garantia de entrega. Em situações raras (falha de infraestrutura, pressão de memória no cache), o pending nunca vira resultado, ou a consulta devolve NOT_FOUND direto. Não há nova tentativa automática no servidor.
A resposta do cliente é a mesma nos dois casos: gere um novo UUID e execute a mutation de novo. Na prática, se o pending persistir por bem mais tempo que o normal para aquele relatório (alguns minutos), trate como perdido e reexecute, sem esperar os 15 minutos.
Atenção: cada execução pela API é auditada como qualquer execução de relatório, vinculada ao usuário do token. Veja Auditoria.
Copiando as instruções para o seu agente
O bloco abaixo resume o fluxo deste artigo. Cole nas instruções do seu agente de IA (o AGENTS.md, CLAUDE.md ou o campo de instruções da ferramenta que você usa), junto com as regras gerais de Usando a API com um agente de IA:
## Relatórios personalizados do Fácil123 via API
- Descubra o relatório com `getReports { id title parameters }`; `parameters` descreve os filtros.
- Gere um UUID v4 novo por execução e dispare
`prepareReportViewerReport(stream: <uuid>, filters:, report_id:, skip_cache:)`.
O retorno é sempre `null`; isso é sucesso, não erro.
- `filters` é uma STRING contendo JSON: `"{\"parameters\": {...}}"` para relatórios SQL,
`"{\"parameters_for_dataset\": {...}}"` para relatórios por conjunto de dados,
`"{}"` quando não há filtros.
- Busque o resultado com `getReportViewerResult(uuid: <uuid>)` a cada 2 segundos
enquanto vier `{"status": "pending"}`.
- Resultado pronto: envelope `{"data": [um item por painel], "generated_at": ...}`.
Um painel pode vir como `{"error": ...}` sem invalidar os demais; trate painel a painel.
- `NOT_FOUND` no polling significa chave inexistente, expirada ou de outro usuário:
gere um novo UUID e repita a mutation. Não insista no mesmo UUID.
- O resultado expira em 15 minutos e é visível só para o usuário do token que executou.
- `generated_at` pode vir do cache dos últimos 15 minutos; use `skip_cache: true`
quando precisar de dados recém-calculados.
- A mutation é bloqueada para token pessoal somente leitura; use leitura e escrita.
- O relatório roda na empresa atual do usuário do token; confirme a empresa antes
de executar (`changeCurrentUserLicensee` troca, quando o token é pessoal).
Perguntas e respostas
1 - Posso usar um token pessoal somente leitura?
R: Não para disparar a geração: prepareReportViewerReport é uma mutation e é bloqueada nesse nível de acesso. Crie um token pessoal com Leitura e escrita ou use um token de API.
2 - A mutation retornou null. Deu erro?
R: Não. null é a resposta de sucesso: a mutation apenas enfileira a geração e o resultado vem pela query getReportViewerResult.
3 - Recebi NOT_FOUND no meio do polling. O que faço?
R: Gere um novo UUID e execute a mutation de novo. NOT_FOUND significa que não há nada gravado sob aquele UUID para o seu usuário: a chave expirou, se perdeu ou pertence a outro usuário.
4 - Por que o generated_at é anterior ao momento em que executei?
R: O visualizador guarda em cache execuções com os mesmos filtros por 15 minutos, e a API pode servir esse resultado. Envie skip_cache: true para forçar o recálculo.
5 - Posso executar o relatório de outra empresa?
R: O relatório roda na empresa atual do usuário do token. Com token pessoal, troque de empresa com changeCurrentUserLicensee antes de executar, como mostrado em Trocando de empresa. Com token de API, crie um token para cada empresa.