Ext Ops Panel
Abrir el panel

Inicio/Docs/Métricas personalizadas

Contrato de métricas personalizadas

Versión del contrato 2.1.0 · payload schema_version 2.0

Las métricas personalizadas permiten que el propio servicio de tu extensión envíe los números que le importan (conversiones, ingresos, estado del backend, embudos) al mismo panel que ya registra las instalaciones y el estado en Chrome Web Store. El contrato está basado en metadatos y tiene doble superficie: cada métrica declara dónde y cómo aparece tanto en el panel como en el estado de Telegram, de modo que el panel sigue siendo un renderizador genérico sin código específico de ningún producto.

↓ Descargar OpenAPI 3.1 (YAML) /custom-metrics-openapi.yaml · info.version 2.1.0

1. Cómo funciona (doble superficie)

Tu servicio expone un endpoint de métricas. Ext Ops Panel es el cliente: consulta ese endpoint (nunca al revés) y renderiza la respuesta. La respuesta alimenta dos superficies a la vez:

El panel combina tus métricas con las métricas base propias (instalaciones / desinstalaciones / reinstalaciones / comentarios / Chrome Web Store / salud), que viven en la pestaña reservada base y en la línea de Telegram del mismo nombre.

2. El endpoint y la autenticación

El panel llama a tu endpoint con GET y espera el payload v2 como respuesta 200. Los dos modos de consulta comparten la misma estructura:

La autenticación es una de none, bearer o api_key_header. Los secretos se almacenan cifrados en reposo en el backend del panel y nunca se entregan a la extensión del panel. Si una consulta falla, el panel se degrada de forma controlada (última instantánea o bloque oculto) sin romper las métricas base.

3. Estructura del payload

El payload de nivel superior contiene el diseño (pestañas, secciones, líneas de Telegram) y el array de métricas.

CampoTipoNotas
schema_versionstringObligatorio. Debe ser "2.0".
generated_atstring (date-time)Momento en que se compuso el payload (frescura / detección de inactividad).
tabsTab[]Pestañas del panel. Véase la sección 4.
sectionsSection[]Grupos de métricas dentro de una pestaña (tab, id, title, order).
telegramTelegramMetaPlantilla header + lines[]. Véase la sección 7.
metricsMetric[]Las propias métricas. Véanse las secciones 5 y 6.

4. Pestañas e iconos de pestañas

Una pestaña es { id, title, order, icon? }. La pestaña reservada base pertenece al propio panel (icon="layout-dashboard"); tu servicio añade el resto. Cuando hay muchas pestañas, la activa muestra icono + título y las demás se reducen al icono (el título queda en el tooltip y en aria-label).

Dos ids de pestaña están reservados para el panel: base (la pestaña Basic) y monitoring (la pestaña Monitoring integrada que llenan los informes de heartbeat, consulte el contrato de monitorización). No los declare en tabs: una declaración se ignora y el panel fija su título, icono y orden. Las métricas siguen apuntando a ellos con dashboard.tab.

El valor icon es una de dos cosas:

El panel incluye un catálogo seleccionado de 60 nombres de Lucide (ligero para el paquete MV3). El mismo catálogo sirve para los iconos de pestañas y los iconos de objetivos (sección 5.3):

5. Métrica: valor + dos bloques de ubicación

Una métrica lleva un valor tipado más dos bloques independientes: dashboard (dónde y cómo en la extensión) y telegram (dónde y cómo en el mensaje). Cualquiera de ellos se puede omitir u ocultar mediante show:false, de modo que una métrica puede existir en una sola superficie.

CampoNotas
idId de métrica estable, único en el payload.
kindTipo de datos (apartado 5.1). Selecciona qué campos de valor se aplican.
labelEtiqueta canónica legible.
value y campos del tipoLos campos del payload dependen de kind (sección 5.1).
severityUno de ok, info, warn, crit (se asigna a color / emoji).
shareableBooleano opcional, por defecto true. Con false la métrica es solo del propietario: en un proyecto compartido en modo de solo lectura se elimina en el servidor y nunca llega a un observador ni a la vista pública (úsalo para métricas financieras). Regula el acceso, no la ubicación.
dashboard{ show, tab, section, order, name, render, options } (sección 5.2).
telegram{ show, line, pos, format, sep, when } (apartados 6 y 8).

5.1. Tipos (tipo de datos + payload)

Los tipos son un conjunto abierto: el renderizador ignora un tipo desconocido (compatibilidad hacia delante).

kindCampos del payloadUso de ejemplo
numbervalue (+ opcional delta, unit)recuentos de instalaciones / comentarios
deltavalue, delta (renderiza 1200 (+72))total (+hoy)
percentvalue (0 a 100)tasa de éxito del motor
durationseconds o minutesp50/p95/media, frescura
bytesbytes o kbtamaño de salida (~522 KB)
ratingvalue, count5.0★ (1)
moneyvalue, currency, opcional deltaMRR, gasto
statusstate + severitysemáforo de salud
seriespoints:[{date,count}], series:[{name,points}] opcional (múltiple), apiladas con stack: trueinstalaciones diarias, usuarios activos
intradayseries:[{name,intraday:[{minute,value}]}], opcional step_min (predeterminado 15), tzhoy en intervalos de 15 minutos
hour_histogram{ weekdays:[...], start, end, tz }conversiones por hora
funnelsteps:[{key,label,value}]de la página de precios al pago
breakdownitems:[{label,value,unit?,sub?/money?,country?,parts?}], opcional ranges:{today,week,month,all}suscripciones por plan, por motor, por superficie; visitas por origen o país
listitems:[{text,count?,severity?}] en el nivel superior de la métrica, no dentro de valueerrores recientes
tableitems:[{…}], un objeto por fila; columnas de dashboard.options.columns, si no, las claves de la primera filacolas, envíos recientes
textvalue (string)texto libre

Una métrica table:

{ "id": "queues", "kind": "table", "label": "Mail queues",
  "items": [ { "queue": "outbound", "size": 3, "oldest": "2m" },
             { "queue": "deferred", "size": 0, "oldest": "-" } ],
  "dashboard": { "show": true, "tab": "backend", "section": "engines", "order": 5, "render": "table",
                 "options": { "columns": ["queue", "size", "oldest"] } } }

5.2. Render (cómo se muestra en el panel)

dashboard.render elige la representación visual (normalmente coincide con kind, pero puede diferir, p. ej. mostrar un percent como badge). También es un conjunto abierto.

renderAspectoOpciones (dashboard.options)
kpiTarjeta: nombre + valor grande + delta; con stack, las partes en una fila debajo del valortone, stack
badgePíldora de estado / pictograma según la gravedad–
chartGráfico de líneas (Mes/Todo el periodo, media móvil de 7 días, hover); con stack: true, columnas apiladas por día con una fila Totalseries, defaultMode, stack
intradayHoy por intervalos (96 por día UTC): barras apiladas o líneas suavizadas, eje de tiempo HH:MM, lectura de todas las series al pasar el cursormode (bars | lines, predeterminado lines)
hour_histogramBarras por hora, agrupadas en Laborables / Fin de semana / Por día de la semanaseries
funnelPasos con la conversión entre ellos–
breakdownBarras categóricas horizontales con valor + porcentaje del total. Con ranges, un selector hoy / 7 días / 30 días / todo; con map: countries, un mapa del mundo sobre la lista; con stack, barras divididas en partes conmutablesitems, defaultRange, map, stack
tableTabla (por ejemplo, comentarios)columns
textPárrafo / en línea–
statusEstado del semáforo por gravedad–
listLista de elementos con recuento/gravedad opcional–

intraday frente a chart. El eje de chart es diario (points[].date = YYYY-MM-DD) y su vista mensual se recorta a los últimos 30 puntos, por lo que los 96 intervalos de hoy no caben en él. intraday es el gráfico Hoy propio del panel expuesto como tipo del contrato: la misma convención en la que minute cuenta los minutos desde el inicio del día UTC (0, 15, …, 1425), pero las series y los datos los declara la fuente. Envía solo los intervalos no vacíos; el panel completa el resto.

Un intervalo vacío es null, no 0. Para un promedio (p. ej., el tiempo medio de conversión) un cero sería una mentira, así que la línea se interrumpe en lugar de caer a cero. Un intervalo ausente y value: null son equivalentes: la línea se interrumpe, no se dibuja ninguna barra y la lectura al pasar el cursor muestra un guion.

Apilado: el total y sus partes en un gráfico y una lista. Cuando una métrica se compone de partes (por ejemplo, las páginas vistas de dos sitios), envía el total y las partes como UNA métrica: el panel dibuja las partes una sobre otra y la leyenda activa y desactiva cada una, sin pestañas adicionales ni filtros desplegables. Ambas extensiones son aditivas: un renderizador que no las conoce dibuja la misma métrica que antes.

Gráfico: kind: "series" con dashboard.options.stack: true. Las series se apilan en el orden del array: la primera es la capa inferior. El panel dibuja una columna por día (en todo el periodo, por ventana de 7 días) con un segmento por serie visible, así que la parte superior de una columna es la suma exacta de las series visibles; un día en que todas valen cero no tiene columna. Al hacer clic en una entrada de la leyenda se oculta una capa (la última visible sigue activa); el resto se redibuja desde cero y el eje de valores sigue al total visible. Al pasar el cursor por una columna, o al llegar a ella con las flechas, la lectura muestra cada capa visible y una fila Total. Todo el periodo apila la media móvil de 7 días de cada serie, marcada con ~. Cualquier otro valor de stack mantiene las líneas superpuestas.

Lista: kind: "breakdown" con dashboard.options.stack como lista ordenada de partes [{key, label}], y parts: {"key": number} en cada elemento, en items y en cada ventana de ranges. value debe ser igual a la suma de todas las partes. El panel muestra un chip por parte que la activa o desactiva para toda la tarjeta, en todas las ventanas. Una fila muestra la suma de sus partes visibles, las filas en cero se ocultan, la lista se reordena por esa suma y la barra se divide en un segmento por cada parte visible. Con map: "countries", los círculos siguen las sumas visibles. Un elemento sin parts cuenta entero, se desactive lo que se desactive.

{ "id": "site_sources", "kind": "breakdown", "label": "Sources",
  "items": [ { "label": "Search engines", "value": 812, "parts": { "makcards": 790, "journalingapp": 22 } } ],
  "ranges": { "today": [ { "label": "Search engines", "value": 30, "parts": { "makcards": 29, "journalingapp": 1 } } ],
              "month": [ { "label": "Search engines", "value": 812, "parts": { "makcards": 790, "journalingapp": 22 } } ] },
  "dashboard": { "show": true, "tab": "site", "section": "site_sources", "render": "breakdown",
                 "options": { "defaultRange": "month",
                              "stack": [ { "key": "makcards", "label": "makcards.online" },
                                         { "key": "journalingapp", "label": "journalingapp.app" } ] } } }

Tarjeta KPI: una tarjeta kpi de tipo number o delta acepta la misma lista dashboard.options.stack y parts: {"key": number} en la propia métrica. El valor grande sigue siendo value, que debe ser igual a la suma de todas las partes. Debajo, el panel muestra las partes en una fila en el orden de la pila (en una tarjeta estrecha la fila se ajusta), con el color que ese mismo nombre tiene en un gráfico apilado de la pestaña; una parte en cero se atenúa. La tarjeta no tiene interruptores. Los titulares de la lista de proyectos y Telegram siguen leyendo value.

{ "id": "site_views_today", "kind": "number", "label": "Page views", "value": 42,
  "parts": { "portal": 30, "cabinet": 4, "backoffice": 8 },
  "dashboard": { "show": true, "tab": "site", "section": "site_today", "render": "kpi",
                 "options": { "stack": [ { "key": "portal", "label": "zakazindia.ru" },
                                         { "key": "cabinet", "label": "Client cabinet" },
                                         { "key": "backoffice", "label": "Backoffice" } ] } } }

5.3. La lista de proyectos: usuarios y objetivos de la pestaña Básica

La lista de proyectos muestra algunos números de un servicio web sin abrirlo. El panel los lee solo de la pestaña base y solo cuando la métrica tiene un value numérico. Gana la primera coincidencia.

MétricaCómo la encuentra el panelDónde se muestra
DAU, WAU, MAUEl id, el label o el nombre en el dashboard es exactamente dau, wau o mau (sin distinguir mayúsculas).WAU y MAU en la columna Usuarios, DAU en Hoy.
Objetivos 1 a 3El id es exactamente goal_1, goal_2 o goal_3.Hoy, en la misma línea que DAU: un icono y un número.

Un objetivo es una acción objetivo de tus usuarios: registros, compras, formularios enviados. Eliges la ventana del número y el panel lo muestra tal cual. Recomendamos contar el día de hoy y nombrar la ventana en el label (por ejemplo, Registros hoy). El label es el tooltip, hasta 80 caracteres. El campo icon admite un nombre del catálogo de la sección 4; los emoji no se aceptan para objetivos. Sin icono, o con un nombre desconocido, el panel dibuja target. Un cero se muestra como un guion atenuado.

{ "id": "goal_1", "kind": "number", "label": "Sign-ups today", "value": 12, "icon": "user-plus",
  "dashboard": { "show": true, "tab": "base", "section": "goals", "render": "kpi" } }

El panel guarda solo los últimos valores, como con DAU. Cada consulta correcta los reemplaza, y un objetivo que ya no está en la respuesta desaparece de la lista.

Varias superficies

Una fuente de métricas personalizadas pertenece a una superficie de tu servicio, y el panel guarda los números por superficie. La lista de proyectos muestra su suma; la página del proyecto muestra cada superficie por separado.

Extensiones de Chrome

Una extensión usa la misma convención. Cuando su feed envía dau y objetivos en la pestaña base, la lista de proyectos los muestra en una línea más bajo los contadores de instalaciones, desinstalaciones, comentarios y reinstalaciones, con el mismo formato que la línea Hoy de un servicio web.

5.4. Umbrales (warn y crit)

Una métrica numérica puede llevar thresholds. Entonces el panel calcula por sí mismo la severidad de la métrica, en cada consulta y en cada recepción de heartbeat, y muestra los límites junto al valor: 41 (threshold 20/100), amarillo entre warn y crit, rojo a partir de crit. La severidad que su servicio envía para esa métrica se sustituye por la calculada.

CampoSignificado
directionabove: peor cuanto más alto (cola, CPU). below: peor cuanto más bajo (disco libre, días restantes).
warn, critNúmeros. Con above, un valor igual o superior a warn es amarillo y igual o superior a crit es rojo; below lo invierte. Con above warn no debe superar a crit, con below no debe quedar por debajo.
unitEtiqueta opcional de hasta 16 bytes para el texto: threshold 80/95%.
recoverHistéresis opcional: una métrica que estaba en warn o crit vuelve a ok solo cuando el valor ha pasado recover; entre ambos sigue en warn.
{
  "id": "queue",
  "kind": "number",
  "label": "Mail queue",
  "value": 41,
  "thresholds": { "direction": "above", "warn": 20, "crit": 100, "recover": 15 },
  "dashboard": { "show": true, "tab": "backend", "section": "mail", "order": 1, "render": "kpi" },
  "telegram": { "show": true, "line": "mail", "pos": 10, "format": "queue {value}", "when": "severity>=warn" }
}

Consulta programada. En un servicio web puede activar la consulta programada de una fuente en su configuración. El panel consulta entonces el endpoint con un intervalo que va desde el mínimo de su plan (5 minutos en Trial e Indie, 1 minuto en Studio y Fleet) hasta 24 horas, con la misma petición que cualquier consulta: un User-Agent honesto y como máximo una petición por minuto. Ocupa una unidad de monitorización, por lo que no está disponible en Free. Un umbral superado abre un incidente para esa métrica y le avisa; una superación repetida no vuelve a avisar. El incidente se cierra tras una consulta sin superación si el intervalo es de 1 hora o más, y tras dos consultas seguidas sin superación si es más corto. Una consulta fallida no cambia nada y muestra su motivo como último error. Hasta 20 métricas con umbrales por fuente generan incidentes.

6. La paleta la controla el panel

Los colores de los gráficos y de las series los asigna el panel a partir de los tokens de marca, no la fuente. options.series[] solo lleva la identidad ({key,label,order}); un campo color en el payload es orientativo y se ignora. Las claves semánticas se asignan a tokens fijos (paid al color de acento, free al neutro atenuado, failed/error a peligro, ready/success a éxito); las demás claves toman la paleta categórica de la marca por posición. Los rellenos y las líneas de las series mantienen un contraste de al menos 3:1 en ambos temas (WCAG 1.4.11), y el texto y las etiquetas de al menos 4,5:1. Así la marca queda protegida de colores hex externos arbitrarios.

7. Plantillas de Telegram

telegram.header es el encabezado del mensaje; telegram.lines[] son las líneas lógicas ({ id, order, prefix, when }). El compositor reúne todas las métricas con un telegram.line dado, filtra por when, ordena por pos, renderiza cada una con su plantilla format y las une con el sep de cada métrica. La línea prefix se imprime primero. Todo es texto plano.

Marcador de posiciónResultado
{value}Por tipo (número 1,200; porcentaje 99%)
{value:k}1.2k / 3.0M (compacto)
{delta:+}+72 / -3
{value:pct}99%
{seconds:dur}28s / 5m 47s
{minutes:age}45m / 2h 30m / never
{bytes:size}~522KB / 1.4MB
{value}★ ({count})5.0★ (1)
{sev}Emoji de gravedad: 🟢 ok, 🟡 warn, 🔴 crit, ⚪ idle
{threshold}El umbral de la métrica: threshold 20/100. Si no está en el formato, a una métrica con umbrales se le añade (threshold 20/100) al final.
{trend}↗ / ↘ (±5pp vs 7d)

8. Condiciones de visibilidad (cuándo)

when controla si se muestra un campo de métrica (o una línea de Telegram completa). Valores:

whenMuestra el campo
alwaysSiempre (predeterminado).
nonzeroSolo cuando el valor no es 0 / vacío.
severity>=warnSolo cuando la gravedad es warn o crit.
changedSolo cuando cambió desde la instantánea anterior.

Los valores when desconocidos se comportan como always (compatibilidad hacia delante).

9. Ejemplo de payload

{
  "schema_version": "2.0",
  "generated_at": "2026-07-02T23:17:30Z",
  "tabs": [
    { "id": "conv", "title": "Conversions", "order": 1, "icon": "trending-up" }
  ],
  "sections": [
    { "tab": "conv", "id": "by_hour", "title": "By hour", "order": 0 }
  ],
  "telegram": {
    "header": "{name} • {cws.users} • {cws.rating}★ ({cws.rating_count})",
    "lines": [
      { "id": "base", "order": 0 },
      { "id": "conv", "order": 1, "prefix": "{sev} Conv " }
    ]
  },
  "metrics": [
    {
      "id": "conv_today",
      "kind": "number",
      "label": "Conversions today",
      "value": 254,
      "severity": "ok",
      "shareable": true,
      "dashboard": {
        "show": true,
        "tab": "conv", "section": "by_hour", "order": 10,
        "name": "Today", "render": "kpi", "options": {}
      },
      "telegram": {
        "show": true,
        "line": "conv", "pos": 20,
        "format": "today {value}", "sep": " • ", "when": "always"
      }
    }
  ]
}
Solo se admite la versión 2 nativa. Tu servicio devuelve métricas más un diseño listo según este contrato; no hay conectores para formatos heredados. Los tipos y los tipos de renderizado salen de los conjuntos declarados arriba; los desconocidos se ignoran, de modo que una fuente más nueva nunca rompe una versión más antigua del panel.

10. El contrato legible por máquina

La descripción formal e importable de este endpoint es el archivo OpenAPI 3.1. Lleva la misma versión de contrato (2.1.0) y es el único artefacto canónico legible por máquina, común a todos los idiomas.

↓ Descargar OpenAPI 3.1 (YAML) /custom-metrics-openapi.yaml