Appearance
Insights: detección de anomalías operativas
English: Insights: anomaly detection.
Esta guía cubre la evidencia de anomalías que la plataforma registra por sí sola y las tres rutas de la API que la leen y la reconocen. Describe solo la API; no da a entender que exista una pantalla del dashboard.
Disponibilidad
| Capacidad | Disponibilidad |
|---|---|
GET /v1/insights/anomalies, GET /v1/insights/series, POST /v1/insights/anomalies/{id}/ack | Publicado en core v0.101.0 (core#1983; ADR #243) |
La clave de sensibilidad insights_anomaly_k | Publicado en core v0.101.0 (core#1983; ADR #243) |
Las tres rutas y sus esquemas están incluidos en el documento OpenAPI vendorizado y generados en la Referencia de la API.
Qué detecta
El evaluador corre cada 15 minutos para todos los tenants. Siempre está activo: no hay clave de activación, solo lee y escribe evidencia, y nunca interviene en una decisión de marcación.
Vigila cinco métricas, en cubetas por hora, para cada campaña y para el tenant completo:
metric | Dirección dañina | Significado |
|---|---|---|
contact_rate | baja | Llamadas contestadas por una persona sobre marcaciones realizadas |
abandon_rate | sube | Llamadas abandonadas sobre llamadas contestadas por una persona |
cost_per_contact | sube | Costo de minutos salientes (USD) sobre llamadas contestadas por una persona |
gate_block_rate | sube | Rechazos de la puerta de cumplimiento sobre rechazos más marcaciones realizadas; una serie por gate |
media_timeout_rate | sube | Tramos contestados cerrados con MEDIA_TIMEOUT sobre llamadas contestadas |
La regla es zscore.v1. Para cada cubeta por hora toma los 7 días anteriores como línea base (sin la cubeta actual) y calcula la media y la desviación estándar. El umbral es mean ± k · max(stddev, floor), y solo el lado dañino de cada métrica puede abrir una anomalía. El piso es 0.01, salvo en cost_per_contact, donde es el mayor entre el 5 % de la media y 0.01. Evita que una serie casi constante alerte por ruido.
Pisos de muestra
Una cubeta no se evalúa si no tiene datos suficientes:
- Al menos 30 en el denominador de la cubeta (
sample_n): marcaciones realizadas paracontact_rate, llamadas contestadas por una persona paraabandon_rateycost_per_contact, llamadas contestadas paramedia_timeout_rate, y rechazos más marcaciones realizadas paragate_block_rate. - Al menos 24 cubetas de línea base detrás de ella (
baseline_n).
Por debajo de cualquiera de los dos pisos no se escribe ninguna fila. GET /v1/insights/series informa los pisos como min_sample y min_baseline y devuelve band: null mientras la línea base es corta.
opened y resolved
La evidencia es solo de anexar. Una anomalía es su fila opened, que lleva el valor observado, la línea base, el umbral, k, los tamaños de muestra y la serie por hora. Queda resolved cuando una fila resolved posterior la cita, algo que el evaluador escribe cuando la métrica vuelve a estar dentro del umbral. En la API esto es state: "open" o state: "resolved", y resolution contiene la fila de cierre (null mientras está abierta). Las filas nunca se actualizan ni se borran.
Sensibilidad: insights_anomaly_k
k es una clave por tenant, escrita por un operador de la plataforma, no a través de la API del tenant. Es un entero en décimas: el valor por defecto es 30 (k = 3.0) y el rango aceptado va de 30 a 200 (3.0 a 20.0). Un valor fuera del rango se rechaza al escribirlo.
Subirla solo silencia a un tenant ruidoso; nunca activa nada, porque el evaluador no tiene clave de encendido/apagado. Cada anomalía registra el k vigente cuando se abrió.
Autenticación y permisos
Todas las rutas son de ámbito tenant. Envía una llave de API dd_ o un token de inicio de sesión del dashboard:
http
Authorization: Bearer dd_example-not-a-real-key| Operación | Llave dd_ | Inicio de sesión del dashboard (ddu_) |
|---|---|---|
| Listar anomalías, leer una serie | Sí; read es null | Sí; read refleja tu reconocimiento |
unread=true | 400 (una llave no tiene bandeja) | Sí |
| Reconocer | 403 | Sí |
El reconocimiento es por persona: cada usuario reconoce por sí mismo, y el reconocimiento de un usuario no marca la anomalía como leída para nadie más.
Listar anomalías
http
GET /v1/insights/anomalies?state=open&metric=abandon_rate&limit=50Filtros: campaign_id, metric, state (open o resolved), unread (solo con inicio de sesión), limit (50 por defecto, máximo 200) y cursor. Los resultados van del más nuevo al más antiguo; pasa el next_cursor anterior para paginar, y es null en la última página.
json
{
"anomalies": [
{
"id": "6f1c1f0e-8f4e-4c3e-9a61-0d7c1b6f2a10",
"campaign_id": null,
"metric": "abandon_rate",
"gate": null,
"rule": "zscore.v1",
"direction": "up",
"state": "open",
"window_start": "2026-10-09T14:00:00Z",
"window_end": "2026-10-09T15:00:00Z",
"observed": 0.09,
"baseline_mean": 0.02,
"baseline_stddev": 0.01,
"stddev_floor": 0.01,
"threshold": 0.05,
"k": 3.0,
"sample_n": 412,
"baseline_n": 160,
"series": [],
"opened_at": "2026-10-09T15:07:00Z",
"resolution": null,
"read": null,
"acknowledged_at": null
}
],
"next_cursor": null
}Los valores son ilustrativos; aquí series está abreviada y, en una respuesta real, lista las cubetas de la línea base seguidas de la cubeta que cruzó el umbral, de la más antigua a la más nueva.
Leer una serie
http
GET /v1/insights/series?metric=contact_rate&from=2026-10-08T00:00:00Z&to=2026-10-09T00:00:00Zmetric es obligatorio. campaign_id limita a una campaña; sin él, la serie es la del tenant completo. gate es obligatorio para gate_block_rate y se rechaza para cualquier otra métrica. from y to se redondean hacia abajo a la hora; to es por defecto el momento actual, from 48 horas antes, y la ventana no puede exceder 14 días.
Cada cubeta lleva su band (mean, stddev, stddev_floor, lower, upper, baseline_n), calculada con la misma aritmética que el evaluador, de modo que una gráfica dibujada con ella no puede contradecir el umbral publicado.
json
{
"rule": "zscore.v1",
"metric": "contact_rate",
"campaign_id": null,
"gate": null,
"direction": "down",
"k": 3.0,
"min_sample": 30,
"min_baseline": 24,
"from": "2026-10-08T00:00:00Z",
"to": "2026-10-09T00:00:00Z",
"buckets": [
{
"start": "2026-10-08T00:00:00Z",
"sample": 380,
"numerator": 95,
"denominator": 380,
"value": 0.25,
"band": {
"mean": 0.24,
"stddev": 0.02,
"stddev_floor": 0.01,
"lower": 0.18,
"upper": 0.3,
"baseline_n": 168
}
}
]
}Reconocer una anomalía
http
POST /v1/insights/anomalies/6f1c1f0e-8f4e-4c3e-9a61-0d7c1b6f2a10/ack
Authorization: Bearer ddu_example-not-a-real-loginLa llamada no tiene cuerpo. Necesita un inicio de sesión del dashboard: una llave dd_ recibe 403. Un id que no es una anomalía de tu tenant (de otro tenant, una fila resolved, un id inventado) recibe 404. Reconocer es idempotente: una segunda llamada responde 200 con el reconocimiento que ya existe.
json
{
"ack": {
"anomaly_id": "6f1c1f0e-8f4e-4c3e-9a61-0d7c1b6f2a10",
"user_id": "0b2a9f6e-2b53-4a7c-8f43-1c2d3e4f5a6b",
"acknowledged_at": "2026-10-09T15:20:00Z"
}
}Lo que no cubre esta guía
El evento de webhook anomaly.detected no forma parte de estas rutas; esta guía no describe ninguno.