Skip to content

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 ​

CapacidadDisponibilidad
GET /v1/insights/anomalies, GET /v1/insights/series, POST /v1/insights/anomalies/{id}/ackPublicado en core v0.101.0 (core#1983; ADR #243)
La clave de sensibilidad insights_anomaly_kPublicado 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:

metricDirección dañinaSignificado
contact_ratebajaLlamadas contestadas por una persona sobre marcaciones realizadas
abandon_ratesubeLlamadas abandonadas sobre llamadas contestadas por una persona
cost_per_contactsubeCosto de minutos salientes (USD) sobre llamadas contestadas por una persona
gate_block_ratesubeRechazos de la puerta de cumplimiento sobre rechazos más marcaciones realizadas; una serie por gate
media_timeout_ratesubeTramos 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 para contact_rate, llamadas contestadas por una persona para abandon_rate y cost_per_contact, llamadas contestadas para media_timeout_rate, y rechazos más marcaciones realizadas para gate_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ónLlave dd_Inicio de sesión del dashboard (ddu_)
Listar anomalías, leer una serieSí; read es nullSí; read refleja tu reconocimiento
unread=true400 (una llave no tiene bandeja)Sí
Reconocer403Sí

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=50

Filtros: 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:00Z

metric 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-login

La 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.

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.