Saltar al contenido
Telemetry
Explorar documentación
Referencia de la APIActualizado el 27 de julio de 2026Revisado por los equipos editorial y de producto de Telemetry22 min de lectura

Usa esta documentación con tu agente de programación

Abra un paquete de mensajes enfocados para Claude Code, Codex, Cursor u otro agente de codificación, luego adáptelo al flujo de trabajo que se describe aquí.

En esta página
  1. Listar respuesta
  2. crear cuerpo
  3. Crear respuesta
  4. Editar cuerpo
  5. Editar respuesta
  6. Eliminar cuerpo
  7. Eliminar respuesta
  8. Errores comunes
  9. Campos de widgets
  10. Comprender el diseño
  11. Configuración del widget de consulta
  12. Ejemplo: widget de consulta con un eje x de gráfico de barras categórico
  13. Configuración del widget de encabezado
  14. Configuración del widget de texto libre
  15. Configuración del widget del explorador
  16. Ejemplo: crear un panel vacío
  17. Ejemplo: listar paneles
  18. Ejemplo: crear un panel vacío
  19. Ejemplo: cambiar el nombre de un panel y actualizar su descripción
  20. Ejemplo: reemplazar todos los widgets en un panel existente
  21. Ejemplo: eliminar un panel
  22. Ejemplos de widgets comunes
  23. Ejemplo: crear un panel con dos widgets de exploración comunes
  24. Ejemplo: crear un widget de consulta para curvas de sonrisas de retención de cohortes
  25. Respuesta

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 /dashboard acepta read, write o read-and-write.
  • POST /dashboard, PATCH /dashboard y DELETE /dashboard requieren write o read-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:

  • PATCH aú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:

  • dashboard contiene los metadatos del panel guardados y url
  • widgets contiene 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 Request si el cuerpo de JSON no es válido o el cuerpo de la solicitud no es un objeto JSON
  • 400 Bad Request si page o pageSize no es un entero positivo en GET /dashboard
  • 400 Bad Request si falta dashboardId o dashboardSlug para PATCH o DELETE
  • 400 Bad Request si PATCH omite todos los campos editables
  • 400 Bad Request si name, slug, description o widgets no superan la validación
  • 400 Bad Request si un widget de consulta contiene SQL que no es de solo lectura
  • 403 Forbidden si la clave API no tiene permiso para la operación solicitada
  • 401 Unauthorized si la clave API falta o no es válida
  • 404 Not Found si el tablero no existe para el equipo de claves API
  • 409 Conflict si 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 = 0
  • w = 12
  • h = 4 para query y explorer
  • h = 2 para header
  • h = 3 para free_text
  • y se 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, xAxis y yAxis deben coincidir con las columnas reales devueltas por querySql.
  • Para Bar Chart, xAxis puede ser texto/categórico, numérico o temporal.
  • Para Scatter Plot, Line Chart y Stacked Area Chart, xAxis debe ser numérico o temporal.
  • Para todos los gráficos que no sean table, yAxis debe ser numérico.
  • Para los ejes x categóricos Bar Chart, el widget conserva el orden de los resultados de la consulta. Utilice ORDER BY en querySql para 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 conserva defaultQuery, tab, chartType, xAxis, yAxis y groupBy.

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:

  • endpoint es una columna de texto del eje x
  • requests es la columna numérica del eje y
  • ORDER BY requests DESC controla 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 title es el título de la sección visible.
  • description admite 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:

  • content admite rebajas. Se escapa el HTML sin formato antes de renderizarlo en la interfaz de usuario.
  • El widget title todaví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:

  • city
  • price
  • wait_time_minutes
  • status
  • user_id
  • timestamp_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 City es un widget de tabla de exploración
  • selectedColumns: ["city", "price"] más aggregation: "sum" produce una columna price agregada por city
  • orderBy: "price" ordena según esa columna de ingresos agregados
  • Average Wait Time by City es un widget de línea Explorar
  • splitBy: ["city"] crea una línea por ciudad
  • orderDirection: "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_ride es el eje x
  • retention_pct es el eje y
  • groupBy: "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_by es null para paneles creados por API porque una clave API no es un usuario
  • created_by_api_key_id es la fila interna team_api_keys.id para la clave de creación
  • Los paneles creados a través de la interfaz de usuario todavía usan created_by con una identificación de usuario y normalmente dejan created_by_api_key_id como null.

Si el slug normalizado ya existe para su equipo, API devuelve 409 Conflict.

Otros errores comunes:

  • 400 Bad Request si faltan campos obligatorios o no son válidos
  • 400 Bad Request si un widget de consulta incluye SQL que no es de solo lectura
  • 404 Not Found si el panel de destino no existe para su equipo
  • 401 Unauthorized si la clave API falta o no es válida
  • 400 Bad Request si la clave API solo tiene alcance read

Función relacionada

Convierta una consulta validada en una vista operativa enfocada y revisable.

Autores de la página y referencias

El equipo editorial de Telemetry es responsable de esta explicación; el equipo de producto revisa el comportamiento, los ejemplos y las limitaciones.

Cómo revisamos nuestra documentación