Appearance
Gestión de calidad (QM)
Generado desde
DialerDigital/corecommit0fc18008b3c8— especificaciónopenapi/dialer-v1.yamlv0.134.0. No se edita a mano; consulta Cómo se construye esta referencia. Descargar la especificación OpenAPI.
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.
Formularios de puntuación de gestión de calidad (ADR #217), versionados de sólo anexado por tenant. Las escrituras requieren el indicador qm_enabled.
Endpoints
Las evaluaciones de QM PROPIAS de quien llama, de la más reciente a la más antigua
http
GET /v1/me/evaluationsLas evaluaciones del puesto que identifica el BEARER, correcciones incluidas, con tope de 200. Ningún parámetro puede nombrar a un agente: agent_id, seat_id, user_id y sip_extension se rechazan por su nombre con 400, nunca se ignoran, de modo que un cliente nunca pueda creer que leyó las evaluaciones de un compañero. La comprobación va DESPUÉS del 403, así que quien llama sin puesto no averigua qué parámetros se leen.
Quién obtiene un puesto: un login de panel ddu_ vinculado a un puesto activo (403 seat_not_linked en caso contrario). Una clave de máquina dd_ no tiene ninguna persona detrás y recibe 403. Un token de widget ddw_ recibe 403 antes del handler: esta ruta no está en la lista de permitidos del widget.
Cada fila lleva las respuestas a los criterios, la puntuación, la versión del formulario, el comment escrito para el agente, si es la current, el estado de la disputa (dispute, nulo cuando no hay ninguna) y dispute_until, el fin de la ventana qm_dispute_window_days mientras la disputa aún puede abrirse (nulo en otro caso: evaluación sustituida, disputa ya abierta o resuelta, fruto de una disputa resuelta, ventana cerrada). La fila NO dice quién evaluó y nunca lleva el id propio del agente. Las lecturas funcionan con qm_enabled activado o no.
Respuestas
200
Las evaluaciones del llamante.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
evaluations | arreglo de QmAgentEvaluation | sí | |
evaluations[].id | string (uuid) | sí | |
evaluations[].recording_id | string (uuid) | sí | |
evaluations[].call_attempt_id | string (uuid) | sí | |
evaluations[].campaign_id | string,null (uuid) | no | |
evaluations[].form_id | string (uuid) | sí | |
evaluations[].form_version | integer | sí | |
evaluations[].answers | object | sí | |
evaluations[].score_bp | integer | sí | Puntos básicos: 8750 es 87,5 %. |
evaluations[].auto_failed | boolean | sí | Un criterio crítico se respondió que no; la puntuación es 0. |
evaluations[].passed | boolean | sí | |
evaluations[].comment | string,null | sí | El texto libre del evaluador; el agente evaluado puede leerlo. |
evaluations[].supersedes_id | string,null (uuid) | no | |
evaluations[].current | boolean | sí | Ninguna otra evaluación reemplaza a esta. |
evaluations[].dispute | QmAgentDispute | null | sí | |
evaluations[].dispute_until | string,null (date-time) | sí | El fin de la ventana de disputa mientras la disputa aún puede abrirse; null en otro caso. |
evaluations[].created_at | string (date-time) | sí |
400
Un parámetro que nombra a un agente (agent_id, seat_id, user_id, sip_extension), rechazado por su nombre.
json
{
"error": {
"code": "bad_request",
"message": "agent_id not accepted here: these evaluations are always the caller's own, derived from the token"
}
}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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}Una de las evaluaciones de QM PROPIAS de quien llama, con sus criterios y su hilo de disputa
http
GET /v1/me/evaluations/{id}Mismo contrato de identidad que GET /v1/me/evaluations: el puesto sale del token y los parámetros que nombran a un agente son 400. Añade los criteria y el pass_threshold de la versión del formulario con la que se puntuó la evaluación, y el hilo dispute_events (kind, reason, note, hora; sin ids de actor). Una evaluación de otro agente, de otro tenant o que no existe da el mismo 404.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
La evaluación.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
evaluation | any | sí | Una QmAgentEvaluation más los criterios con los que se puntuó y su hilo de disputa. |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Abrir la disputa de una de las evaluaciones de QM PROPIAS de quien llama
http
POST /v1/me/evaluations/{id}/disputeEl agente evaluado impugna la evaluación: UNA sola disputa por evaluación, abierta dentro de la ventana qm_dispute_window_days (de 1 a 90 días, 7 por defecto) contada desde que se escribió la evaluación. Sólo el login ddu_ del agente al que se refiere la evaluación puede abrirla, con un puesto ACTIVO. Una clave dd_ es un servicio, no una persona: 403 role_forbidden. Un token de widget ddw_ recibe 403 antes del handler: esta ruta no está en la lista permitida del widget.
Las comprobaciones se ejecutan en este orden y la primera que falla no escribe nada: alcance del tenant, qm_enabled (403 qm_not_enabled: las lecturas siguen con el indicador apagado, las escrituras no), la persona (403 role_forbidden), 404 si el id no es un UUID, 400 (el cuerpo: reason es uno de wrong_call, criterion_misjudged, other, note es texto opcional, los campos desconocidos se rechazan), 404 (la evaluación no es del propio llamante, pertenece a otro tenant o no existe: una sola respuesta para los tres casos), y después 409 cuando la evaluación fue reemplazada por una más nueva (disputa la vigente), ya tiene su disputa o es ella misma el resultado de una disputa resuelta (una disputa resuelta no se reabre), o la ventana se ha cerrado.
Responde con la evaluación tal como la muestra GET /v1/me/evaluations/{id}, ahora con su dispute y el evento opened en dispute_events. Mientras la disputa está abierta la evaluación sigue contando: nada la oculta ni la excluye.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
reason | wrong_call · criterion_misjudged · other | sí | |
note | string | no | Texto libre opcional; en blanco se lee como ausente. |
json
{
"reason": "criterion_misjudged",
"note": "Dije el aviso de grabacion en el minuto 1:10 y el criterio lo marca como omitido."
}Respuestas
201
La evaluación con su disputa abierta.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
evaluation | any | sí | Una QmAgentEvaluation más los criterios con los que se puntuó y su hilo de disputa. |
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"
}
}403
code qm_not_enabled: el indicador qm_enabled del tenant está apagado. coderole_forbidden: el llamante es una clave dd_ o un login sin puesto de agente activo. code forbidden: el tenant está suspendido o el token es un token de widget.
json
{
"error": {
"code": "qm_not_enabled",
"message": "QM is not enabled for this tenant"
}
}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"
}
}409 — Conflict
Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.
json
{
"error": {
"code": "conflict",
"message": "invalid state transition"
}
}Los formularios de puntuación de QM del tenant, cada uno con su versión vigente
http
GET /v1/qm/formsPor nombre, los vigentes; ?include_archived=true añade los archivados. Cada formulario viene con su versión VIGENTE. Tope de 1 000. Las lecturas funcionan con qm_enabled activado o no.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
include_archived | query | boolean | no |
Respuestas
200
Los formularios.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
forms | arreglo de QmForm | sí | |
forms[].id | string (uuid) | sí | |
forms[].tenant_id | string (uuid) | no | |
forms[].name | string | sí | |
forms[].archived | boolean | sí | |
forms[].version | integer | sí | La versión vigente. |
forms[].criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
forms[].criteria[].key | string | sí | |
forms[].criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
forms[].criteria[].kind | yes_no · scale | sí | |
forms[].criteria[].weight | integer | sí | |
forms[].criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
forms[].criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
forms[].criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
forms[].pass_threshold | integer | sí | |
forms[].version_created_at | string (date-time) | no | |
forms[].created_at | string (date-time) | no | |
forms[].updated_at | string (date-time) | no |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}Crear un formulario de QM (versión 1)
http
POST /v1/qm/formsUn formulario es una lista ORDENADA de criterios más el umbral de aprobación (de 0 a 100). name va sin espacios en los extremos, no en blanco, con un máximo de 200 caracteres y único entre los formularios VIGENTES del tenant (409). Un tenant tiene como máximo 200 formularios vigentes (422). El cuerpo se comprueba en su conjunto: el primer criterio que no encaja se nombra por su posición en el mensaje 400. Los campos desconocidos se rechazan.
Las escrituras requieren el indicador qm_enabled (403 qm_not_enabled mientras esté apagado) y una PERSONA: un login del dashboard con el rol account_admin. Una clave de API dd_, un login member o un token de widget recibe 403 role_forbidden.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | sí | |
criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
criteria[].key | string | sí | |
criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
criteria[].kind | yes_no · scale | sí | |
criteria[].weight | integer | sí | |
criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
pass_threshold | integer | sí |
json
{
"name": "Cobranza — llamada saliente",
"pass_threshold": 80,
"criteria": [
{
"key": "identificacion",
"label": "Identifica a la empresa y al agente",
"kind": "yes_no",
"weight": 2
},
{
"key": "mini_miranda",
"label": "Lee el aviso legal completo",
"kind": "yes_no",
"weight": 5,
"critical": true
},
{
"key": "empatia",
"label": "Empatía con el deudor",
"kind": "scale",
"max": 5,
"weight": 3,
"na_allowed": true
}
]
}Respuestas
201
El formulario, en la versión 1.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
form | object (QmForm) | sí | Un formulario: su cabecera y su versión VIGENTE. |
form.id | string (uuid) | sí | |
form.tenant_id | string (uuid) | no | |
form.name | string | sí | |
form.archived | boolean | sí | |
form.version | integer | sí | La versión vigente. |
form.criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
form.criteria[].key | string | sí | |
form.criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
form.criteria[].kind | yes_no · scale | sí | |
form.criteria[].weight | integer | sí | |
form.criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
form.criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
form.criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
form.pass_threshold | integer | sí | |
form.version_created_at | string (date-time) | no | |
form.created_at | string (date-time) | no | |
form.updated_at | string (date-time) | no |
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"
}
}403 — QmWriteForbidden
Se rechazó una escritura de gestión de calidad (QM). code qm_not_enabled: el indicador qm_enabled del tenant está apagado (viene apagado por defecto; un indicador que no se puede leer cuenta como apagado). code role_forbidden: el llamante no es un login del dashboard con el rol account_admin (una clave dd_, un member). code forbidden: el tenant está suspendido o el token es un token de widget.
json
{
"error": {
"code": "qm_not_enabled",
"message": "QM is not enabled for this tenant"
}
}409 — Conflict
Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.
json
{
"error": {
"code": "conflict",
"message": "invalid state transition"
}
}422 — Unprocessable
La forma es correcta, la semántica no (regla de dominio incumplida).
json
{
"error": {
"code": "unprocessable",
"message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
}
}Un formulario de QM, en su versión vigente
http
GET /v1/qm/forms/{id}Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
El formulario (también los archivados, marcados con archived).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
form | object (QmForm) | sí | Un formulario: su cabecera y su versión VIGENTE. |
form.id | string (uuid) | sí | |
form.tenant_id | string (uuid) | no | |
form.name | string | sí | |
form.archived | boolean | sí | |
form.version | integer | sí | La versión vigente. |
form.criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
form.criteria[].key | string | sí | |
form.criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
form.criteria[].kind | yes_no · scale | sí | |
form.criteria[].weight | integer | sí | |
form.criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
form.criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
form.criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
form.pass_threshold | integer | sí | |
form.version_created_at | string (date-time) | no | |
form.created_at | string (date-time) | no | |
form.updated_at | string (date-time) | no |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Editar un formulario de QM para crear una versión NUEVA
http
PATCH /v1/qm/forms/{id}Envía al menos uno de criteria, pass_threshold; el que se omita se hereda de la versión vigente. Esto añade la versión n+1 y deja la versión n exactamente como estaba: una evaluación puede haberla citado. Una edición que no cambia nada no escribe ninguna versión y devuelve la vigente. base_version es opcional y, si se envía, debe ser la versión vigente; si no, 409 y no se escribe nada, de modo que dos personas que editan el mismo formulario no se sobrescriben sin enterarse. Un formulario archivado no se puede editar (409); un formulario tiene como máximo 1 000 versiones (422). Requiere qm_enabled y un login account_admin.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
criteria | arreglo de QmCriterion | no | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
criteria[].key | string | sí | |
criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
criteria[].kind | yes_no · scale | sí | |
criteria[].weight | integer | sí | |
criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
pass_threshold | integer | no | |
base_version | integer | no |
json
{
"base_version": 1,
"pass_threshold": 85
}Respuestas
200
El formulario, en su versión vigente tras la edición.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
form | object (QmForm) | sí | Un formulario: su cabecera y su versión VIGENTE. |
form.id | string (uuid) | sí | |
form.tenant_id | string (uuid) | no | |
form.name | string | sí | |
form.archived | boolean | sí | |
form.version | integer | sí | La versión vigente. |
form.criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
form.criteria[].key | string | sí | |
form.criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
form.criteria[].kind | yes_no · scale | sí | |
form.criteria[].weight | integer | sí | |
form.criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
form.criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
form.criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
form.pass_threshold | integer | sí | |
form.version_created_at | string (date-time) | no | |
form.created_at | string (date-time) | no | |
form.updated_at | string (date-time) | no |
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"
}
}403 — QmWriteForbidden
Se rechazó una escritura de gestión de calidad (QM). code qm_not_enabled: el indicador qm_enabled del tenant está apagado (viene apagado por defecto; un indicador que no se puede leer cuenta como apagado). code role_forbidden: el llamante no es un login del dashboard con el rol account_admin (una clave dd_, un member). code forbidden: el tenant está suspendido o el token es un token de widget.
json
{
"error": {
"code": "qm_not_enabled",
"message": "QM is not enabled for this tenant"
}
}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"
}
}409 — Conflict
Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.
json
{
"error": {
"code": "conflict",
"message": "invalid state transition"
}
}422 — Unprocessable
La forma es correcta, la semántica no (regla de dominio incumplida).
json
{
"error": {
"code": "unprocessable",
"message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
}
}Archivar un formulario de QM
http
DELETE /v1/qm/forms/{id}Los formularios nunca se borran: una evaluación puede haber citado una de sus versiones. Archivar libera el nombre para un formulario nuevo y saca el formulario de la lista por defecto; ya no se puede editar. Idempotente. Requiere qm_enabled y un login account_admin, como las demás escrituras.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
204
El formulario está archivado.
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"
}
}403 — QmWriteForbidden
Se rechazó una escritura de gestión de calidad (QM). code qm_not_enabled: el indicador qm_enabled del tenant está apagado (viene apagado por defecto; un indicador que no se puede leer cuenta como apagado). code role_forbidden: el llamante no es un login del dashboard con el rol account_admin (una clave dd_, un member). code forbidden: el tenant está suspendido o el token es un token de widget.
json
{
"error": {
"code": "qm_not_enabled",
"message": "QM is not enabled for this tenant"
}
}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"
}
}Todas las versiones de un formulario de QM, de la más antigua a la más reciente
http
GET /v1/qm/forms/{id}/versionsParámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
Las versiones (con tope de 1 000).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
versions | arreglo de QmFormVersion | sí | |
versions[].form_id | string (uuid) | sí | |
versions[].version | integer | sí | |
versions[].criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
versions[].criteria[].key | string | sí | |
versions[].criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
versions[].criteria[].kind | yes_no · scale | sí | |
versions[].criteria[].weight | integer | sí | |
versions[].criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
versions[].criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
versions[].criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
versions[].pass_threshold | integer | sí | |
versions[].created_at | string (date-time) | no |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Una versión de un formulario de QM
http
GET /v1/qm/forms/{id}/versions/{version}Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
version | path | integer | sí | El número de versión (>= 1). Una desconocida, mal formada o de otro tenant se lee como 404. |
Respuestas
200
La versión, exactamente como se escribió.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
version | object (QmFormVersion) | sí | Una versión inmutable de un formulario. |
version.form_id | string (uuid) | sí | |
version.version | integer | sí | |
version.criteria | arreglo de QmCriterion | sí | Lista ORDENADA de criterios de puntuación. key es única dentro del formulario; la puntuación de una evaluación se calcula a partir de ellos, en puntos básicos. |
version.criteria[].key | string | sí | |
version.criteria[].label | string | sí | Sin espacios en los extremos, no en blanco. |
version.criteria[].kind | yes_no · scale | sí | |
version.criteria[].weight | integer | sí | |
version.criteria[].max | integer | no | Obligatorio para scale, rechazado para yes_no. |
version.criteria[].critical | boolean | no | Sólo yes_no: un criterio crítico fallido hace fallar toda la evaluación. |
version.criteria[].na_allowed | boolean | no | El evaluador puede marcar el criterio como no aplicable. |
version.pass_threshold | integer | sí | |
version.created_at | string (date-time) | no |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Las evaluaciones de QM de una grabación, de la más reciente a la más antigua
http
GET /v1/recordings/{id}/evaluationsTodas las evaluaciones escritas para la grabación, correcciones incluidas: cada fila indica si es la current (ninguna otra fila la reemplaza) y a qué fila reemplaza con supersedes_id. Tope de 200. Las lecturas funcionan con qm_enabled activado o no. Un login del dashboard que no puede evaluar (un puesto de agente, un member) recibe 403 role_forbidden: las filas indican quién evaluó. Una clave dd_ lee. El comment sólo está presente para un evaluador elegible (un puesto de supervisor o admin, o un login account_admin sin puesto); para cualquier otro la clave está ausente.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
Las evaluaciones.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
evaluations | arreglo de QmEvaluation | sí | |
evaluations[].id | string (uuid) | sí | |
evaluations[].recording_id | string (uuid) | sí | |
evaluations[].call_attempt_id | string (uuid) | sí | |
evaluations[].agent_id | string (uuid) | sí | |
evaluations[].campaign_id | string,null (uuid) | no | |
evaluations[].form_id | string (uuid) | sí | |
evaluations[].form_version | integer | sí | |
evaluations[].evaluator_user_id | string,null (uuid) | sí | El usuario firmante; null cuando evaluator_kind es automatic. |
evaluations[].evaluator_kind | human · automatic | sí | |
evaluations[].answers | object | sí | |
evaluations[].score_bp | integer | sí | Puntos básicos: 8750 es 87,5 %. |
evaluations[].auto_failed | boolean | sí | Un criterio crítico se respondió que no; la puntuación es 0. |
evaluations[].passed | boolean | sí | |
evaluations[].supersedes_id | string,null (uuid) | no | |
evaluations[].current | boolean | sí | Ninguna otra evaluación reemplaza a esta. |
evaluations[].comment | string,null | no | Presente sólo para un evaluador elegible. |
evaluations[].created_at | string (date-time) | sí |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Evaluar una grabación con una versión de formulario de QM
http
POST /v1/recordings/{id}/evaluationsUn supervisor puntúa UNA grabación available contra una versión de un formulario vigente y la fila queda congelada: nunca se edita. Para corregirla, envía el mismo cuerpo con supersedes_id apuntando a la evaluación VIGENTE de esa grabación y formulario; eso añade una fila nueva y deja la anterior como estaba. Sin supersedes_id una grabación admite una evaluación por formulario (409 en cuanto hay una).
La puntuación se calcula aquí, en puntos básicos (8750 es 87,5 %), a partir de la versión que citas y de tus answers: yes_no admite true o false, scale un entero de 0 a max, y "na" donde el criterio lo permite. Se exige una respuesta para cada criterio. agent_id, call_attempt_id y campaign_id salen de la grabación, nunca del cuerpo. Los campos desconocidos se rechazan.
Las comprobaciones se ejecutan en este orden y la primera que falla no escribe nada: qm_enabled (403 qm_not_enabled), la persona (403 role_forbidden), 400 (forma del cuerpo), 404 (grabación), 403 self_evaluation_forbidden (la grabación es del propio evaluador), 409 (grabación no available), 422 (sin agente, pierna de IA, formulario o versión desconocidos, respuestas que no encajan), 409 (formulario archivado, o la cadena). Una clave de API dd_ es un servicio, no una persona: 403 role_forbidden.
Se rechaza una corrección mientras la evaluación que corrige tenga una disputa ABIERTA (el agente la abrió y nadie la resolvió): 409 con error.codeevaluation_under_dispute, no se escribe nada, y la evaluación sigue contando en los informes de gestión de calidad (QM) con su disputa abierta. Esa evaluación sólo se corrige resolviendo la disputa como adjusted. Una vez que la disputa se resuelve como upheld, se vuelve a aceptar una corrección.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
form_id | string (uuid) | sí | Un formulario vigente del tenant. |
form_version | integer | sí | La versión contra la que se puntúan las respuestas. |
answers | object | sí | criterion key -> answer. true/false para yes_no, un entero de 0 a max para scale, la cadena na donde na_allowed. Una entrada por criterio. |
comment | string | no | Texto libre opcional; en blanco se lee como ausente. |
supersedes_id | string (uuid) | no | La evaluación VIGENTE de esta grabación y formulario, para corregirla. |
json
{
"form_id": "5d3b0c8e-4f3a-4a0e-9d1c-0a5f9a9b7e11",
"form_version": 1,
"answers": {
"identificacion": true,
"mini_miranda": true,
"empatia": 4
},
"comment": "Buena apertura; falto cerrar con el aviso de grabacion."
}Respuestas
201
La evaluación, con su puntuación y como la vigente.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
evaluation | object (QmEvaluation) | sí | Una evaluación de gestión de calidad (QM) congelada. Las correcciones son otras filas que apuntan aquí mediante supersedes_id. evaluator_kind es human (una persona la firmó, evaluator_user_id es ese usuario) o automatic (la pasada de IA posterior a la llamada sobre los criterios del formulario: nadie la firmó y evaluator_user_id es null). La puntuación la calcula core en ambos casos, nunca el modelo. Una evaluación humana de la misma grabación y formulario reemplaza a una automática mediante la misma cadena supersedes_id. |
evaluation.id | string (uuid) | sí | |
evaluation.recording_id | string (uuid) | sí | |
evaluation.call_attempt_id | string (uuid) | sí | |
evaluation.agent_id | string (uuid) | sí | |
evaluation.campaign_id | string,null (uuid) | no | |
evaluation.form_id | string (uuid) | sí | |
evaluation.form_version | integer | sí | |
evaluation.evaluator_user_id | string,null (uuid) | sí | El usuario firmante; null cuando evaluator_kind es automatic. |
evaluation.evaluator_kind | human · automatic | sí | |
evaluation.answers | object | sí | |
evaluation.score_bp | integer | sí | Puntos básicos: 8750 es 87,5 %. |
evaluation.auto_failed | boolean | sí | Un criterio crítico se respondió que no; la puntuación es 0. |
evaluation.passed | boolean | sí | |
evaluation.supersedes_id | string,null (uuid) | no | |
evaluation.current | boolean | sí | Ninguna otra evaluación reemplaza a esta. |
evaluation.comment | string,null | no | Presente sólo para un evaluador elegible. |
evaluation.created_at | string (date-time) | sí |
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"
}
}403 — QmEvaluateForbidden
Se rechazó evaluar una grabación. code qm_not_enabled: el indicador qm_enabled del tenant está apagado. code role_forbidden: el llamante no es un evaluador elegible (una clave dd_, un member, un puesto que no es de supervisor o admin, un puesto inactivo). code self_evaluation_forbidden: la grabación es del propio llamante. code forbidden: el tenant está suspendido o el token es un token de widget.
json
{
"error": {
"code": "role_forbidden",
"message": "evaluating a recording takes a supervisor or admin seat, or an account_admin login"
}
}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"
}
}409 — Conflict
Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.
json
{
"error": {
"code": "conflict",
"message": "invalid state transition"
}
}422 — Unprocessable
La forma es correcta, la semántica no (regla de dominio incumplida).
json
{
"error": {
"code": "unprocessable",
"message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
}
}Una evaluación de QM de una grabación
http
GET /v1/recordings/{id}/evaluations/{evaluation_id}La fila exactamente como se escribió, más si sigue siendo la current. Un id que pertenece a otra grabación u otro tenant se lee como 404. El comment y el 403 para un login que no puede evaluar siguen la misma regla que en el listado.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
evaluation_id | path | string (uuid) | sí | El UUID de la evaluación. Mal formado, desconocido o de otro tenant se lee como 404. |
Respuestas
200
La evaluación.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
evaluation | object (QmEvaluation) | sí | Una evaluación de gestión de calidad (QM) congelada. Las correcciones son otras filas que apuntan aquí mediante supersedes_id. evaluator_kind es human (una persona la firmó, evaluator_user_id es ese usuario) o automatic (la pasada de IA posterior a la llamada sobre los criterios del formulario: nadie la firmó y evaluator_user_id es null). La puntuación la calcula core en ambos casos, nunca el modelo. Una evaluación humana de la misma grabación y formulario reemplaza a una automática mediante la misma cadena supersedes_id. |
evaluation.id | string (uuid) | sí | |
evaluation.recording_id | string (uuid) | sí | |
evaluation.call_attempt_id | string (uuid) | sí | |
evaluation.agent_id | string (uuid) | sí | |
evaluation.campaign_id | string,null (uuid) | no | |
evaluation.form_id | string (uuid) | sí | |
evaluation.form_version | integer | sí | |
evaluation.evaluator_user_id | string,null (uuid) | sí | El usuario firmante; null cuando evaluator_kind es automatic. |
evaluation.evaluator_kind | human · automatic | sí | |
evaluation.answers | object | sí | |
evaluation.score_bp | integer | sí | Puntos básicos: 8750 es 87,5 %. |
evaluation.auto_failed | boolean | sí | Un criterio crítico se respondió que no; la puntuación es 0. |
evaluation.passed | boolean | sí | |
evaluation.supersedes_id | string,null (uuid) | no | |
evaluation.current | boolean | sí | Ninguna otra evaluación reemplaza a esta. |
evaluation.comment | string,null | no | Presente sólo para un evaluador elegible. |
evaluation.created_at | string (date-time) | sí |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Resolver la disputa del agente sobre una evaluación
http
POST /v1/qm/evaluations/{id}/dispute/resolutionUn evaluador elegible (un puesto de supervisor o admin, o un login account_admin sin puesto) cierra la disputa que abrió el agente evaluado. El {id} es la evaluación DISPUTADA. decision es upheld (la evaluación se mantiene) o adjusted (se reemplaza). El evaluador no puede ser el agente de la evaluación. Una clave dd_ es un servicio, no una persona: 403 role_forbidden.
adjusted admite answers (obligatorio, con la misma forma que al evaluar) y un comment opcional para la nueva evaluación, que repite el formulario y la versión de la disputada y se puntúa aquí a partir de tus respuestas. La nueva evaluación y el evento que la cita (adjusted_evaluation_id) se escriben en UNA sola transacción: o existen las dos filas o ninguna. upheld no admite ni answers ni comment. La nueva evaluación se lee con GET /v1/recordings/{id}/evaluations/{evaluation_id}.
Las comprobaciones se ejecutan en este orden y la primera que falla no escribe nada: qm_enabled (403 qm_not_enabled), la persona (403 role_forbidden), 404 (un id que no es un UUID), 400 (la forma del cuerpo), 404 (evaluación), 403 self_evaluation_forbidden, 409 (la evaluación no tiene disputa, o ya está resuelta), 422 (respuestas que no encajan con la versión del formulario), 409 (la evaluación ya no es la vigente).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
decision | upheld · adjusted | sí | |
note | string | no | Texto libre opcional; en blanco se lee como ausente. |
answers | object | no | Una respuesta por criterio de la versión del formulario de la evaluación disputada: un booleano para yes_no, un entero de 0 a max para scale, la cadena na donde el criterio lo permite. |
comment | string | no | Texto libre opcional de la nueva evaluación. |
json
{
"decision": "adjusted",
"note": "Tiene razon: el aviso se dijo en el minuto 1:10.",
"answers": {
"identificacion": true,
"mini_miranda": true,
"empatia": 4
}
}Respuestas
201
El evento que la resolución añadió al hilo de la disputa.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
dispute_event | object (QmDisputeResolution) | sí | El evento que una resolución añadió al hilo de la disputa. |
dispute_event.id | string (uuid) | sí | |
dispute_event.evaluation_id | string (uuid) | sí | La evaluación disputada. |
dispute_event.kind | upheld · adjusted | sí | |
dispute_event.note | string,null | sí | |
dispute_event.adjusted_evaluation_id | string,null (uuid) | sí | La evaluación que reemplaza a la disputada; null cuando se mantiene (upheld). |
dispute_event.created_at | string (date-time) | sí |
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"
}
}403 — QmEvaluateForbidden
Se rechazó evaluar una grabación. code qm_not_enabled: el indicador qm_enabled del tenant está apagado. code role_forbidden: el llamante no es un evaluador elegible (una clave dd_, un member, un puesto que no es de supervisor o admin, un puesto inactivo). code self_evaluation_forbidden: la grabación es del propio llamante. code forbidden: el tenant está suspendido o el token es un token de widget.
json
{
"error": {
"code": "role_forbidden",
"message": "evaluating a recording takes a supervisor or admin seat, or an account_admin login"
}
}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"
}
}409 — Conflict
Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.
json
{
"error": {
"code": "conflict",
"message": "invalid state transition"
}
}422 — Unprocessable
La forma es correcta, la semántica no (regla de dominio incumplida).
json
{
"error": {
"code": "unprocessable",
"message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
}
}Cifras de QM por agente y formulario en una ventana
http
GET /v1/qm/reports/agentsCifras derivadas en cada llamada a partir de las evaluaciones VIGENTES (una corrección reemplaza la fila que sustituye; la antigua deja de contar) cuya grabación empezó en [from, to). Una fila por agente, formulario Y evaluator_kind: dos formularios son dos escalas, y la puntuación de una persona y la pasada automática nunca se promedian juntas. Por fila: evaluations, avg_score_bp (puntuación media en puntos básicos, redondeada; null cuando no hay nada que promediar, nunca 0), pass_rate_bp (passed sobre evaluations en puntos básicos, null igualmente), passed y auto_failed (anuladas por un criterio crítico). Nunca se devuelve comentario, número de teléfono ni otro texto libre.
from y to son obligatorios, from debe ser anterior a to y la ventana no puede superar 31 días (400 en caso contrario). Sin agent_id sólo aparecen los agentes con datos; con uno existente que no tiene evaluaciones en la ventana la respuesta es UNA fila con evaluations: 0 y los promedios null; uno desconocido, o de otro tenant, da 404. Las lecturas funcionan con qm_enabled activado o no. Un login del dashboard que no puede evaluar (un puesto de agente, un member) recibe 403 role_forbidden; una clave dd_ lee.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
from | query | string (date-time) | sí | Cota inferior RFC 3339 del inicio de la grabación (inclusiva). |
to | query | string (date-time) | sí | Cota superior RFC 3339 del inicio de la grabación (exclusiva); como máximo 31 días después de from. |
agent_id | query | string (uuid) | no | Sólo este agente. |
form_id | query | string (uuid) | no | Sólo este formulario. |
evaluator_kind | query | human · automatic | no | Sólo evaluaciones humanas, o sólo automáticas. Sin él aparecen ambas, en filas separadas. |
Respuestas
200
Las filas, ordenadas por agente, formulario y luego tipo de evaluador.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
from | string (date-time) | sí | |
to | string (date-time) | sí | |
rows | arreglo de QmAgentReportRow | sí | |
rows[].agent_id | string (uuid) | sí | |
rows[].form_id | string,null (uuid) | sí | |
rows[].evaluator_kind | human · automatic · null | sí | Las evaluaciones humanas y las automáticas nunca se promedian juntas: cada una tiene su propia fila. Null sólo en la fila vacía de una entidad solicitada sin filtro evaluator_kind. |
rows[].evaluations | integer | sí | Las evaluaciones vigentes contabilizadas. |
rows[].avg_score_bp | integer,null | sí | Puntuación media en puntos básicos, redondeada; null cuando no hay evaluaciones. |
rows[].pass_rate_bp | integer,null | sí | Aprobadas sobre evaluaciones en puntos básicos; null cuando no hay evaluaciones. |
rows[].passed | integer | sí | |
rows[].auto_failed | integer | sí | Anuladas por un criterio crítico. |
rows[].adjusted | integer | sí | Evaluaciones vigentes que reemplazaron a una disputada (la disputa se resolvió como adjusted). |
rows[].open_disputes | integer | sí | Evaluaciones vigentes con una disputa abierta y aún no resuelta. Siguen contando en todas las demás cifras. |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Cifras de QM por campaña y formulario en una ventana
http
GET /v1/qm/reports/campaignsCifras derivadas en cada llamada a partir de las evaluaciones VIGENTES (una corrección reemplaza la fila que sustituye; la antigua deja de contar) cuya grabación empezó en [from, to). Una fila por campaña, formulario Y evaluator_kind: dos formularios son dos escalas, y la puntuación de una persona y la pasada automática nunca se promedian juntas. Por fila: evaluations, avg_score_bp (puntuación media en puntos básicos, redondeada; null cuando no hay nada que promediar, nunca 0), pass_rate_bp (passed sobre evaluations en puntos básicos, null igualmente), passed y auto_failed (anuladas por un criterio crítico). Nunca se devuelve comentario, número de teléfono ni otro texto libre.
from y to son obligatorios, from debe ser anterior a to y la ventana no puede superar 31 días (400 en caso contrario). Sin campaign_id sólo aparecen las campañas con datos; con una existente que no tiene evaluaciones en la ventana la respuesta es UNA fila con evaluations: 0 y los promedios null; una desconocida, o de otro tenant, da 404. Las lecturas funcionan con qm_enabled activado o no. Un login del dashboard que no puede evaluar (un puesto de agente, un member) recibe 403 role_forbidden; una clave dd_ lee.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
from | query | string (date-time) | sí | Cota inferior RFC 3339 del inicio de la grabación (inclusiva). |
to | query | string (date-time) | sí | Cota superior RFC 3339 del inicio de la grabación (exclusiva); como máximo 31 días después de from. |
campaign_id | query | string (uuid) | no | Sólo esta campaña. |
form_id | query | string (uuid) | no | Sólo este formulario. |
evaluator_kind | query | human · automatic | no | Sólo evaluaciones humanas, o sólo automáticas. Sin él aparecen ambas, en filas separadas. |
Respuestas
200
Las filas, ordenadas por campaña, formulario y luego tipo de evaluador.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
from | string (date-time) | sí | |
to | string (date-time) | sí | |
rows | arreglo de QmCampaignReportRow | sí | |
rows[].campaign_id | string,null (uuid) | sí | null para las grabaciones sin campaña. |
rows[].form_id | string,null (uuid) | sí | |
rows[].evaluator_kind | human · automatic · null | sí | Las evaluaciones humanas y las automáticas nunca se promedian juntas: cada una tiene su propia fila. Null sólo en la fila vacía de una entidad solicitada sin filtro evaluator_kind. |
rows[].evaluations | integer | sí | Las evaluaciones vigentes contabilizadas. |
rows[].avg_score_bp | integer,null | sí | Puntuación media en puntos básicos, redondeada; null cuando no hay evaluaciones. |
rows[].pass_rate_bp | integer,null | sí | Aprobadas sobre evaluaciones en puntos básicos; null cuando no hay evaluaciones. |
rows[].passed | integer | sí | |
rows[].auto_failed | integer | sí | Anuladas por un criterio crítico. |
rows[].adjusted | integer | sí | Evaluaciones vigentes que reemplazaron a una disputada (la disputa se resolvió como adjusted). |
rows[].open_disputes | integer | sí | Evaluaciones vigentes con una disputa abierta y aún no resuelta. Siguen contando en todas las demás cifras. |
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"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}