Preview, execução e detalhe
Testa um card sem salvar, executa todos os cards de uma aba de uma vez e abre as linhas por trás de um ponto de gráfico.
Estas três rotas são as que devolvem dados. Todas exigem apenas view_reports. O resultado de qualquer card, de qualquer tipo, tem o mesmo formato, o dataset: uma lista de colunas e uma lista de linhas, com a unidade de cada coluna explícita. A regra mais importante: a falha de um card nunca vira erro HTTP. A rota responde 200 e o problema vem em meta.error daquele card, para o painel desenhar um estado de erro em vez de quebrar a tela inteira.
Preview
/api/v1/admin/dashboards/previewAdminExecutar uma especificação sem salvar
Corpo da requisição
specobjectobrigatórioperiodobjectPeríodo a aplicar aos cards com período inherit. Mesmo formato de period na especificação. Cards com período próprio ignoram este.
modestringobrigatórioinheritpresetfixedrelativepresetstringtodayyesterdaylast7last30last90thisMonthlastMonththisQuarterthisYearlastYearmode=preset.fromstringmode=fixed: primeiro dia, YYYY-MM-DD.tostringmode=fixed: último dia, YYYY-MM-DD.last_n_daysintegermode=relative: quantos dias para trás, até 1095.fieldstringentidade.campo). Padrão: o campo de data principal da entidade.limitintegerExemplo de requisição (preview)
curl -X POST "$BASE_URL/api/v1/admin/dashboards/preview" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"spec": {"spec_version": 1, "kind": "query", "title": "Receita por dia", "viz": {"type": "line", "snippet_id": "receita-por-dia", "number_format": {"style": "currency_cents"}}, "query": {"entity": "orders", "period": {"mode": "preset", "preset": "last7"}, "metrics": [{"id": "receita", "kind": "catalog", "ref": "orders.revenue"}], "dimensions": [{"id": "dia", "field": "orders.created_at", "grain": "day", "fill_gaps": true}], "order": [{"by": "dia", "dir": "asc"}]}}}'Resposta (preview)
{
"dataset": {
"columns": [
{ "key": "dia", "label": "dia", "type": "timestamp", "role": "dimension", "unit": "", "grain": "day" },
{ "key": "receita", "label": "receita", "type": "money_cents", "role": "metric", "unit": "cents" }
],
"rows": [
["2026-10-09T00:00:00Z", "13587"],
["2026-10-10T00:00:00Z", "45000"]
],
"meta": { "row_count": 2, "truncated": false, "limit": 100, "elapsed_ms": 2, "cached": false, "spec_hash": "0fac124f6f517da234ce129e4ef907afb4960daed43d8a48999548e42a89a6d9" }
}
}Campos da resposta
datasetobjectdataset.columnsarraydataset.columns[].keystringid da métrica ou da dimensão na especificação).dataset.columns[].labelstringkey.dataset.columns[].typestringmoney_cents, integer, percent_bps, timestamp, order_status, text e outros.dataset.columns[].rolestringdimensionmetricdimension (eixo) ou metric (valor).dataset.columns[].unitstringcents (centavos), bps (pontos-base) ou vazio. Diz se a UI precisa dividir por 100.dataset.columns[].grainstringdataset.rowsarraycolumns. Números podem chegar como número JSON ou como texto numérico ("13587"): converta nos dois casos.dataset.metaobjectdataset.meta.row_countintegerdataset.meta.truncatedbooleantrue se o limite cortou linhas.dataset.meta.limitintegerdataset.meta.elapsed_msintegerdataset.meta.cachedbooleantrue se veio do cache.dataset.meta.spec_hashstringdataset.meta.warningsarraydataset.meta.errorobjectcode (código estável) e message_key (chave de tradução). A falha de um card NÃO vira erro HTTP.compiledobjectmanage_reports e só em cards query. Não faz parte do contrato estável: não dependa dele.Erros (preview)
| Status | Quando | Corpo |
|---|---|---|
| 401 | Sem token, token inválido ou expirado. | {"error":"missing authorization header"} |
| 403 | O usuário não tem o privilégio exigido. | {"error":"insufficient privileges","required":"view_reports"} |
| 400 | Corpo inválido: não é JSON, tem campo desconhecido (a leitura rejeita campos que a API não conhece) ou passa de 1 MB. | {"error":"invalid request body","detail":"json: unknown field \"foo\""} |
| 403 | Especificação kind: sql sem o privilégio manage_reports. | {"error":"manage_reports required for kind=\"sql\" cards"} |
| 422 | Especificação inválida. | {"error":"invalid spec","detail":"unknown_field: ..."} |
| 500 | Falha inesperada no servidor. | {"error":"failed to ..."} |
Executar a aba
/api/v1/admin/dashboards/{id}/runAdminExecutar todos os cards de uma aba
Parâmetros de caminho
iduuidobrigatórioCorpo da requisição
periodobjectPeríodo a aplicar aos cards com período inherit. Mesmo formato de period na especificação. Cards com período próprio ignoram este.
modestringobrigatórioinheritpresetfixedrelativepresetstringtodayyesterdaylast7last30last90thisMonthlastMonththisQuarterthisYearlastYearmode=preset.fromstringmode=fixed: primeiro dia, YYYY-MM-DD.tostringmode=fixed: último dia, YYYY-MM-DD.last_n_daysintegermode=relative: quantos dias para trás, até 1095.fieldstringentidade.campo). Padrão: o campo de data principal da entidade.card_idsarrayrefreshbooleanpadrão: falsetrue ignora o cache de resultados.Exemplo de requisição (run)
curl -X POST "$BASE_URL/api/v1/admin/dashboards/56a570ca-f6a1-4483-ba89-55bcea0b5c4a/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"card_ids": ["f4368e4f-e696-4a57-9ca8-a026ba5ad694"], "period": {"mode": "preset", "preset": "last90"}, "refresh": false}'Resposta (run)
{
"results": [
{
"card_id": "f4368e4f-e696-4a57-9ca8-a026ba5ad694",
"status": "ok",
"dataset": {
"columns": [
{ "key": "status", "label": "status", "type": "order_status", "role": "dimension", "unit": "" },
{ "key": "pedidos", "label": "pedidos", "type": "integer", "role": "metric", "unit": "" }
],
"rows": [ ["C", 1630], ["S", 650], ["O", 145] ],
"meta": { "row_count": 3, "truncated": false, "limit": 20, "elapsed_ms": 7, "cached": false, "spec_hash": "909130ef2a69eb9d303cbad672e6197dcafab6653d010c0223462d25a0dbd00a" }
}
},
{ "card_id": "4ffa2776-0000-4000-8000-000000000002", "status": "native" },
{ "card_id": "52ff7b72-0000-4000-8000-000000000003", "status": "error", "error": { "code": "invalid_stored_spec", "message_key": "admin.dashboards.err.invalid_stored_spec" } }
],
"meta": { "elapsed_ms": 22, "concurrency": 4 }
}Campos da resposta
resultsarraycard_id.results[].card_iduuidresults[].statusstringokerrornativeok (veja dataset), error (veja error) ou native (card de componente: não há dado, o painel o desenha sozinho).results[].datasetobjectstatus: ok.results[].errorobjectcode e message_key do problema. Só com status: error.meta.elapsed_msintegermeta.concurrencyintegerErros (run)
| Status | Quando | Corpo |
|---|---|---|
| 401 | Sem token, token inválido ou expirado. | {"error":"missing authorization header"} |
| 403 | O usuário não tem o privilégio exigido. | {"error":"insufficient privileges","required":"view_reports"} |
| 404 | A aba não existe, não é sua ou não está visível para você. A API responde 404 em vez de 403 para não revelar que o id existe. | {"error":"dashboard not found"} |
Detalhe (drill-down)
/api/v1/admin/dashboards/cards/{cardId}/drilldownAdminAbrir as linhas por trás de um ponto de gráfico
Parâmetros de caminho
cardIduuidobrigatórioCorpo da requisição
dimension_valuesobjectobrigatórioid de cada dimensão do card para o valor clicado ({ "status": "O" }). Dimensões de data aceitam o início do período (RFC 3339 ou YYYY-MM-DD), que vira um intervalo conforme a granularidade. Dimensões não citadas não filtram.periodobjectPeríodo a aplicar aos cards com período inherit. Mesmo formato de period na especificação. Cards com período próprio ignoram este.
modestringobrigatórioinheritpresetfixedrelativepresetstringtodayyesterdaylast7last30last90thisMonthlastMonththisQuarterthisYearlastYearmode=preset.fromstringmode=fixed: primeiro dia, YYYY-MM-DD.tostringmode=fixed: último dia, YYYY-MM-DD.last_n_daysintegermode=relative: quantos dias para trás, até 1095.fieldstringentidade.campo). Padrão: o campo de data principal da entidade.pageintegerpadrão: 1per_pageintegerpadrão: 25langstringpt-BR ou en). Sem isso, usa Accept-Language e depois pt-BR.Exemplo de requisição (drilldown)
curl -X POST "$BASE_URL/api/v1/admin/dashboards/cards/f4368e4f-e696-4a57-9ca8-a026ba5ad694/drilldown" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"dimension_values": {"status": "O"}, "per_page": 2, "lang": "en"}'Resposta (drilldown)
{
"dataset": {
"columns": [
{ "key": "dd_status", "label": "Status", "type": "order_status", "role": "dimension", "unit": "" },
{ "key": "dd_created_at", "label": "Created at", "type": "timestamp", "role": "dimension", "unit": "" },
{ "key": "dd_total", "label": "Total", "type": "money_cents", "role": "dimension", "unit": "cents" },
{ "key": "dd_order_number", "label": "Order", "type": "text", "role": "dimension", "unit": "" }
],
"rows": [
["O", "2026-10-10T07:03:38Z", 22500, "PEDIDO-20261010-0003"],
["O", "2026-10-09T08:51:21Z", 13587, "PEDIDO-20261009-0002"]
],
"meta": { "row_count": 2, "truncated": false, "limit": 2, "elapsed_ms": 1, "cached": false }
},
"entity": "orders",
"page": 1,
"per_page": 2,
"has_more": true,
"filters": { "orders.status": "O" },
"period_from": "2026-09-12",
"period_to": "2026-10-11"
}Campos da resposta
datasetobjectdd_.entitystringpageintegerper_pageintegerhas_morebooleantrue se há uma próxima página.filtersobjectorders.status), não pelo id da dimensão.period_fromstringYYYY-MM-DD.period_tostringErros (drilldown)
| Status | Quando | Corpo |
|---|---|---|
| 401 | Sem token, token inválido ou expirado. | {"error":"missing authorization header"} |
| 403 | O usuário não tem o privilégio exigido. | {"error":"insufficient privileges","required":"view_reports"} |
| 400 | Corpo inválido: não é JSON, tem campo desconhecido (a leitura rejeita campos que a API não conhece) ou passa de 1 MB. | {"error":"invalid request body","detail":"json: unknown field \"foo\""} |
| 404 | O card não existe ou a aba dele não é sua. | {"error":"card not found"} |
| 400 | O card não é do tipo query. | {"error":"drilldown_requires_query_card"} |
| 422 | Entidade fora do catálogo, sem colunas projetáveis, ou falha ao montar o detalhe. | {"error":"drilldown_no_columns"} |
| 500 | Falha inesperada no servidor. | {"error":"failed to ..."} |