Appearance
Gestión de calidad (QM)
English version: Quality management.
La gestión de calidad permite a un supervisor puntuar una llamada grabada contra un formulario de puntuación versionado, permite al agente evaluado disputar la puntuación, y consolida las puntuaciones vigentes en reportes por agente y por campaña. Esta página es la guía; los esquemas exactos de petición y respuesta de cada endpoint están en la referencia de gestión de calidad generada (en inglés). La propia plataforma también puede hacer una primera pasada: consulta Evaluaciones automáticas (IA). La guía en español de esa sección: Evaluación de calidad con IA.
QM viene apagado por defecto
Las escrituras exigen el flag de tenant qm_enabled, que viene apagado por defecto y no es de autoservicio. Mientras esté apagado, toda escritura responde 403 con error.code qm_not_enabled y no escribe nada. Las lecturas siguen funcionando con el flag apagado: una evaluación es evidencia, y el agente evaluado sigue viéndola. Un flag que no se puede leer cuenta como apagado.
Quién puede hacer qué
| Acción | Endpoint | Quién |
|---|---|---|
| Leer formularios, versiones | GET /v1/qm/forms, /v1/qm/forms/{id}, /v1/qm/forms/{id}/versions[/{version}] | cualquier clave o login con alcance de tenant |
| Crear, editar, archivar un formulario | POST /v1/qm/forms, PATCH / DELETE /v1/qm/forms/{id} | un login de dashboard account_admin, flag encendido |
| Evaluar una grabación | POST /v1/recordings/{id}/evaluations | un puesto de supervisor o administrador, o un login account_admin sin puesto; flag encendido |
| Leer las evaluaciones de una grabación | GET /v1/recordings/{id}/evaluations[/{evaluation_id}] | claves dd_ y logins que pueden evaluar; un puesto de agente o un member recibe 403 role_forbidden |
| Resolver una disputa | POST /v1/qm/evaluations/{id}/dispute/resolution | un evaluador elegible que no sea el agente evaluado; flag encendido |
| Reportes | GET /v1/qm/reports/agents, /v1/qm/reports/campaigns | claves dd_ y logins que pueden evaluar |
| Leer las propias evaluaciones, abrir una disputa | GET /v1/me/evaluations[/{id}], POST /v1/me/evaluations/{id}/dispute | el login ddu_ propio del agente evaluado con un puesto activo |
Una clave de integración dd_ es un servicio, no una persona: puede leer, pero toda escritura es 403 role_forbidden. Un token de widget ddw_ se rechaza en las escrituras y en las rutas /v1/me/evaluations. Consulta Autenticación.
1. Define un formulario de puntuación
Un formulario es una lista ordenada de 1 a 100 criterios más un pass_threshold (de 0 a 100). Cada criterio tiene una key (única dentro del formulario), un label, un kind (yes_no o scale) y un weight (de 1 a 100). Un criterio scale necesita max (de 2 a 10). Un criterio yes_no puede ser critical: responder «no» hace fallar toda la evaluación. na_allowed permite al evaluador marcar un criterio como no aplicable.
bash
curl -s -X POST "$API/v1/qm/forms" \
-H "Authorization: Bearer $ADMIN_LOGIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Collections — outbound call",
"pass_threshold": 80,
"criteria": [
{ "key": "identification", "label": "Identifies the agency and the agent", "kind": "yes_no", "weight": 2 },
{ "key": "disclosure", "label": "Reads the full disclosure", "kind": "yes_no", "weight": 5, "critical": true },
{ "key": "empathy", "label": "Empathy with the debtor", "kind": "scale", "max": 5, "weight": 3, "na_allowed": true }
]
}'La respuesta es 201 con el formulario en la versión 1. El nombre de un formulario vivo es único por tenant (409), y un tenant tiene como máximo 200 formularios vivos (422).
Editar nunca reescribe la historia. PATCH /v1/qm/forms/{id} con criteria, pass_threshold o ambos añade la versión n+1 y deja intacta la versión n, porque una evaluación puede haberla citado. Envía base_version para protegerte de que dos personas editen a la vez: si no es la versión actual recibes 409 y no se escribe nada. DELETE archiva el formulario (idempotente, 204); los formularios archivados nunca se borran, no se pueden editar y liberan su nombre. GET /v1/qm/forms?include_archived=true los lista.
2. Evalúa una grabación
bash
curl -s -X POST "$API/v1/recordings/$RECORDING_ID/evaluations" \
-H "Authorization: Bearer $SUPERVISOR_LOGIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"form_id": "'"$FORM_ID"'",
"form_version": 1,
"answers": { "identification": true, "disclosure": true, "empathy": 4 },
"comment": "Good opening; close with the recording notice."
}'- Respuestas:
true/falseparayes_no, un entero de 0 amaxparascale, la cadena"na"donde se permita. Una respuesta por criterio. - El servidor calcula la puntuación; tú nunca la envías. Se devuelve como
score_bpen puntos base (8750es 87,5 %), conpassedyauto_failed(un criterio crítico fallido). - La grabación debe estar
available.agent_id,call_attempt_idycampaign_idsalen de la grabación, no del cuerpo. - La fila de evaluación queda congelada. Para corregirla, envía el mismo cuerpo con
supersedes_idpuesto a la evaluación vigente de esa grabación y formulario: se añade una fila nueva y se conserva la antigua. Sinsupersedes_id, una grabación tiene una evaluación por formulario (409en cuanto ya hay una). - No puedes evaluar tu propia grabación:
403 self_evaluation_forbidden.
3. El agente disputa
El agente evaluado lee sus evaluaciones en GET /v1/me/evaluations. El puesto sale del token; cualquier parámetro que nombre a un agente (agent_id, seat_id, user_id, sip_extension) se rechaza con 400. El agente ve la puntuación, las respuestas, el comment del evaluador y el estado de la disputa, pero no quién evaluó. dispute_until es el fin de la ventana de disputa mientras todavía se pueda abrir una.
bash
curl -s -X POST "$API/v1/me/evaluations/$EVALUATION_ID/dispute" \
-H "Authorization: Bearer $AGENT_LOGIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reason": "criterion_misjudged", "note": "I read the disclosure at 1:10." }'reason es wrong_call, criterion_misjudged u other. Hay una disputa por evaluación, abierta dentro de la ventana qm_dispute_window_days del tenant (7 días por defecto; de 1 a 90). Mientras una disputa está abierta, la evaluación sigue contando en los reportes. Una disputa se rechaza con 409 cuando la evaluación fue reemplazada, ya tiene una disputa, es ella misma el resultado de una disputa resuelta, o la ventana se cerró.
4. Resuelve la disputa
Un evaluador elegible, que no puede ser el agente evaluado, la cierra:
bash
# keep the score
curl -s -X POST "$API/v1/qm/evaluations/$EVALUATION_ID/dispute/resolution" \
-H "Authorization: Bearer $SUPERVISOR_LOGIN_TOKEN" -H "Content-Type: application/json" \
-d '{ "decision": "upheld", "note": "Reviewed with the call evidence." }'
# replace the score
curl -s -X POST "$API/v1/qm/evaluations/$EVALUATION_ID/dispute/resolution" \
-H "Authorization: Bearer $SUPERVISOR_LOGIN_TOKEN" -H "Content-Type: application/json" \
-d '{ "decision": "adjusted", "answers": { "identification": true, "disclosure": true, "empathy": 4 }, "comment": "Adjusted after review." }'adjusted exige answers (la misma forma que al evaluar, puntuadas contra la misma versión del formulario) y escribe la evaluación sustituta y el evento de resolución en una sola transacción. upheld no admite answers ni comment. La respuesta es 201 con el dispute_event; su adjusted_evaluation_id apunta a la nueva evaluación.
Mientras una disputa está abierta, corregir esa evaluación mediante POST …/evaluations con supersedes_id se rechaza con 409 evaluation_under_dispute: resuélvela como adjusted. Una vez resuelta como upheld, se vuelve a aceptar una corrección.
5. Reportes
bash
curl -sG "$API/v1/qm/reports/agents" \
-H "Authorization: Bearer $SUPERVISOR_LOGIN_TOKEN" \
--data-urlencode "from=2026-10-01T00:00:00Z" \
--data-urlencode "to=2026-10-08T00:00:00Z"/v1/qm/reports/campaigns es idéntico con campaign_id en lugar de agent_id. from y to son obligatorios, from debe ser anterior a to, y la ventana no puede superar los 31 días (400 en otro caso). Selecciona las grabaciones que empezaron en [from, to). Las cifras se derivan en cada llamada solo de las evaluaciones vigentes (una corrección reemplaza la fila a la que sustituye). Una fila por agente (o campaña), formulario y evaluator_kind; dos formularios son escalas distintas y nunca se promedian juntos, y tampoco las evaluaciones de una persona con las automáticas (consulta Evaluaciones automáticas (IA)).
| Campo | Significado |
|---|---|
evaluations | evaluaciones vigentes contadas |
avg_score_bp | puntuación media en puntos base; null cuando no hay nada que promediar (nunca 0) |
pass_rate_bp | passed sobre evaluations en puntos base; null igualmente |
passed, auto_failed | cuenta de aprobadas; evaluaciones anuladas por un criterio crítico |
adjusted | evaluaciones vigentes que reemplazaron a una disputada |
open_disputes | evaluaciones vigentes cuya disputa está abierta |
Los reportes llevan solo conteos y puntos base: ni comentarios, ni números de teléfono, ni otro texto libre.
Evaluaciones automáticas (IA)
Desde core v0.106.0 (core#1973) la plataforma puede puntuar por sí sola una llamada grabada, de modo que las llamadas que ningún supervisor tiene tiempo de escuchar reciban igualmente una primera pasada. Es una clase de evaluación aparte: evaluator_kind vale automatic (el de una persona, human). Los nombres de campo y los enums salen del openapi/dialer-v1.yaml vendorizado (info.version: 0.106.0); donde la especificación calla, la afirmación cita un fichero fuente de core en el tag v0.106.0.
Qué evalúa, y qué no
- Qué. Cada criterio de un formulario de puntuación, contra la transcripción redactada de la llamada. El modelo solo devuelve una respuesta por criterio; nunca una puntuación. Core calcula
score_bp,passedyauto_failedcon la misma puntuación que para una persona (QmEvaluationen la especificación: "The score is computed by core in both cases, never by the model"). Elweighty elcriticalde los criterios no se envían al modelo.lib/dialer/qm/auto_qa.ex. - Qué formulario. El formulario vivo más antiguo del tenant, en su versión vigente. No hay un mapeo campaña-formulario: toda grabación elegible se evalúa contra ese único formulario.
lib/dialer/qm/auto_qa.ex. - Qué no hace. El resultado es una sugerencia para el evaluador y una cifra en un reporte; nunca cambia la disposición de una llamada: el job solo escribe una fila de evaluación. No se escribe si las respuestas del modelo no cubren todos los criterios o no caben en el criterio (no se inventa un «no aplica»). Nunca se escribe por encima de una persona: una grabación que ya tiene una evaluación de ese formulario no recibe una automática. Las grabaciones de llamadas con un agente de voz con IA (
ai_used) quedan excluidas.lib/dialer/qm/auto_qa.ex. - La persona gana siempre. La primera evaluación humana de una grabación cuya evaluación vigente es automática la reemplaza; quien llama no necesita enviar
supersedes_id. La fila automática se conserva (la tabla es de solo anexado).lib/dialer/qm/evaluations.ex. - El agente puede disputarla igual que una humana (sección 3). La disputa nunca se resuelve sola: la resuelve un evaluador elegible, y si la
adjusted, su evaluación humana reemplaza a la automática. La vista propia del agente (QmAgentEvaluation) no llevaevaluator_kind, así que al agente no se le dice de qué clase era.
Quién la recibe
Deben cumplirse todas, o no se hace ninguna evaluación y no se gasta nada:
| Condición | Detalle | Fuente |
|---|---|---|
| Plan | Nivel scale o enterprise (el derecho :qa). Cualquier otro nivel, incluido uno desconocido, no tiene derecho. | lib/dialer/post_call/entitlement.ex |
| Flag de tenant | qm_enabled encendido para el tenant (consulta la advertencia al principio de esta página). Una migración lo enciende por segmento para scale y enterprise; una anulación por tenant, escrita solo por Dialer Digital, prevalece sobre el segmento en ambos sentidos. | lib/dialer/qm/auto_qa.ex, priv/repo/migrations/20261111030002_seed_qm_enabled_segments.exs |
| Grabación | available, audio no purgado, con un agente, no ai_used, con una transcripción guardada, y sin una evaluación previa de esa grabación y formulario. | lib/dialer/qm/auto_qa.ex |
| Proveedor | Un proveedor de pago configurado para análisis en la celda. El proveedor local no atiende esta tarea y el job termina sin gastar. | lib/dialer/qm/auto_qa.ex |
El job se encola cuando se guarda la transcripción de la grabación (Inteligencia post-llamada describe la elegibilidad de la propia transcripción).
El freno de gasto (falla cerrado)
La evaluación pasa por el mismo freno de gasto que la transcripción y el análisis, con function: "qa". El job pide una reserva antes de llamar al modelo y la liquida con el uso real de tokens después. Cualquier cosa distinta de una reserva concedida termina el job sin gastar y sin reintentar; un libro mayor que no se puede leer hace fallar el intento sin gastar. La salida del modelo está acotada a 2.048 tokens. Consulta el freno de gasto para el tope, los motivos de denegación y las reglas de fallar cerrado; se aplican sin cambios. Para un cliente de la API el efecto visible de una denegación es que no aparece ninguna evaluación automática.
Cómo se ven en reportes y listas
evaluator_kind es un campo de QmEvaluation (human o automatic; evaluator_user_id es null en automatic). Los dos reportes agrupan por agente (o campaña), formulario y evaluator_kind, de modo que la puntuación de una persona y la pasada automática son filas distintas y nunca se promedian juntas, y admiten un filtro opcional evaluator_kind=human|automatic (cualquier otro valor es 400):
bash
curl -sG "$API/v1/qm/reports/agents" \
-H "Authorization: Bearer $SUPERVISOR_LOGIN_TOKEN" \
--data-urlencode "from=2026-10-01T00:00:00Z" \
--data-urlencode "to=2026-10-08T00:00:00Z" \
--data-urlencode "evaluator_kind=automatic"En el dashboard (v0.51.0), una evaluación automática se etiqueta «Automatic» en el panel QM de la grabación (apps/web/src/routes/recordings/QmEvaluationPanel.tsx), y el reporte de la página de calidad muestra la clase de cada fila y se puede filtrar por ella (apps/web/src/routes/quality/QualityPage.tsx).
Errores
Los errores usan el sobre de error estándar. Los específicos de QM:
error.code | Estado | Cuándo |
|---|---|---|
qm_not_enabled | 403 | una escritura mientras el flag qm_enabled del tenant está apagado |
role_forbidden | 403 | una clave dd_, un member, o un puesto que no puede evaluar |
self_evaluation_forbidden | 403 | evaluar, o resolver una disputa sobre, tu propia grabación |
evaluation_under_dispute | 409 | corregir una evaluación cuya disputa sigue abierta |
Qué se cubre de punta a punta
La prueba de integración de la plataforma dirigida por navegador (quality-management.spec.ts) ejercita este flujo contra un core y un dashboard reales, con qm_enabled puesto para el tenant de prueba: con el flag apagado una escritura de formulario se rechaza con qm_not_enabled; una clave dd_ se rechaza con role_forbidden; un account_admin crea un formulario y evalúa una grabación en el dashboard; un supervisor que evalúa su propia grabación se rechaza con self_evaluation_forbidden; el agente evaluado abre una disputa desde su dashboard; la disputa se resuelve mediante esta API como adjusted; y el reporte del agente muestra entonces una evaluación al 100 %, una ajustada y ninguna disputa abierta.