Skip to content

Estadísticas y uso ​

Generado desde DialerDigital/core commit 0fc18008b3c8 — especificación openapi/dialer-v1.yaml v0.134.0. No se edita a mano; consulta Cómo se construye esta referencia. Descargar la especificación OpenAPI.

English version

Traducción en curso

Esta referencia se genera desde la especificación OpenAPI. Las cadenas que todavía no tienen traducción aparecen en inglés.

Contadores operativos, resúmenes de marcaciones bloqueadas, medición de uso.

Endpoints ​

MétodoRutaResumen
GET/v1/stats/summaryContadores operativos de hoy (día UTC)
GET/v1/stats/contact-windowsTasa de contacto por hora local del consumidor, mejor ventana, teléfonos por persona
GET/v1/stats/promisesResumen del embudo de PTP, totales del tenant y por campaña
GET/v1/stats/penetrationAnalítica de penetración a nivel de persona
GET/v1/stats/abandonmentInforme duradero de abandono de todas las campañas, en una sola respuesta
GET/v1/stats/blockedResumen duradero de marcaciones bloqueadas
GET/v1/stats/capacityTechos de capacidad del tenant + uso en vivo (ADR #12)
GET/v1/stats/ai-usageMedición de minutos de IA de voz (lectura para facturación)
GET/v1/stats/sms-usageMedición de segmentos de SMS (lectura para facturación)

Contadores operativos de hoy (día UTC) ​

http
GET /v1/stats/summary

Calculado a partir del CDR solo anexo + el registro de puestos en vivo: verdad reconstruible, no estado en caché.

Respuestas

200 ​

Contadores de hoy.

CampoTipoObligatorioDescripción
datestring (date)no
window"utc_day"no
calls_todayintegerno
connected_todayintegerno
connect_ratenumberno
agents_onlineintegerno
agents_availableintegerno
campaigns_runningintegerno
json
{
  "date": "2026-07-05",
  "window": "utc_day",
  "calls_today": 412,
  "connected_today": 67,
  "connect_rate": 0.1626,
  "agents_online": 5,
  "agents_available": 3,
  "campaigns_running": 2
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

503 ​

Los contadores no se pudieron leer dentro del tiempo límite de la sentencia (report_timeout).

Tasa de contacto por hora local del consumidor, mejor ventana, teléfonos por persona ​

http
GET /v1/stats/contact-windows

Para cada hora del día (0-23) en la hora local del CONSUMIDOR: cuántos intentos se realizaron, cuántos llegaron a una persona (answered_human) y la tasa. La hora es la del consumidor, no la del tenant ni la UTC: usa la zona que la compuerta de horario de silencio congeló en el intento cuando se realizó (las zonas del código de área del número más la zona declarada del contacto), incluido el horario de verano. Se deriva al leer a partir del CDR solo anexo; no se guarda ningún contador.

Un intento es una llamada saliente original realizada por la plataforma. Las llamadas entrantes, las piernas de transferencia externa y las filas que la plataforma nunca realizó no son intentos. Un contacto es un intento con al menos un resultado answered_human.

Un intento cuya hora local no puede conocerse se cuenta en unknown, nunca se reparte entre las horas: no_zone (nada congelado, o una zona que la base de datos de zonas horarias no conoce) y ambiguous_zone (un código de área que abarca varias zonas, o una zona declarada que no coincide con él). unknown.attempts es la suma de ambos motivos.

best_window son las dos horas locales consecutivas con la mayor tasa de contacto, y solo se nombra cuando CADA UNA de las dos horas reúne al menos min_attempts intentos (ajuste del tenant contact_window_min_attempts, por defecto 50). En caso contrario es null y best_window_reason explica por qué (insufficient_attempts, o no_resolved_attempts cuando ningún intento tenía una hora local conocida); nunca se inventa una hora a partir de un puñado de llamadas. Los empates van a la hora más temprana; la ventana no cruza la medianoche. Ambas horas deben además estar dentro de la ventana legal de llamadas [08:00, 21:00), de modo que nunca se recomiende una ventana en un horario en que la plataforma se negaría a marcar; las horas fuera de ella siguen apareciendo en hours pero no pueden anclar best_window.

weekdays es la misma tasa de contacto sobre el eje de día de la semana ISO local del consumidor (1 = lunes ... 7 = domingo). applied indica si el ajuste best_time_ranking_enabled del tenant está activado (el flujo del marcador ordena los leads según estos datos), source el nivel que lo respalda (campaign, portfolio o none), y weights la tabla de pesos que el marcador aplica a cada celda (día de la semana, hora local) dentro de la ventana legal mientras applied es verdadero, con el nivel que respaldó esa celda; cuando applied es falso el marcador mantiene el orden neutro y source/weights muestran lo que aplicaría con el ajuste activado. weights siempre proviene de los últimos 90 días y no se acota con from/to.

phones_per_person es el promedio de números de teléfono distintos por persona (consumer_ref) entre las cuentas activas del tenant (cualquier estado salvo paid, settled o closed). Una persona con una cuenta activa y sin teléfono cuenta como 0. Es de todo el tenant: from, to y campaign_id no lo acotan. average es null cuando no hay ninguna persona activa.

from/to por defecto abarcan los últimos 30 días; una ventana mayor de 90 días, o con from no anterior a to, es 422. Una campaña desconocida (o de otro tenant) es 404, nunca un informe todo en cero.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)noLímite inferior RFC 3339 (campo específico del endpoint; por defecto los últimos 30 días UTC donde se indica).
toquerystring (date-time)noLímite superior RFC 3339.
campaign_idquerystring (uuid)no

Respuestas

200 ​

Tasas de contacto por hora, la mejor ventana y teléfonos por persona.

CampoTipoObligatorioDescripción
fromstring (date-time)sí
tostring (date-time)sí
campaign_idstring (uuid) | nullsí
basis"consumer_local_hour"sí
min_attemptsintegersíEl mínimo que debe cumplir cada hora de best_window (contact_window_min_attempts).
hoursarreglo de ContactWindowHoursíSiempre 24 filas, de la hora 0 a la 23, en la hora local del consumidor.
hours[].hourintegersí
hours[].attemptsintegersí
hours[].contactsintegersí
hours[].contact_ratenumber | nullsícontacts / attempts, cuatro decimales; null cuando no hubo intentos.
weekdaysarreglo de ContactWindowWeekdaynoSiempre 7 filas, día de la semana ISO 1 (lunes) a 7 (domingo), en la hora local del consumidor. Aditivo (core#1980); opcional para clientes escritos antes de él.
weekdays[].weekdayintegersíDía de la semana ISO, 1 = lunes.
weekdays[].attemptsintegersí
weekdays[].contactsintegersí
weekdays[].contact_ratenumber | nullsícontacts / attempts, cuatro decimales; null cuando no hubo intentos.
appliedbooleannoSi el ajuste best_time_ranking_enabled del tenant está activado, es decir, si el flujo de marcación ordena los leads según estos datos. Aditivo (core#1980).
sourcecampaign · portfolio · nonenoEl nivel que respalda weights: el historial propio de la campaña, la cartera del tenant, o none (sin evidencia, orden neutro). Aditivo (core#1980).
weightsarreglo de ContactWindowWeightnoEl peso que el marcador aplica a cada celda (día de la semana, hora local) dentro de la ventana legal [08:00, 21:00) mientras applied es verdadero (con applied falso mantiene el orden neutro y esto es lo que aplicaría): 7 x 13 filas, o vacío si no se pudo calcular. Últimos 90 días; no se acota con from/to. Aditivo (core#1980).
weights[].weekdayintegersí
weights[].hourintegersí
weights[].weightnumbersí1.0 es neutro.
weights[].sourcecampaign · portfolio · nonesí
weights[].attemptsintegersíIntentos en esta celda en el nivel que respaldó; 0 cuando source es none.
weights[].contactsintegersí
unknownobjectsíIntentos cuya hora local no se puede conocer. Nunca se redistribuyen entre hours.
unknown.attemptsintegersí
unknown.contactsintegersí
unknown.no_zoneintegersíIntentos sin una zona congelada utilizable.
unknown.ambiguous_zoneintegersíIntentos cuyo código de área abarca varias zonas o no coincide con el declarado.
best_windowContactWindow | nullsí
best_window_reasoninsufficient_attempts · no_resolved_attempts | nullsíPor qué best_window es null; null cuando se nombra una ventana.
phones_per_personobjectsí
phones_per_person.personsintegersíconsumer_ref distintos con una cuenta activa.
phones_per_person.phonesintegersíSuma de teléfonos distintos por persona.
phones_per_person.averagenumber | nullsíphones / persons, dos decimales; null sin ninguna persona activa.
json
{
  "from": "2026-09-04T00:00:00Z",
  "to": "2026-10-04T00:00:00Z",
  "campaign_id": null,
  "basis": "consumer_local_hour",
  "min_attempts": 50,
  "hours": [
    {
      "hour": 9,
      "attempts": 120,
      "contacts": 18,
      "contact_rate": 0.15
    },
    {
      "hour": 10,
      "attempts": 140,
      "contacts": 28,
      "contact_rate": 0.2
    },
    {
      "hour": 11,
      "attempts": 0,
      "contacts": 0,
      "contact_rate": null
    }
  ],
  "weekdays": [
    {
      "weekday": 1,
      "attempts": 90,
      "contacts": 15,
      "contact_rate": 0.1667
    }
  ],
  "unknown": {
    "attempts": 14,
    "contacts": 1,
    "no_zone": 9,
    "ambiguous_zone": 5
  },
  "applied": true,
  "source": "portfolio",
  "weights": [
    {
      "weekday": 1,
      "hour": 9,
      "weight": 1.2,
      "source": "portfolio",
      "attempts": 120,
      "contacts": 18
    },
    {
      "weekday": 1,
      "hour": 10,
      "weight": 1,
      "source": "none",
      "attempts": 0,
      "contacts": 0
    }
  ],
  "best_window": {
    "start_hour": 9,
    "end_hour": 11,
    "attempts": 260,
    "contacts": 46,
    "contact_rate": 0.1769
  },
  "best_window_reason": null,
  "phones_per_person": {
    "persons": 4,
    "phones": 7,
    "average": 1.75
  }
}

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

json
{
  "error": {
    "code": "not_found",
    "message": "resource not found for this tenant"
  }
}

422 ​

La ventana es mayor de 90 días, o from no es anterior a to.

json
{
  "error": {
    "code": "unprocessable",
    "message": "the window must not exceed 90 days"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Resumen del embudo de PTP, totales del tenant y por campaña ​

http
GET /v1/stats/promises

Las monedas nunca se suman entre sí; kept_rate_30d es null cuando nada se resolvió en la ventana.

by_campaign tiene una fila por cada campaña con al menos una promesa, derivada al leer a través del intento en el que se capturó la promesa (promises.call_attempt_id -> call_attempts.campaign_id); no se guarda ningún contador. Una promesa sin intento, o cuyo intento no tiene campaña, se agrupa en la fila con campaign_id: null, nunca se descarta, de modo que las filas siempre suman los totales de nivel superior.

Con campaign_id toda cifra se acota a esa campaña. Una campaña desconocida (o de otro tenant) es 404, nunca un resumen todo en cero.

La tasa de cumplimiento la reporta el cliente. La plataforma no se entera por sí misma de si un consumidor pagó: una promesa solo pasa a kept o broken cuando el cliente lo indica mediante PATCH /v1/promises/{id}. Un cliente que nunca reporta deja sus promesas en pending y la tasa en null.

Parámetros

NombreEnTipoObligatorioDescripción
campaign_idquerystring (uuid)no

Respuestas

200 ​

Resumen del embudo.

json
{
  "campaign_id": null,
  "open": {
    "count": 4,
    "amount_cents": {
      "USD": 50000,
      "MXN": 120000
    }
  },
  "resolved_30d": {
    "kept": 3,
    "broken": 1
  },
  "kept_rate_30d": 0.75,
  "window_days": 30,
  "by_campaign": [
    {
      "campaign_id": "3f2c1b0a-9d8e-4c7b-a6f5-4e3d2c1b0a99",
      "open": {
        "count": 3,
        "amount_cents": {
          "USD": 50000,
          "MXN": 0
        }
      },
      "resolved_30d": {
        "kept": 3,
        "broken": 1
      },
      "kept_rate_30d": 0.75
    },
    {
      "campaign_id": null,
      "open": {
        "count": 1,
        "amount_cents": {
          "USD": 0,
          "MXN": 120000
        }
      },
      "resolved_30d": {
        "kept": 0,
        "broken": 0
      },
      "kept_rate_30d": null
    }
  ]
}

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

json
{
  "error": {
    "code": "not_found",
    "message": "resource not found for this tenant"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Analítica de penetración a nivel de persona ​

http
GET /v1/stats/penetration

Deduplicación a nivel de persona (cada deuda cuenta una vez): attempted = al menos un intento original, reached = cualquier disposición o conversación no fallida, effective_contact = answered_human o conversación. campaign_ids es opcional (máx. 100; también se acepta campaign_id=): sin él el informe cubre TODAS las campañas del tenant que llama, con un único total a nivel de persona (una cuenta en dos campañas cuenta una vez), de modo que un tenant con más de 100 campañas lee toda su penetración en una sola llamada. Con la lista la respuesta es la de siempre.

granularity=day añade by_day: una fila por cada día LOCAL de la ventana y por campaña, con el día cortado en la zona IANA que nombra tz, incluido el horario de verano (CET no es un +01:00 fijo). La cuadrícula es densa (un día tranquilo es una fila de ceros) y se deriva al leer del CDR solo anexo. Con granularity=day, tz es OBLIGATORIO (no hay zona del tenant por defecto) y un tz ausente o que no sea IANA es 422, nunca un corte UTC silencioso; from y to también son obligatorios y la ventana está limitada a 31 días (400 en caso contrario, el criterio de /v1/reports/attempts). totals y by_campaign siguen siendo las cifras del rango: un conteo a nivel de persona no se suma entre días. Sin granularity (o con total) la respuesta no cambia y tz no se lee.

window=7d|30d|90d|all responde los mismos totals / by_campaign sobre una ventana RELATIVA a ahora, servida desde una instantánea en memoria de corta vida derivada del CDR solo anexo (no se escribe nada al marcar). La respuesta añade window y as_of, el instante en que se calcularon las cifras; una instantánea más antigua que el max_age del servidor (por defecto 60 s) no se sirve nunca, y el informe se calcula en vivo. window es excluyente con from / to y con granularity=day (422); sin él la respuesta no cambia.

Parámetros

NombreEnTipoObligatorioDescripción
campaign_idsquerystringnoUUID de campañas separados por comas (máx. 100). Omítelo (o envíalo vacío) para todas las campañas del tenant.
fromquerystring (date-time)noLímite inferior RFC 3339 (campo específico del endpoint; por defecto los últimos 30 días UTC donde se indica).
toquerystring (date-time)noLímite superior RFC 3339.
windowquery7d · 30d · 90d · allnoVentana relativa servida desde la instantánea derivada. Excluyente con from/to y con granularity=day.
granularityquerytotal · daynototal (por defecto) o day (añade by_day; requiere tz, from y to).
tzquerystringnoZona horaria IANA en la que se corta el día. Obligatoria con granularity=day; se ignora en otro caso.

Respuestas

200 ​

Totales de penetración + filas por campaña (+ by_day con granularity=day).

CampoTipoObligatorioDescripción
totalsobject (PenetrationRow)no
totals.campaign_idstring (uuid) | nullno
totals.unique_accountsintegerno
totals.unique_consumersintegerno
totals.attemptedintegerno
totals.reachedintegerno
totals.effective_contactintegerno
totals.attempted_pctnumberno
totals.reached_pctnumberno
totals.effective_contact_pctnumberno
by_campaignarreglo de PenetrationRowno
by_campaign[].campaign_idstring (uuid) | nullno
by_campaign[].unique_accountsintegerno
by_campaign[].unique_consumersintegerno
by_campaign[].attemptedintegerno
by_campaign[].reachedintegerno
by_campaign[].effective_contactintegerno
by_campaign[].attempted_pctnumberno
by_campaign[].reached_pctnumberno
by_campaign[].effective_contact_pctnumberno
window7d · 30d · 90d · allnoPresente solo con window=.
as_ofstring (date-time)noPresente solo con window=: cuándo se calcularon las cifras. Nunca más antigua que el max_age de la instantánea del servidor.
granularitydaynoPresente solo con granularity=day.
tzstringnoLa zona en la que se cortaron los días. Presente solo con granularity=day.
by_dayarreglo de PenetrationDayRownoPresente solo con granularity=day. Cada día local que toca la ventana × cada campaña con un universo, ordenado por día y luego por campaña; con los ceros incluidos. El primer y el último día son parciales cuando from/to no son medianoches locales. Los conteos se suman entre campañas (una deuda pertenece a una campaña), no entre días.
by_day[].daystring (date)no
by_day[].campaign_idstring (uuid)no
by_day[].unique_accountsintegernoEl universo de la campaña: el mismo número que reporta su fila de by_campaign.
by_day[].attemptedintegerno
by_day[].reachedintegerno
by_day[].effective_contactintegerno
by_day[].attempted_pctnumbernoSobre unique_accounts.
by_day[].reached_pctnumbernoSobre unique_accounts.
by_day[].effective_contact_pctnumbernoSobre unique_accounts.
json
{
  "totals": {
    "campaign_id": null,
    "unique_accounts": 4,
    "unique_consumers": 4,
    "attempted": 2,
    "reached": 1,
    "effective_contact": 1,
    "attempted_pct": 50,
    "reached_pct": 25,
    "effective_contact_pct": 25
  },
  "by_campaign": [
    {
      "campaign_id": "3f2c1b0a-9d8e-4c7b-a6f5-4e3d2c1b0a99",
      "unique_accounts": 4,
      "unique_consumers": 4,
      "attempted": 2,
      "reached": 1,
      "effective_contact": 1,
      "attempted_pct": 50,
      "reached_pct": 25,
      "effective_contact_pct": 25
    }
  ]
}

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

422 ​

granularity=day sin tz, o con un tz que no es una zona horaria IANA; o window desconocida o combinada con from/to o granularity=day.

json
{
  "error": {
    "code": "unprocessable",
    "message": "tz is required with granularity=day"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Informe duradero de abandono de todas las campañas, en una sola respuesta ​

http
GET /v1/stats/abandonment

El mismo informe que da GET /v1/campaigns/{id}/abandonment, para TODAS las campañas del tenant que llama a la vez (la consola solía preguntar una vez por campaña). Servido desde una instantánea en memoria de corta vida derivada del CDR solo anexo (no se escribe nada al marcar): as_of es el instante en que se calcularon las cifras, y una instantánea más antigua que el max_age del servidor (por defecto 60 s) no se sirve nunca; el informe se calcula en vivo. La ruta por campaña sigue siendo en vivo y es el registro; esta es la vista general.

Respuestas

200 ​

Un AbandonmentReport por campaña, ordenado por nombre de campaña.

CampoTipoObligatorioDescripción
as_ofstring (date-time)sí
campaignsarreglo de AbandonmentReportsí
campaigns[].campaign_idstring (uuid)sí
campaigns[].measured_atstring (date-time)sí
campaigns[].period_days30sí
campaigns[].ceiling_percent3sí
campaigns[].anchored_atstring (date-time) | nullsíLa primera marcación realizada de la campaña; null = aún sin marcaciones (y sin período).
campaigns[].predicateobjectsí
campaigns[].predicate.countsarreglo de no_agent · ai_stream_lost · agent_bridge_failed · handoff_failed · late_bridgesíLos motivos por los que la tasa cuenta como abandonada.
campaigns[].predicate.reported_apartarreglo de no_agent · ai_stream_lost · agent_bridge_failed · handoff_failed · late_bridgesíMotivos informados por período y que NO se cuentan en la tasa.
campaigns[].periodsarreglo de AbandonmentPeriodsíUna entrada por período de 30 días con al menos una marcación realizada, de la más antigua a la más reciente.
campaigns[].periods[].indexintegersí[anchored_at + 30·index days, + 30 days).
campaigns[].periods[].starts_atstring (date-time)sí
campaigns[].periods[].ends_atstring (date-time)síExclusivo.
campaigns[].periods[].completebooleansíends_at no es posterior a measured_at.
campaigns[].periods[].placedintegersí
campaigns[].periods[].answeredintegersí
campaigns[].periods[].answered_machineintegersíContestada con un veredicto AMD machine.
campaigns[].periods[].answered_liveintegersíEl denominador: answered - answered_machine.
campaigns[].periods[].abandonedintegersíEl numerador; la suma de abandoned_by_reason.
campaigns[].periods[].abandoned_by_reasonobjectsíUna clave por cada motivo de predicate.counts. Los lectores no deben asumir un conjunto cerrado de claves: agent_bridge_failed se añadió en core#1383.
campaigns[].periods[].handoff_failedintegersíTraspasos de IA de voz sin puesto disponible; no entran en la tasa.
campaigns[].periods[].late_bridgeintegersíRespuestas en vivo no contadas como abandonadas cuya pierna del consumidor se puenteó más de 2 s después de answered_at (core#1426); no entran en la tasa.
campaigns[].periods[].ratenumber | nullsíabandoned / answered_live, 4 decimales; null si no hay respuesta en vivo.
campaigns[].periods[].exceeds_ceilingbooleansíabandoned * 100 > 3 * answered_live.

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Resumen duradero de marcaciones bloqueadas ​

http
GET /v1/stats/blocked

Lee la evidencia duradera y SOLO ANEXA de gate_blocks: una fila por (deuda, compuerta, campaña, día UTC) que el motor de cumplimiento SE NEGÓ a marcar. Cada cifra es un conteo de rechazos distintos, nunca una estimación. Por defecto: los últimos 30 días UTC.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)noLímite inferior RFC 3339 (campo específico del endpoint; por defecto los últimos 30 días UTC donde se indica).
toquerystring (date-time)noLímite superior RFC 3339.
campaign_idquerystring (uuid)no

Respuestas

200 ​

Resumen de marcaciones bloqueadas.

CampoTipoObligatorioDescripción
fromstring (date-time)no
tostring (date-time)no
campaign_idstring (uuid) | nullno
totalintegerno
by_gateobjectnoLas claves son el vocabulario de ComplianceGate.
dailyarreglo de objectno
daily[].datestring (date)no
daily[].totalintegerno
daily[].by_gateobjectno
json
{
  "from": "2026-06-05T00:00:00Z",
  "to": "2026-07-05T00:00:00Z",
  "campaign_id": null,
  "total": 5,
  "by_gate": {
    "quiet_hours": 4,
    "dnc_listed": 1
  },
  "daily": [
    {
      "date": "2026-06-01",
      "total": 3,
      "by_gate": {
        "quiet_hours": 2,
        "dnc_listed": 1
      }
    }
  ]
}

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Techos de capacidad del tenant + uso en vivo (ADR #12) ​

http
GET /v1/stats/capacity

Los techos de capacidad fijados por el administrador del tenant (max_cps, max_concurrent_channels; null = sin techo) más el uso en vivo leído de la compuerta de capacidad. limits: null = los techos no se pudieron leer Y nunca se almacenaron en caché: justo la condición ante la que la compuerta falla CERRADA; se muestra con honestidad, nunca fabricado como «ilimitado».

supervision es un SEGUNDO techo, independiente (ADR #37): las piernas de supervisión de llamadas en vivo se originan fuera del punto de estrangulamiento de marcación, así que se cuentan en su propia cuota. Ninguno de los dos carriles puede agotar al otro: el piso de marcación puede estar en su techo mientras la supervisión aún admite, y viceversa.

Respuestas

200 ​

Techos de capacidad + uso en vivo.

CampoTipoObligatorioDescripción
limitsobject | nullnoTechos fijados por el administrador, o null cuando la compuerta falla cerrada (techos ilegibles y nunca almacenados en caché). Cada techo es null cuando no está definido.
channels_in_useintegerno
dials_last_secondintegerno
supervisionobjectnoEl SEGUNDO techo (ADR #37): sesiones concurrentes de supervisión de llamadas en vivo, contadas y acotadas por separado del carril de marcación para que ninguno pueda dejar sin recursos al otro. limit es el max_supervision_channels de despliegue; in_use cuenta sesiones (llamada, supervisor), de modo que un cambio de listen → barge en una llamada cuenta una vez.
supervision.limitintegerno
supervision.in_useintegerno
json
{
  "limits": {
    "max_cps": 10,
    "max_concurrent_channels": 50
  },
  "channels_in_use": 3,
  "dials_last_second": 2,
  "supervision": {
    "limit": 20,
    "in_use": 1
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Medición de minutos de IA de voz (lectura para facturación) ​

http
GET /v1/stats/ai-usage

Sobre el flujo duradero ai_usage de exactamente una vez (una fila por llamada de IA contestada, facturada EXACTAMENTE UNA VEZ por call_uuid). Cada fila guarda una instantánea de la tarifa por minuto con la que se midió: cambiar la tarifa configurada nunca revalora el historial. La ventana es semiabierta (from inclusive, to EXCLUSIVO), de modo que una llamada contestada justo en un corte de facturación pertenece a un solo periodo (el que comienza en ese instante), nunca a ambos lados del corte. ?format=csv (o Accept: text/csv) devuelve el artefacto de facturación con su propio X-Artifact-SHA256.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)noLímite inferior RFC 3339 (campo específico del endpoint; por defecto los últimos 30 días UTC donde se indica).
toquerystring (date-time)noLímite superior RFC 3339.
campaign_idquerystring (uuid)no
formatquerycsvnocsv para el artefacto descargable (equivalente: Accept: text/csv).

Respuestas

200 ​

Estadísticas de medición (JSON) o el artefacto de facturación en CSV.

CampoTipoObligatorioDescripción
fromstring (date-time)no
tostring (date-time)no
campaign_idstring (uuid) | nullno
minute_rate_usdnumberno
callsintegerno
billable_secondsintegerno
billable_minutesnumberno
estimated_cost_usdnumberno
dailyarreglo de objectno
daily[].datestring (date)no
daily[].callsintegerno
daily[].billable_secondsintegerno
daily[].billable_minutesnumberno
daily[].minute_rate_usdnumberno
daily[].estimated_cost_usdnumberno
json
{
  "from": "2026-06-05T00:00:00Z",
  "to": "2026-07-05T00:00:00Z",
  "campaign_id": null,
  "minute_rate_usd": 0.25,
  "calls": 3,
  "billable_seconds": 270,
  "billable_minutes": 4.5,
  "estimated_cost_usd": 1.13,
  "daily": [
    {
      "date": "2026-06-01",
      "calls": 2,
      "billable_seconds": 180,
      "billable_minutes": 3,
      "minute_rate_usd": 0.25,
      "estimated_cost_usd": 0.75
    }
  ]
}

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Medición de segmentos de SMS (lectura para facturación) ​

http
GET /v1/stats/sms-usage

Directamente del libro unificado: eventos facturables sms_segment, exactamente una vez al ACCEPT del proveedor, cada uno valorado con su instantánea de tarifa por región. JSON, o ?format=csv / Accept: text/csv para el artefacto de facturación.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)noLímite inferior RFC 3339 (campo específico del endpoint; por defecto los últimos 30 días UTC donde se indica).
toquerystring (date-time)noLímite superior RFC 3339.
campaign_idquerystring (uuid)no
formatquerycsvnocsv para el artefacto descargable (equivalente: Accept: text/csv).

Respuestas

200 ​

Estadísticas de medición (JSON) o el artefacto de facturación en CSV.

CampoTipoObligatorioDescripción
fromstring (date-time)no
tostring (date-time)no
campaign_idstring (uuid) | nullno
segment_rates_usdobjectnoTarifa por región vigente actualmente (la que fotografiarán los nuevos envíos).
messagesintegerno
segmentsstringnoDecimal como cadena.
amount_usdstringnoDecimal como cadena.
dailyarreglo de objectno
daily[].datestring (date)no
daily[].messagesintegerno
daily[].segmentsstringno
daily[].segment_rate_usdstringno
daily[].amount_usdstringno
json
{
  "from": "2026-06-05T00:00:00Z",
  "to": "2026-07-05T00:00:00Z",
  "campaign_id": null,
  "segment_rates_usd": {
    "us": "0.0100",
    "mx": "0.0300",
    "eu": "0.0500"
  },
  "messages": 2,
  "segments": "3",
  "amount_usd": "0.03",
  "daily": [
    {
      "date": "2026-07-01",
      "messages": 2,
      "segments": "3",
      "segment_rate_usd": "0.0100",
      "amount_usd": "0.03"
    }
  ]
}

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

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