Cards: criar, alterar, apagar, layout
Cria, altera e apaga cards de uma aba e reorganiza a posição e o tamanho de todos de uma vez.
Um card é um número ou gráfico dentro de uma aba. Ele é definido pela sua especificação e posicionado numa grade de 3 colunas. Estas rotas exigem manage_dashboards e acesso de escrita à aba. A especificação é validada por inteiro antes de gravar: um card que não compila nunca é salvo.
Criar card
/api/v1/admin/dashboards/{id}/cardsAdminCriar um card na aba
Parâmetros de caminho
iduuidobrigatórioId da aba (UUID).
Corpo da requisição
specobjectobrigatórioA especificação do card. Obrigatório.
grid_rowintegerpadrão: 0Linha da grade, a partir de
0.grid_colintegerpadrão: 0Coluna, de
0 a 2. Fora disso o banco recusa e a resposta é 500.widthintegerpadrão: 1Largura em colunas, 1 a 3. Valores fora do intervalo (inclusive omitido, que vale 0) são ajustados para o limite mais próximo, então o padrão efetivo é 1.
heightintegerpadrão: 1Altura em linhas, 1 a 3, com o mesmo ajuste.
orderintegerpadrão: 0Ordem de exibição.
Exemplo de requisição (criar)
bash
curl -X POST "$BASE_URL/api/v1/admin/dashboards/8da0f9c1-b05b-4809-b07d-0b36573489e7/cards" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"spec": {"spec_version": 1, "kind": "query", "title": "Receita", "viz": {"type": "kpi", "snippet_id": "receita-semana", "number_format": {"style": "currency_cents"}}, "query": {"entity": "orders", "period": {"mode": "inherit"}, "metrics": [{"id": "receita", "kind": "catalog", "ref": "orders.revenue"}]}}, "grid_row": 0, "grid_col": 0, "width": 1, "height": 1, "order": 0}'Resposta (criar)
201Card criado
json
{
"id": "dd3470e6-dc16-4b41-9393-23797b50d7ba",
"dashboard_id": "8da0f9c1-b05b-4809-b07d-0b36573489e7",
"spec": {
"spec_version": 1,
"kind": "query",
"title": "Receita",
"viz": { "type": "kpi", "snippet_id": "receita-semana", "number_format": { "style": "currency_cents" } },
"query": {
"entity": "orders",
"period": { "mode": "inherit" },
"metrics": [ { "id": "receita", "kind": "catalog", "ref": "orders.revenue", "status_scope": { "mode": "store_default", "include": ["Paid", "shipped", "delivered"] } } ]
}
},
"spec_version": 1,
"kind": "Q",
"title": "Receita",
"grid_row": 0,
"grid_col": 0,
"width": 1,
"height": 1,
"order": 0,
"status": "A",
"source": "M",
"created_by": "d4613dde-ac7a-431c-9232-50d8cd6ad5fc",
"created_at": "2026-10-11T21:10:41.552Z",
"updated_at": "2026-10-11T21:10:41.552Z"
}Campos da resposta
iduuidId do card.
dashboard_iduuidAba dona do card.
specobjectA especificação do card, devolvida como foi gravada (com a classificação de status materializada em métricas de receita). Veja Especificação do card.
spec_versionintegerVersão do formato da especificação. Hoje só
1.kindstringQSCVTTipo, derivado de
spec.kind: Q consulta, S SQL livre, C componente nativo, V serviço de relatório, T texto.titlestringTítulo do card (cópia de
spec.title).grid_rowintegerLinha da grade, a partir de
0.grid_colintegerColuna da grade, de
0 a 2 (a grade tem 3 colunas).widthintegerLargura em colunas, de 1 a 3.
heightintegerAltura em linhas, de 1 a 3.
orderintegerOrdem de exibição.
statusstringADXA ativo, D rascunho, X reservado. Só cards ativos entram na execução da aba.sourcestringMITOrigem:
M manual, I inserido por insight, T criado a partir de modelo.template_slugstring|nullModelo de origem, quando
source é T.approved_by_user_iduuid|nullQuem aprovou, só em cards de SQL livre.
approved_atstring|nullQuando foi aprovado.
created_byuuid|nullQuem criou.
created_atstringCriação, RFC 3339.
updated_atstringÚltima alteração, RFC 3339.
Erros (criar)
| 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":"manage_dashboards"} |
| 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 | 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"} |
| 403 | Card kind: sql sem o privilégio manage_reports. | {"error":"manage_reports required for kind=\"sql\" cards"} |
| 422 | Especificação inválida ou referência inexistente no catálogo. | {"error":"invalid card spec","detail":"unknown_field: entity \"orders\" has no catalog metric \"nope\""} |
| 500 | Falha ao gravar, ou grid_col fora de 0 a 2. | {"error":"failed to persist card"} |
Alterar card
/api/v1/admin/dashboards/{id}/cards/{cardId}AdminAlterar um card
Parâmetros de caminho
iduuidobrigatórioId da aba (UUID).
cardIduuidobrigatórioId do card (UUID).
Corpo da requisição
specobjectobrigatórioA especificação do card. Obrigatório.
grid_rowintegerpadrão: 0Linha da grade, a partir de
0.grid_colintegerpadrão: 0Coluna, de
0 a 2. Fora disso o banco recusa e a resposta é 500.widthintegerpadrão: 1Largura em colunas, 1 a 3. Valores fora do intervalo (inclusive omitido, que vale 0) são ajustados para o limite mais próximo, então o padrão efetivo é 1.
heightintegerpadrão: 1Altura em linhas, 1 a 3, com o mesmo ajuste.
orderintegerpadrão: 0Ordem de exibição.
Exemplo de requisição (alterar)
bash
curl -X PUT "$BASE_URL/api/v1/admin/dashboards/8da0f9c1-b05b-4809-b07d-0b36573489e7/cards/dd3470e6-dc16-4b41-9393-23797b50d7ba" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"spec": {"spec_version": 1, "kind": "query", "title": "Receita (30 dias)", "viz": {"type": "kpi", "snippet_id": "receita-semana", "number_format": {"style": "currency_cents"}}, "query": {"entity": "orders", "period": {"mode": "inherit"}, "metrics": [{"id": "receita", "kind": "catalog", "ref": "orders.revenue"}]}}, "grid_row": 0, "grid_col": 0, "width": 2, "height": 1, "order": 0}'Resposta (alterar)
200Card alterado
json
{
"id": "dd3470e6-dc16-4b41-9393-23797b50d7ba",
"dashboard_id": "8da0f9c1-b05b-4809-b07d-0b36573489e7",
"spec": {
"spec_version": 1,
"kind": "query",
"title": "Receita (30 dias)",
"viz": { "type": "kpi", "snippet_id": "receita-semana", "number_format": { "style": "currency_cents" } },
"query": {
"entity": "orders",
"period": { "mode": "inherit" },
"metrics": [ { "id": "receita", "kind": "catalog", "ref": "orders.revenue", "status_scope": { "mode": "store_default", "include": ["Paid", "shipped", "delivered"] } } ]
}
},
"spec_version": 1,
"kind": "Q",
"title": "Receita (30 dias)",
"grid_row": 0,
"grid_col": 0,
"width": 1,
"height": 1,
"order": 0,
"status": "A",
"source": "M",
"created_by": "d4613dde-ac7a-431c-9232-50d8cd6ad5fc",
"created_at": "2026-10-11T21:10:41.552Z",
"updated_at": "2026-10-11T21:10:41.552Z"
}Campos da resposta
(objeto)objectO card alterado, com os campos de Criar card. Este PUT substitui a especificação e o layout inteiros: reenvie tudo, não só o que mudou.
Erros (alterar)
| 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":"manage_dashboards"} |
| 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"} |
| 403 | Card kind: sql sem manage_reports. | {"error":"manage_reports required for kind=\"sql\" cards"} |
| 422 | Especificação inválida. | {"error":"invalid card spec","detail":"..."} |
| 500 | Falha inesperada no servidor. | {"error":"failed to ..."} |
Apagar card
/api/v1/admin/dashboards/{id}/cards/{cardId}AdminApagar um card
Parâmetros de caminho
iduuidobrigatórioId da aba (UUID).
cardIduuidobrigatórioId do card (UUID).
Exemplo de requisição (apagar)
bash
curl -X DELETE "$BASE_URL/api/v1/admin/dashboards/8da0f9c1-b05b-4809-b07d-0b36573489e7/cards/dd3470e6-dc16-4b41-9393-23797b50d7ba" \
-H "Authorization: Bearer $TOKEN"Resposta (apagar)
200Card apagado
json
{
"message": "card deleted"
}Campos da resposta
messagestringTexto fixo de confirmação, em inglês.
Erros (apagar)
| 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":"manage_dashboards"} |
| 404 | O card não existe ou a aba dele não é sua. | {"error":"card not found"} |
| 500 | Falha inesperada no servidor. | {"error":"failed to ..."} |
Reorganizar layout
/api/v1/admin/dashboards/{id}/layoutAdminReorganizar o layout da aba
Parâmetros de caminho
iduuidobrigatórioId da aba (UUID).
Corpo da requisição
versionintegerO
version da aba que você leu por último. Se for diferente do atual, a gravação é recusada com 409. 0 ou omitido pula a conferência.itemsarrayobrigatórioPosição de cada card a mover.
card_iduuidobrigatórioId do card. Precisa pertencer à aba.
grid_rowintegerobrigatórioLinha.
grid_colintegerobrigatórioColuna, 0 a 2.
widthintegerobrigatórioLargura. Valores fora de 1 a 3 são ajustados para o limite mais próximo.
heightintegerobrigatórioAltura, com o mesmo ajuste.
Exemplo de requisição (layout)
bash
curl -X PUT "$BASE_URL/api/v1/admin/dashboards/8da0f9c1-b05b-4809-b07d-0b36573489e7/layout" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"version": 1791753037137, "items": [{"card_id": "dd3470e6-dc16-4b41-9393-23797b50d7ba", "grid_row": 1, "grid_col": 1, "width": 2, "height": 1}]}'Resposta (layout)
200Layout gravado
json
{
"message": "layout updated",
"version": 1791753046998
}409Alguém salvou antes de você
json
{
"version": 1791753046998,
"items": [
{ "card_id": "dd3470e6-dc16-4b41-9393-23797b50d7ba", "grid_row": 1, "grid_col": 1, "width": 2, "height": 1, "order": 0 }
]
}Campos da resposta
versionintegerNo 200, a versão nova: use-a na próxima gravação. No 409, a versão atual do servidor.
messagestringlayout updated (só no 200).itemsarraySó no 409: o layout atual (
card_id, grid_row, grid_col, width, height, order) para você decidir entre manter o seu ou adotar este.Erros (layout)
| 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":"manage_dashboards"} |
| 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 | 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"} |
| 500 | Um card_id não pertence à aba (ou grid_col fora de 0 a 2). Nada é aplicado; hoje isso volta como 500, não 404. | {"error":"failed to update layout","detail":"..."} |
| 409 | version diferente da atual. Nada é aplicado. | {"version":1791753046998,"items":[...]} |
| 500 | Falha inesperada no servidor. | {"error":"failed to ..."} |