API de widgets de dashboards
Un dashboard contiene una cuadrícula de widgets. Cada widget tiene un ULID asignado por el servidor, un type de un catálogo cerrado, los campos de configuración de ese tipo, un width, un height (en todos los tipos salvo el título de sección), una position en la cuadrícula y, en todos los tipos salvo el título de sección, un alcance de proyectos.
Todo lo que describe esta página sigue las reglas del dashboard: el control por plan (403 forbidden sin un plan que incluya dashboards), 404 not_found para un dashboard que no puedes ver y 403 forbidden para un cambio de alguien que no creó el dashboard ni es propietario o administrador de la organización. Cualquiera que pueda ver un dashboard puede listar sus widgets.
Catálogo
Sección titulada «Catálogo»type | Campos de configuración | width por defecto | height por defecto |
|---|---|---|---|
section_title | Ninguno. title es obligatorio: el widget es un encabezado que divide la cuadrícula | full | Ninguno |
count | metric: test_cases, open_defects, active_runs, suites, flaky_cases | third | 2 |
created_count | metric: test_cases, defects, results, runs; rangeDays | third | 2 |
distribution | customFieldUlid: un campo personalizado de casos de prueba; chart: donut (por defecto) o bar | half | 4 |
time_series | metric: open_defects, result_events, case_events, run_pass_rate, run_average_duration; rangeDays; chart: auto (por defecto) o heatmap, solo para result_events y case_events, como máximo 365 días | two_thirds | 4 |
pass_rate | rangeDays; chart: gauge (por defecto) o bullet; target: un entero de 1 a 100 (por defecto 80), contra el que se mide la barra | third | 3 |
query | savedQueryUlid: la consulta guardada que dibuja, leída en vivo; render: table, bar, line, donut o value (por defecto null, la visualización de la propia consulta); sin filters | half | 4 |
rangeDayses7,14,30,90,180,365o730(por defecto30), contados hacia atrás desde el día en que se ve el widget. Cada widget guarda su propio rango, y todos los que ven el dashboard ven el mismo. Unheatmapadmite365días como máximo.- Un
distributionagrupa los casos de prueba por un campo personalizado de tiposelect_single,radio,checkbox,select_multiouser_picker, de sistema o creado por la organización. Un campo que no existe devuelve404 not_found; un campo de otro tipo, o un campo de defectos o de ejecuciones, devuelve422 validation_failed. widthesthird,half,two_thirdsofull.heightes la cantidad de filas de la cuadrícula que ocupa el widget, un entero de2a8. Unsection_titleno tiene altura y la rechaza.titleadmite hasta 200 caracteres. En todos los tipos salvosection_titlees opcional:nullmuestra el título por defecto del tipo.
Alcance de proyectos
Sección titulada «Alcance de proyectos»projects define qué proyectos cuenta un widget:
{ "mode": "all" }(por defecto): todos los proyectos que puede abrir quien ve el widget.{ "mode": "selected", "projectUlids": [...] }: de 1 a 50 proyectos indicados, sin repetir. Al modificar un widget, la lista puede estar vacía (ver más abajo).
Lo que cuenta un widget depende siempre de quién lo ve. Un miembro que no puede abrir un proyecto seleccionado (un proyecto privado al que no tiene acceso, un proyecto bloqueado o uno archivado) no lo ve en projects.projectUlids; projects.hiddenProjectCount le indica cuántos proyectos seleccionados quedan ocultos para él. Sus ULID nunca se devuelven.
Al agregar o modificar un widget, cada proyecto que indiques debe ser uno que puedas abrir; de lo contrario, 404 not_found indica el ULID y no se guarda nada. Modificar un alcance selected reemplaza solo los proyectos que puedes abrir: los proyectos seleccionados que no puedes ver se mantienen como están. Por eso puedes enviar projectUlids vacío para quitar todos los proyectos que puedes abrir mientras queden proyectos ocultos; un alcance selected que se quedaría sin ningún proyecto devuelve 422 validation_failed. Cambiar el alcance a all quita todos los proyectos seleccionados, incluidos los ocultos. Cuando se elimina un proyecto, sale de todos los widgets que lo seleccionaban.
Filtros
Sección titulada «Filtros»Todos los tipos salvo section_title pueden tener filters, que acotan su número para todos los que ven el dashboard. Solo quienes pueden editar el dashboard los modifican.
"filters": [ { "field": "assignee", "values": ["01J9Z3Q4M8R2T6V1W5X7Y9A0C2", "01J9Z3Q4M8R2T6V1W5X7Y9A0C3"] }, { "field": "custom_field", "customFieldUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BF", "values": ["01J9Z3Q4M8R2T6V1W5X7Y9A0C0"] }]- Como máximo 10 filtros, uno por
field(uno por campo personalizado), cada uno con 1 a 50 valores, sin repetir. - Los valores de un filtro se combinan con O; los distintos filtros, con Y. El ejemplo cuenta los defectos asignados a cualquiera de las dos personas y que tienen esa opción.
Los campos que ofrece un widget dependen de lo que cuenta su métrica:
| Cuenta | Widgets | field |
|---|---|---|
| Casos de prueba | count test_cases y flaky_cases, created_count test_cases, time_series case_events, distribution | author, suite, tag, milestone, custom_field |
| Defectos | count y time_series open_defects, created_count defects | reporter, assignee, milestone, tag, custom_field; status solo en created_count |
| Ejecuciones | count active_runs, created_count runs, time_series run_pass_rate y run_average_duration, pass_rate | author, environment, milestone, test_plan, tag, custom_field |
| Resultados | created_count results, time_series result_events | status, executed_by, environment, milestone |
Un widget count suites no acepta filtros. El entorno y el hito de un resultado son los de su ejecución. Un filtro suite también incluye los casos de las suites que están debajo.
Qué es cada valor:
author,reporter,assignee,executed_by: ULID de miembros.suite,tag,milestone,environment,test_plan: sus ULID. Las suites, los hitos, los entornos y los planes de prueba deben estar en proyectos que puedas abrir.status:open,in_progress,resolvedoclosedpara defectos;passed,failed,skippedoblockedpara resultados.custom_field:customFieldUlidindica un campo de la entidad contada (caso de prueba, defecto o ejecución) de tiposelect_single,radio,checkbox,select_multiouser_picker. Los valores son ULID de opciones, ULID de miembros parauser_picker, o"true"/"false"paracheckbox."false"coincide solo con los elementos que guardan un valor sin marcar.
Un filtro que la métrica no ofrece, o un valor que no indica nada que puedas usar (un miembro, etiqueta, campo u opción que no existe, o una suite, hito, entorno o plan de prueba de un proyecto que no puedes abrir), devuelve 422 validation_failed; details indica la ruta de cada valor rechazado y no se guarda nada. Si más adelante se elimina una entidad filtrada, su valor se mantiene en el widget, con etiqueta null, y no coincide con nada hasta que alguien que edita el dashboard lo quite.
Los filtros se guardan, y se devuelven, en un orden fijo y con sus valores ordenados, así que enviar los mismos filtros en otro orden no cambia nada.
/api/v1/dashboards/{dashboardUlid}/widgets200 con { "items": [...] }, todos los widgets por position. Un dashboard tiene como máximo 50 widgets, por lo que la lista no se pagina.
Agregar
Sección titulada «Agregar»/api/v1/dashboards/{dashboardUlid}/widgetsAgrega un widget después del último. El cuerpo lleva type, los campos de configuración del tipo y, de forma opcional, title, width, height, projects y filters (los tres últimos no van en un section_title). El cuerpo es estricto: un campo que el tipo no tiene devuelve 422 validation_failed. Un dashboard que ya tiene 50 widgets devuelve 409 conflict.
201 con el widget. Consulta Campos de la respuesta.
curl -sS -X POST \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"type":"time_series","metric":"run_pass_rate","rangeDays":90,"projects":{"mode":"selected","projectUlids":["01J9Z3Q4M8R2T6V1W5X7Y9A0BC"]}}' \ "https://app.probara.net/api/v1/dashboards/$DASHBOARD_ULID/widgets"Modificar
Sección titulada «Modificar»/api/v1/dashboards/{dashboardUlid}/widgets/{widgetUlid}Envía al menos uno de title, width, height, projects, metric, rangeDays, customFieldUlid, chart, target o filters. El type de un widget no cambia: quítalo y agrega otro. Un campo que el tipo del widget no tiene, o un valor que el tipo no acepta (por ejemplo, una métrica de count en un time_series), devuelve 422 validation_failed. Un título de sección conserva su título (null se rechaza) y no acepta height ni projects. Un cuerpo que no cambia nada devuelve el widget sin modificar.
filters reemplaza la lista completa; [] quita todos los filtros. Los valores que el widget ya guarda se mantienen aunque su entidad se haya eliminado; los valores nuevos se validan. Cambiar metric mientras el widget tiene un filtro que la nueva métrica no ofrece devuelve 422 validation_failed, salvo que el mismo cuerpo envíe filters.
200 con el widget.
Reordenar
Sección titulada «Reordenar»/api/v1/dashboards/{dashboardUlid}/widgets/orderEnvía { "widgetUlids": [...] } con cada widget del dashboard exactamente una vez, en el nuevo orden; cualquier otra lista devuelve 422 validation_failed. 200 con los widgets en su nuevo orden.
/api/v1/dashboards/{dashboardUlid}/widgets/{widgetUlid}204. Los widgets que estaban después suben una posición. Eliminar un dashboard quita sus widgets.
Widgets de consulta
Sección titulada «Widgets de consulta»Un widget query dibuja una consulta guardada en vivo, así que una edición de la consulta llega al widget. Crea uno con "type":"query", savedQueryUlid (una consulta guardada que puedas ver) y, opcionalmente, render para reemplazar la visualización de la propia consulta (null, por defecto, la conserva). Recibe width, height y projects como cualquier widget de datos, y no recibe filters: el texto de la consulta decide sus filas. Una modificación puede apuntarlo a otra consulta (savedQueryUlid) o cambiar su render. Un widget cuya consulta guardada se eliminó conserva su lugar con savedQueryUlid en null; un editor lo quita.
Sus datos son la consulta guardada ejecutada con tu propio acceso a los proyectos y limitada a los proyectos del widget: savedQueryUlid, savedQueryName, queryDeleted, canOpenQuery (si puedes abrir la consulta, que es cuando aplica un enlace a ella), render (el dibujo en uso), scope (los mismos conteos que reporta cualquier widget de datos) y result, la primera página de la respuesta de la consulta en la forma que responde el endpoint de consultas: filas, o grupos cuando la consulta agrega. Quien puede ver el dashboard ve esa página aunque la consulta sea privada para otra persona, sin su texto. Una consulta eliminada responde queryDeleted: true con savedQueryUlid, savedQueryName, render y result en null.
Los registros de un widget query no se listan aquí: sus filas se leen en su consulta guardada, así que el endpoint de registros responde 422 validation_failed.
Datos de un widget
Sección titulada «Datos de un widget»/api/v1/dashboards/{dashboardUlid}/widgets/{widgetUlid}/dataDevuelve lo que muestra un widget. Cualquiera que pueda ver el dashboard puede leerlo, y los números siempre se calculan para ti: solo cubren los proyectos del alcance del widget que puedes abrir. Los propietarios y administradores de la organización abren todos los proyectos que no están bloqueados. Los proyectos bloqueados y archivados nunca se cuentan.
{ "type": "created_count", "widgetUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BD", "metric": "defects", "scope": { "includedProjectCount": 3, "hiddenProjectCount": 1, "emptySelection": false }, "range": { "requestedDays": 365, "effectiveDays": 90, "from": 1751976000000, "to": 1759752000000, "clampedByRetention": true }, "data": { "value": 42 }}scope indica si el número está completo:
includedProjectCount: cuántos proyectos se contaron.hiddenProjectCount: cuántos quedaron fuera. En un widgetselected, los proyectos seleccionados que no puedes abrir (el mismo número queprojects.hiddenProjectCounten el widget). En un widgetall, los proyectos de la organización que no están archivados y que no puedes abrir, incluidos los bloqueados. Si es mayor que 0, el valor es parcial: muéstralo como tal, no como el total.emptySelection:trueen un widgetselectedcuyos proyectos se eliminaron todos. Su valor es0.
Qué cuenta cada métrica:
type | metric | data.value |
|---|---|---|
count | test_cases | Casos de prueba no archivados |
count | open_defects | Defectos con estado open o in_progress |
count | active_runs | Ejecuciones que no están cerradas |
count | suites | Suites |
count | flaky_cases | Casos de prueba no archivados con Is flaky marcado |
created_count | test_cases | Casos de prueba creados en el rango, incluidos los que se archivaron después |
created_count | defects | Defectos creados en el rango |
created_count | results | Resultados registrados en el rango, según su hora de ejecución |
created_count | runs | Ejecuciones iniciadas en el rango |
Un widget created_count también devuelve range, la ventana que contó: desde from (incluido) hasta to (excluido), en milisegundos desde epoch, terminando en el momento de la solicitud. La ventana es el rangeDays del widget, recortado a la retención del historial de tu plan: las ejecuciones cerradas más antiguas se eliminan, así que una ventana más larga contaría de menos. effectiveDays es la duración que realmente se contó, y clampedByRetention es true cuando la retención la recortó.
Distribución
Sección titulada «Distribución»Un widget distribution cuenta los casos de prueba del alcance que no están archivados y pertenecen a un proyecto donde se aplica su campo, agrupados por el valor que tienen en ese campo.
{ "type": "distribution", "widgetUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BE", "customFieldUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BF", "scope": { "includedProjectCount": 2, "hiddenProjectCount": 0, "emptySelection": false }, "data": { "fieldRemoved": false, "field": { "ulid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BF", "title": "Prioridad", "type": "select_single" }, "multiValue": false, "totalCases": 120, "buckets": [ { "kind": "option", "key": "01J9Z3Q4M8R2T6V1W5X7Y9A0C0", "label": "Alta", "color": "#a890e8", "count": 30 }, { "kind": "option", "key": "01J9Z3Q4M8R2T6V1W5X7Y9A0C1", "label": "Baja", "color": "#a89fb2", "count": 75 }, { "kind": "not_set", "key": null, "count": 15 } ] }}Cada grupo tiene un kind:
kind | key | Notas |
|---|---|---|
option | ULID de la opción | Campos de selección y de opción única. Se listan todas las opciones en el orden del campo, incluidas las vacías. label es el nombre en el idioma de la solicitud |
checkbox | true/false | Marcado y sin marcar |
user | ULID del usuario | Campos de usuario, de mayor a menor. formerMember es true cuando la persona dejó la organización; label es null cuando la organización ya no la conoce |
other | other | A partir de 20 grupos, los más pequeños se juntan en este; groupedCount indica cuántos |
not_set | null | Siempre al final: casos sin valor, o cuyo valor apunta a una opción eliminada |
totalCases es la cantidad de casos contados. En un campo de selección múltiple, multiValue es true: un caso cuenta una vez en cada opción que tiene, así que la suma de los grupos puede superar totalCases.
Si el campo se eliminó después de agregar el widget, la respuesta sigue siendo 200, con data igual a { "fieldRemoved": true, "buckets": [] }.
Serie temporal
Sección titulada «Serie temporal»Un widget pass_rate devuelve range y un único objeto data. value es la tasa de aprobación agregada de las ejecuciones cerradas en el rango, en porcentaje con un decimal: passed sobre executed, es decir, los casos aprobados, fallidos, bloqueados u omitidos. Se excluyen las ejecuciones abortadas, y value es null cuando no se ejecutó nada. closedRuns cuenta las ejecuciones que agrupa, y sus registros listan esas mismas ejecuciones:
{ "type": "pass_rate", "widgetUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BC", "scope": { "includedProjectCount": 2, "hiddenProjectCount": 0, "emptySelection": false }, "range": { "requestedDays": 30, "effectiveDays": 30, "from": 1756742400000, "to": 1759334400000, "clampedByRetention": false }, "data": { "value": 83.3, "passed": 10, "executed": 12, "closedRuns": 2 }}Un widget time_series devuelve range, recortado a la retención de tu plan igual que un widget created_count, dividido en intervalos:
{ "type": "time_series", "widgetUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BG", "metric": "run_pass_rate", "scope": { "includedProjectCount": 2, "hiddenProjectCount": 0, "emptySelection": false }, "range": { "requestedDays": 7, "effectiveDays": 7, "from": 1759147200000, "to": 1759752000000, "clampedByRetention": false }, "data": { "granularity": "day", "timeZone": "America/Argentina/Buenos_Aires", "buckets": [ { "start": 1759147200000, "end": 1759201200000 }, { "start": 1759201200000, "end": 1759287600000 } ], "series": [{ "key": "passRate", "values": [null, 83.3] }] }}(Abreviado: un rango de 7 días tiene 8 intervalos.)
granularityesdaypara rangos de hasta 31 días,weekhasta 186 días (las semanas empiezan el lunes) ymonthpara rangos más largos. Unheatmapes siempreday.- Los límites de los intervalos caen a la medianoche de
timeZone: la zona horaria de tu perfil, oUTCsi no elegiste ninguna. Dos personas pueden ver límites distintos en el mismo widget. Cuando cambia el horario de verano, un intervalo puede durar una hora más o una hora menos. bucketsson ventanas[start, end)contiguas, en milisegundos desde epoch; la primera empieza enrange.fromy la última termina enrange.to, así que ambas pueden ser parciales.seriestiene un valor por intervalo, en el mismo orden, para cada serie de la métrica:
metric | Series | Valor por intervalo |
|---|---|---|
open_defects | open | Defectos abiertos (open o in_progress) al final del intervalo. Los defectos eliminados nunca se cuentan |
result_events | passed, failed, skipped, blocked | Resultados registrados en el intervalo con ese estado, según su hora de ejecución |
case_events | created, edited | Casos de prueba creados, y ediciones guardadas: un guardado, una importación o una restauración. Los cambios hechos a través de un paso compartido no cuentan como ediciones |
run_pass_rate | passRate | De las ejecuciones cerradas en el intervalo (sin las abortadas), casos aprobados ÷ casos ejecutados, como porcentaje con un decimal. null si no se ejecutó nada |
run_average_duration | averageDurationMs | Tiempo promedio entre el inicio y el cierre de las ejecuciones cerradas en el intervalo (sin las abortadas; una ejecución cerrada antes de su inicio cuenta como 0). null si no se cerró ninguna |
La tasa de aprobación suma los casos de todas las ejecuciones del intervalo antes de dividir: una ejecución con 100 casos ejecutados pesa más que una con 2. Los casos ejecutados son los aprobados, fallidos, bloqueados y omitidos; no cuentan los casos sin probar ni los que esperan volver a probarse.
Errores
Sección titulada «Errores»Un section_title no tiene datos y devuelve 422 validation_failed. Un widget que no existe, o un dashboard que no puedes ver, devuelve 404 not_found.
Campos de la respuesta
Sección titulada «Campos de la respuesta»| Campo | Tipo | Notas |
|---|---|---|
ulid | string | El ULID del widget |
type | string | Consulta Catálogo |
title | string | null | Siempre es un string en un section_title |
width | string | third, half, two_thirds o full |
height | number | Filas de la cuadrícula, de 2 a 8; no aparece en un section_title |
position | number | Lugar en la cuadrícula, desde 0 |
| campos de configuración | — | Los del tipo del widget, por ejemplo metric y rangeDays |
projects | object | null | null en un section_title |
projects.mode | string | all o selected |
projects.projectUlids | string[] | Los proyectos seleccionados que tú puedes abrir; vacío con all |
projects.hiddenProjectCount | number | Proyectos seleccionados que no puedes abrir |
filters | object[] | No aparece en un section_title. Consulta Filtros |
filters[].values[].label | string | null | Ver más abajo |
createdAt, updatedAt | number | Milisegundos desde epoch; reordenar no cambia updatedAt |
Cada filtro de una respuesta repite field (y customFieldUlid, además de customFieldTitle y customFieldType, ambos null cuando el campo se eliminó) y lista sus valores como { "value", "label" }. label es el nombre del miembro, o el nombre de la suite, etiqueta, hito, entorno, plan de prueba u opción; los títulos de campos personalizados y los nombres de opciones siguen el idioma de la solicitud. Es null para un estado o un valor de casilla, para una entidad eliminada y para una entidad de un proyecto que no puedes abrir.
Cada cambio en un widget también actualiza el updatedAt del dashboard.
Registros de un widget
Sección titulada «Registros de un widget»/api/v1/dashboards/{dashboardUlid}/widgets/{widgetUlid}/recordsLista los registros detrás de un valor de un widget, de a una página por vez: los casos, defectos, ejecuciones, resultados o suites que cuenta el número. Sigue las mismas reglas que los datos del widget. Cualquiera que pueda ver el dashboard puede leerlos, y solo se listan los proyectos del alcance del widget que puedes abrir. El total es el valor que muestran los datos del widget para el mismo selector. En run_pass_rate y run_average_duration, total es la cantidad de ejecuciones detrás del punto, no la tasa de aprobación ni el promedio graficado.
Elige el valor con el selector que admite el widget:
| Widget | Selector | Lista |
|---|---|---|
count, created_count | ninguno: el valor completo | los registros que cuenta la métrica |
distribution | bucket: una key de data.buckets (el ULID de una opción o de una persona, true, false, not_set u other) | los casos de ese grupo |
time_series | bucketStart: un start de data.buckets, y series cuando la métrica tiene varias (result_events: passed, failed, skipped o blocked; case_events: created o edited) | los registros de ese intervalo y de esa serie |
En time_series, una métrica con una sola serie admite su propio nombre de serie, de forma opcional: open_defects admite open, run_pass_rate admite passRate y run_average_duration admite averageDurationMs. La serie se verifica con la métrica y no cambia nada; cualquier otra serie devuelve 422 validation_failed. En un widget heatmap, bucketStart nombra un día local, y series sigue las mismas reglas. Una celda cuenta todas las series juntas, así que los registros de una serie son solo una parte del recuento de ese día; la aplicación web no abre los registros de una celda.
GET /api/v1/dashboards/01J9Z3Q4M8R2T6V1W5X7Y9A0BD/widgets/01J9Z3Q4M8R2T6V1W5X7Y9A0BG/records?bucket=not_set&page=1&pageSize=50{ "kind": "test_case", "total": 2, "page": 1, "pageSize": 50, "items": [ { "kind": "test_case", "projectUlid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BK", "projectCode": "PAY", "projectName": "Payments", "ulid": "01J9Z3Q4M8R2T6V1W5X7Y9A0BM", "displayId": "PAY-41", "title": "Reembolsar una tarjeta", "suiteName": "Compra", "archived": false, "createdAt": 1759600000000, "revision": null } ]}kindes el tipo de cada elemento:test_case,defect,run,resultosuite.- Cada elemento trae lo que una fila necesita para mostrar y enlazar: su proyecto (
projectUlid,projectCode,projectName), suulidy sudisplayId. El identificador visible de un caso es el código de su proyecto y su número (PAY-41); el de un defecto esD-y su número; el de una ejecución esR-y su número; el de una suite es el código de su proyecto,Sy su número (PAY-S4). - Los elementos van del más reciente al más antiguo según la marca de tiempo que usa la métrica; si empatan, por ID. Las páginas empiezan en 1;
pageSizevale 50 por defecto y como máximo 200.
Los campos según el tipo:
kind | Campos |
|---|---|
test_case | title, suiteName, archived, createdAt, y revision (number, source, savedAt) en un registro edited de case_events; en los demás es null |
defect | title, status (el estado actual: un defecto que estaba abierto en el pasado pudo cerrarse después), createdAt, closedAt |
run | name, state, status, startedAt, closedAt, passed, executed, passRate (null si no se ejecutó nada), durationMs (null mientras la ejecución está abierta) |
result | status, executedAt, executedByName (null si la organización ya no conoce a quien lo ejecutó), runUlid, runDisplayId, runCaseUlid (null si el caso ya no está en la ejecución), testCaseUlid, testCaseDisplayId, testCaseTitle (el título con el que se ejecutó; los campos del caso son null una vez eliminado) |
suite | name, createdAt |
Un porcentaje de aprobación es la suma de passed de los elementos dividida entre la suma de executed, tal como lo calcula la serie. Un tiempo promedio es la media de durationMs de los elementos.
Un bucketStart identifica el intervalo del día, la semana o el mes local en el que cae. El primer intervalo empieza al inicio del rango, que se mueve con el tiempo, así que un inicio tomado de una respuesta anterior sigue encontrándolo. En una distribución de select_multi, un caso que tiene dos opciones de other es un solo registro, igual que en el conteo del grupo.
Errores:
422 validation_failedcuando el selector no corresponde al widget (unbucketen uncount, o una serie temporal sinbucketStart), y para unsection_title, que no tiene registros.404 not_foundcuando el grupo no es uno que muestre el widget (una opción agrupada enother), cuando el intervalo no es uno que muestre (unbucketStartanterior al rango), y para un widget o un dashboard que no puedes ver.403 forbiddencuando el plan no incluye dashboards.
Solo lectura: no se registra ningún evento de auditoría.
Eventos de auditoría
Sección titulada «Eventos de auditoría»Cada cambio registra un evento sobre el dashboard (tipo de entidad dashboard, con su nombre como etiqueta) con el widgetUlid y el type del widget en la metadata: dashboard.widget_added, dashboard.widget_updated (además de changedFields, los nombres de los campos que cambiaron, como title, width, height, projects, rangeDays o filters) y dashboard.widget_removed. Un reordenamiento registra dashboard.widgets_reordered con widgetUlids en su nuevo orden. Un cambio que no cambia nada no registra nada. Consulta Eventos de auditoría.
Las mismas operaciones están disponibles como las herramientas MCP list_dashboard_widgets, create_dashboard_widget, update_dashboard_widget, reorder_dashboard_widgets, delete_dashboard_widget, get_dashboard_widget_data y list_dashboard_widget_records, con las mismas reglas. Consulta la referencia de herramientas MCP.