# API de dashboards

> Crea, lista, obtén, modifica y elimina los dashboards de la organización, y administra quién ve cada uno, a través de la API para integradores.

Un dashboard pertenece a la organización, no a un proyecto. Cada uno tiene un ULID asignado por el servidor, un nombre, una descripción opcional, un propietario (el miembro que lo creó) y una visibilidad: `public` (todos los miembros de la organización pueden verlo) o `private`.

Los dashboards están disponibles en los planes **Pro** y **Enterprise**. En cualquier otro plan, todos los endpoints de esta página, incluidas las lecturas, devuelven `403 forbidden`; `dashboardsEnabled` en [`GET /api/v1/orgs/current`](https://docs.probara.net/es/developers/api-reference/) lo indica de antemano. Bajar de plan no elimina nada: los dashboards de la organización vuelven tal como estaban cuando el plan vuelve a incluirlos.

Los widgets de un dashboard tienen sus propios endpoints: consulta [Widgets de dashboards](https://docs.probara.net/es/developers/api/dashboard-widgets/).

## Quién ve y quién modifica un dashboard

Un miembro ve un dashboard cuando se cumple alguna de estas condiciones:

- es `public` y no se le revocó su propio acceso;
- lo creó;
- se le otorgó acceso a él, o a uno de sus [grupos de usuarios](https://docs.probara.net/es/developers/api/user-groups/).

Los propietarios y administradores de la organización ven todos los dashboards. Un miembro cuyo propio acceso fue revocado no ve el dashboard, aunque sea público o se le haya otorgado a uno de sus grupos.

Solo quien creó el dashboard y los propietarios y administradores de la organización pueden renombrarlo, cambiar su descripción o eliminarlo, o consultar y modificar su [acceso](#acceso), sin importar qué otro acceso tenga un miembro. Si se elimina la cuenta de quien lo creó, el dashboard se conserva y solo los propietarios y administradores pueden modificarlo.

Las dos negativas se distinguen a propósito:

- un dashboard que no puedes ver responde **`404 not_found`** en todos los endpoints, igual que un ULID que no existe, de modo que nunca se revela que existe;
- un dashboard que puedes ver pero no modificar responde **`403 forbidden`** en `PATCH`, `PUT` y `DELETE`, y en todos los endpoints de [acceso](#acceso), incluidas las lecturas.

## Crear

`POST /api/v1/dashboards`

Requiere el permiso `dashboards.write` (que tienen `owner`/`admin`/`member` de forma predeterminada); un `viewer` recibe `403 forbidden`. Quien llama pasa a ser el propietario del dashboard. Envía `accessMode: "private"` para crear un dashboard que sea privado desde el principio; si no lo envías, empieza como `public`. Un dashboard privado puede empezar con sus miembros y grupos ya autorizados: el dashboard y sus accesos se crean juntos, o no se crea nada.

### Cuerpo de la solicitud

| Campo         | Requerido | Notas                                                                                                                                              |
| ------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | sí        | Sin espacios al inicio/fin, 1–200 caracteres                                                                                                       |
| `description` | no        | Hasta 2000 caracteres, u omitirlo/`null` para no tener una                                                                                         |
| `accessMode`  | no        | `public` (predeterminado) o `private`; se cambia después con [Reemplazar la configuración](#reemplazar-la-configuración) o desde [Acceso](#acceso) |
| `members`     | no        | ULID de miembros actuales de la organización; cada uno recibe un acceso `granted`; solo en privado                                                 |
| `groups`      | no        | ULID de grupos de usuarios; cada uno recibe acceso al dashboard; solo en privado                                                                   |

`members` y `groups` admiten como máximo 200 ULID cada uno, sin repetir, y deben ir vacíos u omitirse cuando el dashboard es público; si no, `422 validation_failed`. Un ULID que no es de un miembro actual, o de un grupo de usuarios de la organización, devuelve `404 not_found` y no se crea nada. Se acepta incluirte a ti, a un propietario o a un administrador, aunque ya ven el dashboard de todos modos. El cuerpo es estricto: cualquier otra clave devuelve `422 validation_failed`.

### Respuesta

`201` con el dashboard creado. Consulta [Campos de la respuesta](#campos-de-la-respuesta).

### Ejemplo

```bash
curl -sS -X POST \
  -H "Authorization: Bearer $PROBARA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Salud de la versión","accessMode":"private","members":["01J9Z3Q4M8R2T6V1W5X7Y9A0BC"],"groups":["01J9Z3R2K4N6P8Q0S2T4V6W8XY"]}' \
  "https://app.probara.net/api/v1/dashboards"
```

## Listar

`GET /api/v1/dashboards`

Devuelve solo los dashboards que quien llama puede ver, primero los actualizados más recientemente. Paginación por páginas.

| Parámetro  | Notas                                                 |
| ---------- | ----------------------------------------------------- |
| `page`     | Número de página (empieza en 1, `1` por defecto)      |
| `pageSize` | Elementos por página (`50` por defecto, máximo `200`) |

`200` con `{ items, page, pageSize, total }`. `total` cuenta solo los dashboards que quien llama puede ver.

## Obtener

`GET /api/v1/dashboards/{dashboardUlid}`

`200` con el dashboard, o `404 not_found` cuando quien llama no puede verlo. Un ULID mal formado devuelve `422 validation_failed`.

## Actualizar

`PATCH /api/v1/dashboards/{dashboardUlid}`

Cambia `name` y/o `description` (`null` borra la descripción). Se requiere al menos un campo, y el cuerpo es estricto: cualquier otra clave, incluida `accessMode`, devuelve `422 validation_failed` (la visibilidad se cambia con [Reemplazar la configuración](#reemplazar-la-configuración) o desde [Acceso](#acceso)). Un cuerpo que no cambia nada devuelve el dashboard tal como está, sin mover `updatedAt` ni registrar un evento de auditoría.

`200` con el dashboard actualizado. `403 forbidden` cuando quien llama puede ver el dashboard pero no lo creó ni es propietario o administrador de la organización; `404 not_found` cuando no puede verlo.

## Reemplazar la configuración

`PUT /api/v1/dashboards/{dashboardUlid}`

Establece toda la configuración de un dashboard en una sola llamada: se aplica todo o nada. Es lo que guarda el diálogo de configuración del dashboard en la aplicación. Aplican las mismas reglas de `403`/`404` que en [Actualizar](#actualizar).

| Campo         | Requerido | Notas                                                                                                  |
| ------------- | --------- | ------------------------------------------------------------------------------------------------------ |
| `name`        | sí        | Sin espacios al inicio/fin, 1–200 caracteres                                                           |
| `description` | sí        | Hasta 2000 caracteres, o `null` para no tener una                                                      |
| `accessMode`  | sí        | `public` o `private`                                                                                   |
| `members`     | sí        | ULID de los miembros actuales de la organización que tendrán un acceso `granted` después de la llamada |
| `groups`      | sí        | ULID de los grupos de usuarios que tendrán acceso al dashboard después de la llamada                   |

Las listas siguen las reglas de [Crear](#crear): como máximo 200 ULID cada una, sin repetir, vacías en un dashboard público, y `404 not_found` para un ULID que no es de un miembro actual o de un grupo de usuarios de la organización (no cambia nada).

Después de la llamada, exactamente los miembros indicados tienen un acceso `granted` y exactamente los grupos indicados tienen acceso al dashboard: se agregan los que faltan y se quitan los demás. Los accesos `revoked` no forman parte de esta configuración. Uno que no indiques se conserva tal como está; indicar a un miembro cuyo acceso es `revoked` lo cambia a `granted`. Hacer público el dashboard quita todos los accesos `granted` y todos los de grupos, y conserva los `revoked`.

Cada cambio se registra igual que la llamada de [Acceso](#acceso) correspondiente. Un cuerpo que no cambia nada devuelve el dashboard tal como está y no registra nada. `updatedAt` cambia cuando cambian el nombre, la descripción o la visibilidad; cambiar solo miembros o grupos no lo mueve, igual que en las llamadas de [Acceso](#acceso).

`200` con el dashboard. Consulta [Campos de la respuesta](#campos-de-la-respuesta).

## Eliminar

`DELETE /api/v1/dashboards/{dashboardUlid}`

Elimina el dashboard de forma permanente, junto con todos los accesos otorgados sobre él. Aplican las mismas reglas de `403`/`404` que en [Actualizar](#actualizar).

`204` sin cuerpo.

## Acceso

Quién ve un dashboard se administra en `/api/v1/dashboards/{dashboardUlid}/access`, con la misma forma que el [acceso a proyectos](https://docs.probara.net/es/developers/api/project-access/). Todos estos endpoints, incluidas las lecturas, siguen las reglas de `403`/`404` de [Actualizar](#actualizar): solo los usan quien creó el dashboard y los propietarios y administradores de la organización. Cada cambio registra un [evento de auditoría](#eventos-de-auditoría) `dashboard.access_changed`; una solicitud que no cambia nada no registra nada.

### Consultar el estado del acceso

`GET /api/v1/dashboards/{dashboardUlid}/access`

`200` con:

| Campo           | Tipo           | Notas                                                                       |
| --------------- | -------------- | --------------------------------------------------------------------------- |
| `mode`          | string         | `public` o `private`                                                        |
| `ownerUserUlid` | string \| null | Quien lo creó, que siempre ve y modifica el dashboard                       |
| `grantedCount`  | number         | Miembros con un acceso `granted`, incluidos los que dejaron la organización |
| `revokedCount`  | number         | Miembros con un acceso `revoked`, incluidos los que dejaron la organización |

### Hacer un dashboard público o privado

`PATCH /api/v1/dashboards/{dashboardUlid}/access` con `{ "mode": "public" }` o `{ "mode": "private" }`.

- **Privado** oculta el dashboard a todos los miembros sin un acceso otorgado. Quien lo creó y los propietarios y administradores de la organización lo siguen viendo sin necesitarlo.
- **Público** quita todos los accesos `granted` y todos los accesos de grupos: al volver a hacerlo privado, las listas empiezan vacías. Los accesos `revoked` se conservan.

`mode` es obligatorio y es el único campo: el propietario de un dashboard es siempre quien lo creó y no se puede transferir. `200` con el estado del acceso; el `updatedAt` del dashboard cambia cuando cambia el modo.

### Accesos de miembros

El acceso de un miembro tiene un valor `access`:

- `granted` deja entrar al miembro a un dashboard privado. En un dashboard público se rechaza con `409 conflict`, porque todos los miembros ya lo ven.
- `revoked` deja fuera al miembro: fuera de un dashboard público, y fuera de uno privado aunque se le haya otorgado a uno de sus grupos de usuarios.

| Endpoint                                                              | Resultado                                                         |
| --------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `GET /api/v1/dashboards/{dashboardUlid}/access/members`               | `200`, página de miembros con un acceso, cada uno con su `access` |
| `POST /api/v1/dashboards/{dashboardUlid}/access/members`              | `201`, el miembro con su acceso                                   |
| `DELETE /api/v1/dashboards/{dashboardUlid}/access/members/{userUlid}` | `204`; vuelve a decidir el modo del dashboard                     |

La lista usa paginación por páginas (`page`, `pageSize`), con los miembros más antiguos primero, y muestra solo a los miembros actuales. Cada elemento lleva los mismos campos de miembro que la [lista de miembros con acceso a un proyecto](https://docs.probara.net/es/developers/api/project-access/#listar-los-miembros-con-acceso), más `access`. Quien lo creó y los propietarios y administradores aparecen solo si tienen un acceso.

`POST` recibe `{ "userUlid": "…", "access": "revoked" }`; `access` es `granted` por defecto. Crea el acceso o cambia su valor. El destinatario debe ser miembro actual de la organización; si no, `404 not_found`. `DELETE` quita el acceso sea cual sea su valor, también el de un miembro que ya dejó la organización; `404 not_found` cuando el miembro no tiene ninguno.

### Accesos de grupos

| Endpoint                                                                  | Resultado                                      |
| ------------------------------------------------------------------------- | ---------------------------------------------- |
| `GET /api/v1/dashboards/{dashboardUlid}/access/groups`                    | `200`, página de grupos con acceso, por nombre |
| `POST /api/v1/dashboards/{dashboardUlid}/access/groups`                   | `201`, el grupo                                |
| `DELETE /api/v1/dashboards/{dashboardUlid}/access/groups/{userGroupUlid}` | `204`                                          |

`POST` recibe `{ "userGroupUlid": "…" }`. Todos los miembros actuales y futuros del grupo ven el dashboard mientras es privado. Otorgar acceso a un grupo en un dashboard público se rechaza con `409 conflict`, porque todos los miembros ya lo ven. Cada grupo lleva `ulid`, `name`, `description` y `userCount` (miembros actuales). Un grupo desconocido, o un `DELETE` para un grupo sin acceso, devuelve `404 not_found`.

### Ejemplo

```bash
curl -sS -X POST \
  -H "Authorization: Bearer $PROBARA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"userUlid":"01J9Z3Q4M8R2T6V1W5X7Y9A0BC","access":"revoked"}' \
  "https://app.probara.net/api/v1/dashboards/$DASHBOARD_ULID/access/members"
```

## Campos de la respuesta

| Campo           | Tipo           | Notas                                                                                                                                                    |
| --------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ulid`          | string         | El ULID del dashboard                                                                                                                                    |
| `name`          | string         |                                                                                                                                                          |
| `description`   | string \| null |                                                                                                                                                          |
| `ownerUserUlid` | string \| null | El miembro que lo creó; `null` una vez eliminada esa cuenta                                                                                              |
| `accessMode`    | string         | `public` o `private`                                                                                                                                     |
| `createdAt`     | number         | Milisegundos desde la época Unix                                                                                                                         |
| `updatedAt`     | number         | Milisegundos desde la época Unix; cambia con cada cambio de nombre, de descripción, de visibilidad o de [widgets](https://docs.probara.net/es/developers/api/dashboard-widgets/) |
| `canEdit`       | boolean        | Si **tú** puedes renombrarlo y eliminarlo: `true` para quien lo creó y para propietarios y administradores                                               |

## Eventos de auditoría

Cada creación, actualización, eliminación y cambio de acceso registra un evento en el feed de actividad de la organización, con tipo de entidad `dashboard` y el nombre del dashboard como etiqueta: `dashboard.created` (metadata `accessMode`), `dashboard.updated` (metadata `changes`, una entrada por campo modificado), `dashboard.deleted` y `dashboard.access_changed`. El `reason` de la metadata de un cambio de acceso es uno de estos:

| `reason`         | Otra metadata                    | Cambio                                   |
| ---------------- | -------------------------------- | ---------------------------------------- |
| `mode_toggle`    | `mode`                           | Se hizo público o privado                |
| `member_granted` | `targetUserId`                   | Acceso de un miembro puesto en `granted` |
| `member_revoked` | `targetUserId`                   | Acceso de un miembro puesto en `revoked` |
| `member_cleared` | `targetUserId`, `previousAccess` | Se quitó el acceso de un miembro         |
| `group_granted`  | `userGroupUlid`                  | Se otorgó acceso a un grupo              |
| `group_revoked`  | `userGroupUlid`                  | Se quitó el acceso de un grupo           |

Una creación que otorga acceso a miembros y grupos registra `dashboard.created` seguido de un evento `member_granted` o `group_granted` por cada acceso. Un [reemplazo de la configuración](#reemplazar-la-configuración) registra `dashboard.updated` cuando cambiaron el nombre o la descripción, y luego un evento `dashboard.access_changed` por cada cambio: `mode_toggle`, `member_granted`, `member_cleared` (con `previousAccess: "granted"`), `group_granted`, `group_revoked`. Hacer público un dashboard con `PATCH …/access` registra `mode_toggle` seguido de un `group_revoked` por cada acceso de grupo que quita.

Consulta [Eventos de auditoría](https://docs.probara.net/es/developers/api/audit-events/).

## MCP

Las mismas operaciones están disponibles como las herramientas MCP `list_dashboards`, `get_dashboard`, `create_dashboard` (con `members` y `groups`), `update_dashboard`, `replace_dashboard_settings` y `delete_dashboard`, y para el acceso `get_dashboard_access`, `update_dashboard_access`, `list_dashboard_access_members`, `set_dashboard_access_member`, `remove_dashboard_access_member`, `list_dashboard_access_groups`, `add_dashboard_access_group` y `remove_dashboard_access_group`, con las mismas reglas de plan y de acceso. Consulta la [referencia de herramientas MCP](https://docs.probara.net/es/developers/mcp-tools/).
