Panel de control
El Panel API le permite enumerar, crear, editar y eliminar paneles mediante programación con las mismas claves API que ya usa para operaciones de ingesta y consulta.
Operaciones soportadas:
- Lista.
GET https://api.telemetry.sh/dashboard - Crear.
POST https://api.telemetry.sh/dashboard - Editar.
PATCH https://api.telemetry.sh/dashboard - Eliminar.
DELETE https://api.telemetry.sh/dashboard
Encabezados
| Nombre | Tipo | Descripción |
|---|---|---|
Content-Type |
Cadena de texto | Debe ser application/json. |
Authorization |
Cadena de texto | Su clave API, ya sea como clave sin formato o como Bearer <key>. |
Reglas de alcance:
GET /dashboardaceptaread,writeoread-and-write.POST /dashboard,PATCH /dashboardyDELETE /dashboardrequierenwriteoread-and-write
Listar respuesta
GET /dashboard devuelve todos los tableros del equipo de claves API, ordenados por updated_at DESC.
Parámetros de consulta admitidos:
| campo | Tipo | Requerido | Contrato exacto | Predeterminado |
|---|---|---|---|---|
page |
Entero | No | Número de página entero positivo. Se rechazan los valores inferiores a 1. |
1 |
pageSize |
Entero | No | Tamaño de página entero positivo. Los valores superiores a 100 se fijan en 100. El API también acepta el alias heredado page_size. |
50 |
Cada elemento incluye los metadatos del panel, un url, un widget_count y la lista completa de widgets en forma de API.
Respuesta de alto nivel:
| campo | Tipo | Contrato exacto |
|---|---|---|
status |
Cadena de texto | Siempre "success" en caso de éxito. |
pagination |
Objeto | Metadatos de paginación para la página actual. Vea la tabla a continuación. |
dashboards |
matriz | Conjunto de objetos del tablero para el equipo de claves API. Matriz vacía cuando el equipo no tiene paneles. |
pagination incluye:
| campo | Tipo | Contrato exacto | Ejemplo |
|---|---|---|---|
page |
Entero | Número de página actual basado en 1. | 2 |
pageSize |
Entero | Tamaño de página efectivo después de la sujeción predeterminada y del tamaño máximo. | 25 |
total |
Entero | Número total de paneles para el equipo antes de la paginación. | 63 |
totalPages |
Entero | Número total de páginas para el pageSize actual. 0 cuando total es 0. |
3 |
hasNextPage |
Booleano | true cuando existe otra página después de la página actual. |
true |
hasPreviousPage |
Booleano | true cuando existe otra página antes de la página actual. |
true |
Cada artículo de dashboards incluye:
| campo | Tipo | Contrato exacto | Ejemplo |
|---|---|---|---|
id |
Cadena de texto | Identificación del panel. | "550e8400-e29b-41d4-a716-446655440000" |
team_id |
Cadena de texto | Identificación del equipo propietario. | "a7d4..." |
name |
Cadena de texto | Nombre del panel. | "Operations Overview" |
slug |
Cadena de texto | Babosa del tablero. | "operations-overview" |
description |
Cadena o null |
Descripción del panel guardada. | "Core service health and latency" |
created_by |
Cadena o null |
ID de usuario para paneles creados por UI. null para paneles creados por API. |
null |
created_by_api_key_id |
Cadena o null |
ID de clave del equipo API para paneles creados por API. null para paneles creados por UI. |
"key_123" |
created_at |
Cadena de texto | Cadena de marca de tiempo de la tabla de paneles. | "2026-03-27T00:09:03.797Z" |
updated_at |
Cadena de texto | Cadena de marca de tiempo de la tabla de paneles. | "2026-03-27T00:09:03.797Z" |
url |
Cadena de texto | URL relativa del panel en la interfaz de usuario Telemetry. | "/team/acme/dashboard/operations-overview" |
widget_count |
Entero | Número de widgets adjuntos al panel. | 2 |
widgets |
matriz | Matriz de widgets con la misma forma devuelta al crear y editar respuestas. | [] |
crear cuerpo
| campo | Tipo | Requerido | Contrato exacto | Ejemplo |
|---|---|---|---|---|
name |
Cadena de texto | si | Nombre del tablero recortado. No debe estar vacío después del recorte. | "HTTP Monitoring" |
slug |
Cadena de texto | No | Babosa personalizada opcional. Telemetry lo normaliza recortando, poniendo minúsculas, eliminando todos los caracteres excepto a-z, 0-9, espacios y -, convirtiendo ejecuciones de espacios en blanco a -, colapsando - repetido y recortando el inicio/final. -. Si se omite, Telemetry lo deriva de name. El resultado normalizado aún debe contener al menos un carácter alfanumérico. |
"HTTP Monitoring!!!" se convierte en "http-monitoring" |
description |
Cadena o null |
No | Descripción opcional. Las cadenas vacías están normalizadas a null. |
"Core service health and latency" |
widgets |
matriz | No | Matriz opcional de widgets para crear inmediatamente. Si se omite, el panel se crea vacío. Si algún widget no es válido, se rechaza toda la solicitud con 400. |
[] |
Crear respuesta
POST /dashboard devuelve 201 Created con el panel guardado más la lista de widgets normalizada.
{
"status": "success",
"dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"team_id": "a7d4...",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": "Core service health and latency",
"created_by": null,
"created_by_api_key_id": "key_123",
"created_at": "2026-03-27T00:09:03.797Z",
"updated_at": "2026-03-27T00:09:03.797Z",
"url": "/team/acme/dashboard/http-monitoring"
},
"widgets": [
{
"id": "0b3cb3f5-1147-4f2f-bf64-5044147c3e9a",
"title": "Errors Per Hour",
"widget_type": "query",
"config": {
"querySql": "SELECT hour, errors FROM error_rollups",
"chartType": "Line Chart",
"xAxis": "hour",
"yAxis": "errors",
"groupBy": null,
"queryId": null,
"sourceUrl": null
},
"layout": {
"x": 0,
"y": 0,
"w": 12,
"h": 4
},
"sort_order": 1
}
]
}
Editar cuerpo
PATCH /dashboard es un punto final de actualización parcial. Los campos omitidos permanecen sin cambios.
Debe identificar el panel de destino con dashboardId o dashboardSlug.
| campo | Tipo | Requerido | Contrato exacto | Ejemplo |
|---|---|---|---|---|
dashboardId |
Cadena de texto | Uno de dashboardId o dashboardSlug |
ID del panel existente para editar. Si se envían ambos identificadores, deben hacer referencia al mismo panel. | "550e8400-e29b-41d4-a716-446655440000" |
dashboardSlug |
Cadena de texto | Uno de dashboardId o dashboardSlug |
Slug del panel de control existente para editar. Esta es la babosa actual, no la nueva. | "http-monitoring" |
name |
Cadena de texto | No | Nuevo nombre del tablero recortado. No debe estar vacío cuando se proporcione. La actualización de name no cambia automáticamente el slug. |
"HTTP Monitoring v2" |
slug |
Cadena de texto | No | Nuevo valor de slug. Telemetry lo normaliza con las mismas reglas que crear. Si se omite, el slug existente permanece sin cambios. | "http-monitoring-v2" |
description |
Cadena o null |
No | Nueva descripción del panel. null o "" borra la descripción. Si se omite, la descripción existente permanece sin cambios. |
"Updated on-call dashboard" |
widgets |
matriz | No | Lista completa de widgets de reemplazo. Si se omite, los widgets existentes permanecen sin cambios. Si se proporciona, Telemetry elimina el conjunto de widgets actual y lo reemplaza exactamente con los widgets de esta matriz. [] borra todos los widgets. |
[] |
Notas:
PATCHaún no admite agregar ni editar un solo widget.- Cuando se proporciona
widgets, los ID de widget se regeneran porque API reemplaza el conjunto completo de widgets.
Editar respuesta
PATCH /dashboard devuelve 200 OK con la misma forma de respuesta que crear:
dashboardcontiene los metadatos del panel guardados yurlwidgetscontiene la lista completa de widgets persistentes en forma API- Si se reemplazó
widgets, los identificadores de widget devueltos reflejan las filas recién insertadas
Eliminar cuerpo
DELETE /dashboard elimina un único panel y todos sus widgets.
Debe identificar el panel de destino con dashboardId o dashboardSlug.
| campo | Tipo | Requerido | Contrato exacto | Ejemplo |
|---|---|---|---|---|
dashboardId |
Cadena de texto | Uno de dashboardId o dashboardSlug |
ID del panel existente para eliminar. Si se envían ambos identificadores, deben hacer referencia al mismo panel. | "550e8400-e29b-41d4-a716-446655440000" |
dashboardSlug |
Cadena de texto | Uno de dashboardId o dashboardSlug |
Slug del tablero existente para eliminar. | "http-monitoring" |
Notas:
- La eliminación es permanente.
- Al eliminar un panel, también se eliminan todos los widgets actualmente adjuntos a él.
Eliminar respuesta
DELETE /dashboard devuelve 200 OK:
{
"status": "success",
"deleted_dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": "Core service health and latency",
"widget_count": 2,
"url": "/team/acme/dashboard/http-monitoring"
}
}
Errores comunes
400 Bad Requestsi el cuerpo de JSON no es válido o el cuerpo de la solicitud no es un objeto JSON400 Bad RequestsipageopageSizeno es un entero positivo enGET /dashboard400 Bad Requestsi faltadashboardIdodashboardSlugparaPATCHoDELETE400 Bad RequestsiPATCHomite todos los campos editables400 Bad Requestsiname,slug,descriptionowidgetsno superan la validación400 Bad Requestsi un widget de consulta contiene SQL que no es de solo lectura403 Forbiddensi la clave API no tiene permiso para la operación solicitada401 Unauthorizedsi la clave API falta o no es válida404 Not Foundsi el tablero no existe para el equipo de claves API409 Conflictsi otro panel del mismo equipo ya utiliza el slug solicitado
Campos de widgets
Cada artículo en widgets admite:
| campo | Tipo | Requerido | Contrato exacto | Ejemplo |
|---|---|---|---|---|
title |
Cadena de texto | si | Título del widget recortado. No debe estar vacío después del recorte. | "Errors Per Hour" |
widget_type |
Cadena de texto | si | Enumeración exacta: query, explorer, header o free_text. |
"query" |
config |
Objeto | si | Objeto de configuración de widgets. Sus campos obligatorios dependen de widget_type. |
{ "querySql": "...", "chartType": "Line Chart" } |
layout |
Objeto | No | Colocación de rejilla opcional. Si se omite, Telemetry apila los widgets verticalmente utilizando los valores predeterminados de tipo de widget. | { "x": 0, "y": 0, "w": 12, "h": 4 } |
Comprender el diseño
Los paneles Telemetry utilizan una cuadrícula de 12 columnas.
El objeto layout utiliza unidades de cuadrícula, no píxeles:
| campo | Tipo | Predeterminado | Contrato exacto | Ejemplo |
|---|---|---|---|---|
x |
Entero | 0 |
Entero no negativo. 0 es el extremo izquierdo. Los valores no válidos vuelven a ser 0. |
0 |
y |
Entero | siguiente fila abierta | Entero no negativo. 0 es la parte superior del tablero. Si se omite, Telemetry coloca el widget después del y + h del widget anterior. Los valores no válidos vuelven a esa fila calculada. |
0 |
w |
Entero | 12 |
Ancho entero positivo en columnas de la cuadrícula. 12 significa ancho completo y 6 significa ancho medio. Los valores no válidos vuelven a ser 12. |
12 |
h |
Entero | valor predeterminado de tipo widget | Altura de entero positivo en filas de la cuadrícula. Los valores no válidos recurren al valor predeterminado del tipo de widget que se enumera a continuación. | 4 |
Entonces esto:
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 }
significa:
- coloque el widget en la esquina superior izquierda del tablero
- haz que abarque las 12 columnas
- haz que tenga 4 filas de cuadrícula de tablero de alto
Esto:
"layout": { "x": 6, "y": 0, "w": 6, "h": 4 }
significa:
- coloque el widget en la fila superior
- comenzar a la mitad del tablero
- haz que tome la mitad derecha de la fila
Por ejemplo, estos dos widgets se muestran uno al lado del otro:
[
{ "layout": { "x": 0, "y": 0, "w": 6, "h": 4 } },
{ "layout": { "x": 6, "y": 0, "w": 6, "h": 4 } }
]
Si omite layout, Telemetry apila los widgets verticalmente usando estos valores predeterminados:
x = 0w = 12h = 4paraqueryyexplorerh = 2paraheaderh = 3parafree_textyse coloca en la siguiente fila abierta
Telemetry no sujeta x y w a la cuadrícula de 12 columnas y no resuelve las posiciones superpuestas por usted. Mantenga x + w <= 12 y evite la superposición de rangos x/y si desea una representación predecible.
Configuración del widget de consulta
| campo | Tipo | Requerido | Valores exactos o formato | Ejemplo |
|---|---|---|---|---|
querySql |
Cadena de texto | si | Cadena SQL de solo lectura recortada. No debe estar vacío después del recorte. Los widgets de consulta del panel rechazan declaraciones de escritura como INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE, GRANT y REVOKE. |
"SELECT endpoint, COUNT(*) AS errors FROM http_logs GROUP BY endpoint" |
chartType |
Cadena de texto | si | Enumeración exacta validada por API: table, Scatter Plot, Bar Chart, Line Chart o Stacked Area Chart. |
"Line Chart" |
xAxis |
Cadena de texto | Requerido para gráficos que no sean table |
Nombre de la columna de resultados recortada que se utilizará para el eje x. Para Bar Chart, puede ser una columna de texto/categórica, numérica o de tiempo. Para Scatter Plot, Line Chart y Stacked Area Chart, utilice una columna numérica o de tiempo. Puede ser una cadena vacía solo cuando chartType es table. |
"hour" |
yAxis |
Cadena de texto | Requerido para gráficos que no sean table |
Nombre de la columna de resultados numéricos recortados para usar en el eje y. Puede ser una cadena vacía solo cuando chartType es table. |
"errors" |
groupBy |
Cadena o null |
No | Nombre de columna de resultado recortado opcional que se utiliza para dividir un gráfico en varias series. Utilice null u omítalo para no agruparlo. |
"service" |
queryId |
Cadena o null |
No | ID de consulta guardada recortada opcional. El API lo almacena como una cadena y no valida su forma. | "550e8400-e29b-41d4-a716-446655440000" |
sourceUrl |
Cadena o null |
No | URL de UI recortada opcional para abrir cuando se hace clic en el título del widget. Se recomiendan URL de aplicaciones relativas, como /team/acme/ops/draft/0?.... |
"/team/acme/ops/draft/0?tab=chart&chartType=Line+Chart&xAxis=hour&yAxis=errors" |
Notas:
- Utilice
chartType: "table"si desea una tabla de resultados simple en lugar de un gráfico. - Los widgets de consulta del panel se ejecutan automáticamente cuando se carga el panel, por lo que solo se acepta SQL de solo lectura.
- Para todos los gráficos que no sean
table,xAxisyyAxisdeben coincidir con las columnas reales devueltas porquerySql. - Para
Bar Chart,xAxispuede ser texto/categórico, numérico o temporal. - Para
Scatter Plot,Line ChartyStacked Area Chart,xAxisdebe ser numérico o temporal. - Para todos los gráficos que no sean
table,yAxisdebe ser numérico. - Para los ejes x categóricos
Bar Chart, el widget conserva el orden de los resultados de la consulta. UtiliceORDER BYenquerySqlpara controlar el orden de las barras. - Si se omite
sourceUrl, la interfaz de usuario del panel obtiene una URL de consulta preliminar alternativa cuando puede. El enlace derivado conservadefaultQuery,tab,chartType,xAxis,yAxisygroupBy.
Ejemplo: widget de consulta con un eje x de gráfico de barras categórico
{
"title": "Requests by Endpoint",
"widget_type": "query",
"config": {
"querySql": "SELECT endpoint, COUNT(*) AS requests FROM http_logs GROUP BY endpoint ORDER BY requests DESC",
"chartType": "Bar Chart",
"xAxis": "endpoint",
"yAxis": "requests",
"groupBy": null
}
}
En ese ejemplo:
endpointes una columna de texto del eje xrequestses la columna numérica del eje yORDER BY requests DESCcontrola el orden de las barras de izquierda a derecha
Configuración del widget de encabezado
Utilice widget_type: "header" para títulos de secciones que agrupen widgets relacionados.
| campo | Tipo | Requerido | Valores exactos o formato | Ejemplo |
|---|---|---|---|---|
description |
Cadena o null |
No | Descripción de rebajas opcional que se muestra debajo del título del encabezado. Las cadenas vacías están normalizadas a null. |
"Watch these charts during deploys and incident triage." |
Notas:
- El widget
titlees el título de la sección visible. descriptionadmite rebajas. Se escapa el HTML sin formato antes de renderizarlo en la interfaz de usuario.
Ejemplo:
{
"title": "Deploy Health",
"widget_type": "header",
"config": {
"description": "Use this section during rollout checks and incident response."
}
}
Configuración del widget de texto libre
Utilice widget_type: "free_text" para obtener notas de rebajas, enlaces o instrucciones de runbook independientes.
| campo | Tipo | Requerido | Valores exactos o formato | Ejemplo |
|---|---|---|---|---|
content |
Cadena de texto | si | Contenido de rebajas no vacío después del recorte. | "Check [the latency recipe](/sql/api-latency-percentiles) before paging infra." |
Notas:
contentadmite rebajas. Se escapa el HTML sin formato antes de renderizarlo en la interfaz de usuario.- El widget
titletodavía es necesario para el API, aunque la interfaz de usuario solo muestra el cuerpo de rebajas para este tipo de componente.
Ejemplo:
{
"title": "API Runbook Note",
"widget_type": "free_text",
"config": {
"content": "Check the on-call runbook first, then compare request volume with deploy timestamps."
}
}
Configuración del widget del explorador
| campo | Tipo | Requerido | Valores exactos o formato | Ejemplo |
|---|---|---|---|---|
tableName |
Cadena de texto | si | Nombre de la tabla de origen recortada. No debe estar vacío después del recorte. | "queue_metrics" |
explorerState |
Objeto | si | Configuración de exploración completa utilizada para representar el widget. Consulte la tabla de campos detallada a continuación. | { "graphType": "line", "aggregation": "p95", ... } |
sourceUrl |
Cadena o null |
No | URL de UI recortada opcional para abrir cuando se hace clic en el título del widget. Si se omite, Telemetry deriva una URL de exploración alternativa de tableName y explorerState. |
"/team/acme/table/queue_metrics?tab=explore&graphType=line" |
Campos explorerState
| campo | Tipo | Requerido | Valores exactos o formato | Predeterminado | Ejemplo |
|---|---|---|---|---|---|
graphType |
Cadena de texto | si | Enumeración exacta validada por API: samples, table, line, bar, stacked-area. |
"samples" |
"line" |
aggregation |
Cadena de texto | si | Enumeración exacta validada por el API: count, sum, avg, min, max, p50, p90, p95, p99. |
"count" |
"p95" |
metric |
Cadena o null |
No | Columna recortada o ruta de campo para agregar. Utilice null para count. Para agregaciones que no son count, esta es la medida principal y tiene prioridad sobre el selectedColumns numérico detectado automáticamente cuando se proporciona. |
null |
"latency_ms" |
timeZone |
Cadena o null |
No | Utilice UTC o un identificador de zona horaria de IANA como America/Los_Angeles o Europe/Berlin. Actualmente, el API recorta y almacena cualquier cadena que no esté vacía, pero los nombres de zona horaria no válidos pueden fallar más adelante cuando se ejecuta la consulta del widget. Si se omite, la configuración guardada mantiene null y la interfaz de usuario vuelve a la zona horaria del cliente o UTC, según el contexto. |
null |
"America/Los_Angeles" |
timePreset |
Cadena de texto | si | Los valores admitidos son 1h, 6h, 24h, 7d, 30d, 90d, custom. Actualmente, API acepta cualquier cadena que no esté vacía, pero la interfaz de usuario y el generador SQL solo admiten esos valores. |
"7d" |
"7d" |
customStart |
Cadena de texto | No | Se utiliza cuando timePreset es custom. Formato recomendado: YYYY-MM-DDTHH:mm, YYYY-MM-DD HH:mm, YYYY-MM-DDTHH:mm:ss o YYYY-MM-DDTHH:mm:ss.sss, seguido opcionalmente de Z o un desplazamiento numérico como -07:00. Si no hay ningún sufijo de zona horaria, el valor se interpreta en timeZone. |
desarmado | "2026-03-20T00:00:00Z" |
customEnd |
Cadena de texto | No | Mismo formato que customStart. Si se omite para un rango personalizado, la consulta no tiene límite de tiempo superior. |
desarmado | "2026-03-26T00:00:00-07:00" |
granularity |
Cadena de texto | si | Enumeración exacta validada por API: auto, minute, hour, day, week, month. |
"auto" |
"hour" |
splitBy |
Conjunto de cuerdas | si | Matriz de nombres de campos recortados no vacíos. Se permiten rutas de puntos como attributes.queue_name para campos JSON anidados. |
[] |
["queue_name"] |
seriesLimit |
Entero o null |
No | Entero positivo o null. null significa que no hay límite de serie explícito. Más útil para gráficos divididos. |
100 |
10 |
filters |
matriz | si | Matriz de grupos de filtros. A cada grupo se le aplica un AND internamente; varios grupos se combinan mediante OR. Los grupos vacíos o no válidos se eliminan durante la normalización. | [] |
[] |
selectedColumns |
Conjunto de cuerdas | si | Matriz de nombres de campos recortados no vacíos para mostrar en las vistas samples o table. Se permiten rutas JSON con puntos. Utilice [] para permitir que Telemetry elija los valores predeterminados. |
[] |
["timestamp_utc", "queue_name", "latency_ms"] |
orderBy |
Cadena o null |
No | Nombre de campo opcional utilizado para ordenar resultados tabulares. Se permiten rutas JSON con puntos. | null |
"timestamp_utc" |
limit |
Entero | si | Límite de fila o grupo de números enteros positivos. Los valores no válidos vuelven a los valores predeterminados. | 200 |
200 |
orderDirection |
Cadena de texto | si | Enumeración exacta validada por API: ASC o DESC. |
"DESC" |
"DESC" |
Valores timePreset
| Valor | Significado |
|---|---|
1h |
Última 1 hora |
6h |
últimas 6 horas |
24h |
Últimas 24 horas |
7d |
últimos 7 días |
30d |
últimos 30 días |
90d |
últimos 90 días |
custom |
Utilice customStart y customEnd en lugar de un rango relativo |
Si envía un timePreset no documentado, el generador SQL actual vuelve a tener un comportamiento de 7 días. Trate los valores anteriores como el contrato admitido.
Comportamiento granularity: "auto"
Cuando granularity es auto, Telemetry lo resuelve así:
timePreset |
Granularidad efectiva |
|---|---|
1h, 6h, 24h |
minute |
7d |
hour |
30d, 90d, custom |
day |
Grupos de filtros y condiciones.
filters debería tener la siguiente forma:
[
{
"logic": "AND",
"conditions": [
{ "field": "queue_name", "operator": "=", "value": "email" },
{ "field": "latency_ms", "operator": ">", "value": "1000" }
]
}
]
Cada elemento de filters es un grupo de filtros:
| campo | Tipo | Requerido | Valores exactos o formato | Ejemplo |
|---|---|---|---|---|
logic |
Cadena de texto | si | Debe ser AND. La creación del panel API normaliza cada grupo a AND; no hay ningún OR por grupo. Para expresar OR, utilice varios grupos porque los grupos se combinan con OR. |
"AND" |
conditions |
matriz | si | Matriz de condiciones de filtro en el grupo. Un grupo con cero condiciones válidas se descarta. | [{ "field": "queue_name", "operator": "=", "value": "email" }] |
Cada artículo en conditions admite:
| campo | Tipo | Requerido | Valores exactos o formato | Ejemplo |
|---|---|---|---|---|
field |
Cadena de texto | si | Ruta de campo recortada no vacía. Se admiten rutas JSON anidadas con puntos, como attributes.queue_name. No se permiten segmentos de ruta vacíos. |
"latency_ms" |
operator |
Cadena de texto | si | Enumeración exacta validada por el API: =, !=, >, >=, <, <=, LIKE, NOT LIKE, IS NULL, IS NOT NULL. |
">" |
value |
Cadena, Número, Booleano o null |
Generalmente | Almacenado como una cadena durante la normalización. Para IS NULL y IS NOT NULL, utilice "" u omita el valor semántico. null se convierte en "". |
"1000" |
Cuando se ejecuta el SQL generado:
- Las condiciones dentro de un grupo se unen con
AND. - Los grupos se unen con
OR. - Las rutas de campo punteadas, como
attributes.queue_name, se traducen en acceso a campos anidados.
Ejemplo: crear un panel vacío
Ejemplo: listar paneles
curl "https://api.telemetry.sh/dashboard?page=1&pageSize=25" \
-H "Authorization: $API_KEY"
Respuesta de ejemplo:
{
"status": "success",
"pagination": {
"page": 1,
"pageSize": 25,
"total": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
},
"dashboards": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"team_id": "team_123",
"name": "Operations Overview",
"slug": "operations-overview",
"description": "Core service health and latency",
"created_by": null,
"created_by_api_key_id": "key_123",
"created_at": "2026-03-27T00:09:03.797Z",
"updated_at": "2026-03-27T00:09:03.797Z",
"url": "/team/acme/dashboard/operations-overview",
"widget_count": 1,
"widgets": [
{
"id": "widget_123",
"title": "Requests Per Hour",
"widget_type": "query",
"config": {
"querySql": "SELECT DATE_TRUNC('hour', timestamp_utc) AS hour, COUNT(*) AS requests FROM http_logs GROUP BY 1 ORDER BY 1",
"chartType": "Line Chart",
"xAxis": "hour",
"yAxis": "requests",
"groupBy": null,
"sourceUrl": "/team/acme/ops/draft/0?tab=chart&chartType=Line+Chart&xAxis=hour&yAxis=requests"
},
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 },
"sort_order": 0
}
]
}
]
}
Ejemplo: crear un panel vacío
curl -X POST https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"name": "Operations Overview",
"description": "Core service health and latency"
}'
Ejemplo: cambiar el nombre de un panel y actualizar su descripción
curl -X PATCH https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"dashboardSlug": "operations-overview",
"name": "Operations Overview v2",
"description": "Updated to focus on API latency and error rates"
}'
Ejemplo: reemplazar todos los widgets en un panel existente
Esta solicitud mantiene el mismo panel pero reemplaza el conjunto de widgets completo con la nueva matriz widgets.
curl -X PATCH https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"dashboardSlug": "operations-overview",
"widgets": [
{
"title": "Revenue by City",
"widget_type": "explorer",
"config": {
"tableName": "uber_rides",
"explorerState": {
"graphType": "table",
"aggregation": "sum",
"metric": "price",
"timePreset": "30d",
"granularity": "auto",
"splitBy": ["city"],
"filters": [],
"selectedColumns": [],
"orderBy": "price",
"limit": 10,
"orderDirection": "DESC"
}
},
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 }
}
]
}'
Ejemplo: eliminar un panel
curl -X DELETE https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"dashboardSlug": "operations-overview"
}'
Ejemplos de widgets comunes
Los siguientes ejemplos muestran solo el cuerpo de la solicitud JSON que envía a POST /dashboard.
Estos ejemplos asumen una tabla uber_rides con campos como:
citypricewait_time_minutesstatususer_idtimestamp_utc
Ejemplo: crear un panel con dos widgets de exploración comunes
Este ejemplo incluye:
- un widget de tabla que suma los ingresos por viajes por ciudad y los ordena de forma descendente por ingresos
- un widget de serie temporal que muestra el tiempo de espera promedio por ciudad como líneas agrupadas
{
"name": "Uber Marketplace Overview",
"widgets": [
{
"title": "Revenue by City",
"widget_type": "explorer",
"config": {
"tableName": "uber_rides",
"explorerState": {
"graphType": "table",
"aggregation": "sum",
"metric": "price",
"timePreset": "30d",
"granularity": "auto",
"splitBy": ["city"],
"filters": [
{
"logic": "AND",
"conditions": [
{ "field": "status", "operator": "=", "value": "completed" }
]
}
],
"selectedColumns": ["city", "price"],
"orderBy": "price",
"limit": 20,
"orderDirection": "DESC"
}
},
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 }
},
{
"title": "Average Wait Time by City",
"widget_type": "explorer",
"config": {
"tableName": "uber_rides",
"explorerState": {
"graphType": "line",
"aggregation": "avg",
"metric": "wait_time_minutes",
"timePreset": "7d",
"granularity": "hour",
"splitBy": ["city"],
"seriesLimit": null,
"filters": [
{
"logic": "AND",
"conditions": [
{ "field": "status", "operator": "=", "value": "completed" }
]
}
],
"selectedColumns": ["city", "wait_time_minutes"],
"limit": 200,
"orderDirection": "ASC"
}
},
"layout": { "x": 6, "y": 0, "w": 6, "h": 4 }
}
]
}
En ese ejemplo:
Revenue by Cityes un widget de tabla de exploraciónselectedColumns: ["city", "price"]másaggregation: "sum"produce una columnapriceagregada porcityorderBy: "price"ordena según esa columna de ingresos agregadosAverage Wait Time by Cityes un widget de línea ExplorarsplitBy: ["city"]crea una línea por ciudadorderDirection: "ASC"mantiene el eje del tiempo en orden cronológico
Ejemplo: crear un widget de consulta para curvas de sonrisas de retención de cohortes
Este ejemplo utiliza un widget de consulta porque la retención de cohortes es mucho más difícil de expresar en Explorar. Utiliza:
- una definición de cohorte de primer viaje
- una tabla de actividades semanales distinta
- una unión entre el tamaño de las cohortes y los usuarios retenidos
Legible querySql:
WITH first_rides AS (
SELECT
user_id,
date_trunc('week', MIN(timestamp_utc)) AS cohort_week
FROM
uber_rides
WHERE
status = 'completed'
GROUP BY
user_id
),
weekly_activity AS (
SELECT DISTINCT
user_id,
date_trunc('week', timestamp_utc) AS activity_week
FROM
uber_rides
WHERE
status = 'completed'
),
cohort_activity AS (
SELECT
f.cohort_week,
a.activity_week,
date_part('day', a.activity_week - f.cohort_week) / 7 AS weeks_since_first_ride,
COUNT(DISTINCT a.user_id) AS retained_riders
FROM
first_rides f
JOIN weekly_activity a ON a.user_id = f.user_id
WHERE
a.activity_week >= f.cohort_week
GROUP BY
f.cohort_week,
a.activity_week,
date_part('day', a.activity_week - f.cohort_week) / 7
),
cohort_sizes AS (
SELECT
cohort_week,
COUNT(*) AS cohort_size
FROM
first_rides
GROUP BY
cohort_week
)
SELECT
weeks_since_first_ride,
100.0 * retained_riders / cohort_size AS retention_pct,
CAST(ca.cohort_week AS VARCHAR) AS cohort_week
FROM
cohort_activity ca
JOIN cohort_sizes cs ON ca.cohort_week = cs.cohort_week
WHERE
weeks_since_first_ride BETWEEN 0 AND 12
ORDER BY
weeks_since_first_ride ASC,
cohort_week ASC
{
"name": "Uber Rider Retention",
"widgets": [
{
"title": "Weekly Rider Retention Smile Curves",
"widget_type": "query",
"config": {
"querySql": "WITH first_rides AS ( SELECT user_id, date_trunc('week', MIN(timestamp_utc)) AS cohort_week FROM uber_rides WHERE status = 'completed' GROUP BY user_id ), weekly_activity AS ( SELECT DISTINCT user_id, date_trunc('week', timestamp_utc) AS activity_week FROM uber_rides WHERE status = 'completed' ), cohort_activity AS ( SELECT f.cohort_week, a.activity_week, date_part('day', a.activity_week - f.cohort_week) / 7 AS weeks_since_first_ride, COUNT(DISTINCT a.user_id) AS retained_riders FROM first_rides f JOIN weekly_activity a ON a.user_id = f.user_id WHERE a.activity_week >= f.cohort_week GROUP BY f.cohort_week, a.activity_week, date_part('day', a.activity_week - f.cohort_week) / 7 ), cohort_sizes AS ( SELECT cohort_week, COUNT(*) AS cohort_size FROM first_rides GROUP BY cohort_week ) SELECT weeks_since_first_ride, 100.0 * retained_riders / cohort_size AS retention_pct, CAST(ca.cohort_week AS VARCHAR) AS cohort_week FROM cohort_activity ca JOIN cohort_sizes cs ON ca.cohort_week = cs.cohort_week WHERE weeks_since_first_ride BETWEEN 0 AND 12 ORDER BY weeks_since_first_ride ASC, cohort_week ASC",
"chartType": "Line Chart",
"xAxis": "weeks_since_first_ride",
"yAxis": "retention_pct",
"groupBy": "cohort_week"
},
"layout": { "x": 0, "y": 0, "w": 12, "h": 5 }
}
]
}
En ese ejemplo:
weeks_since_first_ridees el eje xretention_pctes el eje ygroupBy: "cohort_week"crea una línea por cohorte de registro, lo que produce la comparación de estilos de curva de sonrisa- Este es un widget de consulta en lugar de un widget de exploración porque la lógica de cohorte depende de múltiples CTE y uniones.
Respuesta
Las solicitudes POST exitosas devuelven 201 Created.
Las solicitudes PATCH exitosas devuelven 200 OK.
Ambos devuelven esta forma:
{
"status": "success",
"dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"team_id": "team_123",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": null,
"created_by": null,
"created_by_api_key_id": "8dff0c1a-8b36-49c3-9e6b-dc0d3ff51d74",
"created_at": "2026-03-26T00:00:00.000Z",
"updated_at": "2026-03-26T00:00:00.000Z",
"url": "/team/acme/dashboard/http-monitoring"
},
"widgets": [
{
"id": "9a6e4f27-d3bf-4a70-bf3a-38fe63211f93",
"title": "Errors Per Hour",
"widget_type": "query",
"config": {
"queryId": null,
"querySql": "SELECT ...",
"chartType": "Line Chart",
"xAxis": "hour",
"yAxis": "errors",
"groupBy": null,
"sourceUrl": null
},
"layout": {
"x": 0,
"y": 0,
"w": 12,
"h": 4
},
"sort_order": 1
}
]
}
Las solicitudes DELETE exitosas devuelven 200 OK con un resumen de eliminación:
{
"status": "success",
"deleted_dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": null,
"widget_count": 6,
"url": "/team/acme/dashboard/http-monitoring"
}
}
Los paneles creados a través de este API tienen alcance de equipo y se atribuyen a la clave API que los creó:
created_byesnullpara paneles creados por API porque una clave API no es un usuariocreated_by_api_key_ides la fila internateam_api_keys.idpara la clave de creación- Los paneles creados a través de la interfaz de usuario todavía usan
created_bycon una identificación de usuario y normalmente dejancreated_by_api_key_idcomonull.
Si el slug normalizado ya existe para su equipo, API devuelve 409 Conflict.
Otros errores comunes:
400 Bad Requestsi faltan campos obligatorios o no son válidos400 Bad Requestsi un widget de consulta incluye SQL que no es de solo lectura404 Not Foundsi el panel de destino no existe para su equipo401 Unauthorizedsi la clave API falta o no es válida400 Bad Requestsi la clave API solo tiene alcanceread