Appearance
Academia
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.
Registros de formación de la Academia dentro de la app: la constancia auditable (quién completó qué y cuándo), de solo anexar y por agente.
Endpoints
| Método | Ruta | Resumen |
|---|---|---|
POST | /v1/academy/completions | Registrar UN hito de la Academia para un agente |
GET | /v1/academy/progress | El estado DERIVADO de la Academia de un agente |
GET | /v1/academy/transcript | El registro de formación auditable: quién completó qué y cuándo |
Registrar UN hito de la Academia para un agente
http
POST /v1/academy/completionsAnexa un evento de finalización (una lección terminada, una insignia obtenida) al flujo de formación de solo anexar del tenant. La fila es el registro que audita un cliente, así que nada aquí se puede reescribir después: la tabla no tiene permiso de UPDATE ni de DELETE y un trigger rechaza ambos.
Idempotente. Completar el mismo elemento dos veces no es un segundo evento: se devuelve el PRIMER registro sin cambios, de modo que occurred_at sigue indicando cuándo llegó realmente el agente.
occurred_at NO es una entrada: lo sella el servidor. Un registro de formación cuyo instante lo elige el navegador de la persona auditada no es evidencia.
Quién puede nombrar a quién (ADR #134). Un token de agente ddw_ escribe SOLO su PROPIO registro (agent_id se toma del token; nombrar a otro agente es 403). Una clave de máquina dd_ DEBE nombrar al agente: en el carril del tenant no existe un "yo", y asumir uno por defecto atribuiría la formación a quien hubiera llamado (400). Una sesión de inicio de sesión ddu_ vinculada a un asiento de agente escribe como ESE asiento (agent_id opcional o el suyo; otro agente es 403 forbidden); un ddu_ SIN asiento vinculado DEBE nombrar al agente (400) y solo puede nombrar a otro con users.role = account_admin; un member es 403 role_forbidden, una vez que la clave de catálogo del tenant ddu_author_role_enforced está activada (nace apagada: hasta entonces mantiene el contrato de dd_).
item_id se comprueba por formato, no se enumera: la fuente de verdad del currículo es el dashboard, así que una lección nueva no debe necesitar una migración de core. Minúsculas alfanuméricas más ._-, hasta 64 caracteres.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
agent_id | string (uuid) | no | Obligatorio para una clave dd_ y para una sesión ddu_ sin asiento; para un token ddw_ y para una sesión ddu_ vinculada a un asiento, solo puede ser su propio agente (y puede omitirse). |
kind | lesson · badge | sí | |
item_id | string | sí | Id del elemento del currículo, p. ej. ch1.dashboard. |
json
{
"agent_id": "8ac7f2f9-1f2c-4f7a-9d3b-1f0a6c5d4e3b",
"kind": "lesson",
"item_id": "ch1.dashboard"
}Respuestas
201
El hito tal como se registró (el PRIMER registro, si ya existía).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
id | string (uuid) | sí | |
agent_id | string (uuid) | sí | |
kind | lesson · badge | sí | |
item_id | string | sí | |
occurred_at | string (date-time) | sí | Sellado por el servidor, nunca por quien llama. |
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"
}
}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"
}
}El estado DERIVADO de la Academia de un agente
http
GET /v1/academy/progressPliega el flujo de finalizaciones del agente en el estado a partir del cual renderiza la Academia. El capítulo y el nivel NO se calculan aquí deliberadamente: qué lección pertenece a qué capítulo es el currículo del cliente, y duplicarlo del lado del servidor haría que cada capítulo nuevo fuera un despliegue de core.
Un token ddw_ lee su PROPIO progreso (agent_id opcional, y solo se acepta el suyo); lo mismo una sesión de inicio de sesión ddu_ vinculada a un asiento de agente (otro agente es 403 forbidden). Una clave dd_ DEBE pasar agent_id; lo mismo un ddu_ SIN asiento vinculado, y solo puede leer el de otro agente con users.role = account_admin; un member es 403 role_forbidden, una vez que la clave de catálogo del tenant ddu_author_role_enforced está activada (nace apagada: hasta entonces mantiene el contrato de dd_; ADR #134). Un agente de otro tenant se lee como un documento VACÍO: RLS devuelve cero filas, el handler no compara ids.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
agent_id | query | string (uuid) | no |
Respuestas
200
El documento de progreso derivado.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
agent_id | string (uuid) | sí | |
lessons_done | arreglo de string | sí | Ids de lecciones en orden de finalización. |
badges | object | sí | Id de insignia -> el instante en que se obtuvo. |
updated_at | string,null (date-time) | sí | El hito más reciente, o null cuando el agente aún no tiene ninguno. |
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"
}
}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"
}
}El registro de formación auditable: quién completó qué y cuándo
http
GET /v1/academy/transcriptLa constancia en sí: todos los hitos de todos los agentes del tenant, con el nombre, el email y el rol del agente, en una ventana. Este es el entregable de negocio de MT-CLI-20: el informe que pide un cliente que audita la formación de sus gestores de cobranza.
La ventana es semiabierta: from inclusive, to EXCLUSIVO, como los informes de facturación, de modo que un hito en un corte de periodo pertenece exactamente a un lado. from es por defecto la época y no una ventana reciente: la pregunta de un auditor es "todo lo que este gestor ha completado alguna vez", y el flujo está acotado por construcción (una fila por agente y elemento, nunca más).
Solo ámbito de tenant: esta es la exportación de todo el tenant, pedida por quien audita, no por el auditado. Un token de agente ddw_ se deniega antes del handler (403).
?format=csv (o Accept: text/csv) devuelve el artefacto con su propio X-Artifact-SHA256, de modo que un auditor que lo archive pueda demostrar después que es el que se le entregó.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
from | query | string (date-time) | no | Límite inferior RFC 3339 (campo específico del endpoint; por defecto los últimos 30 días UTC donde se indica). |
to | query | string (date-time) | no | Límite superior RFC 3339. |
agent_id | query | string (uuid) | no | |
format | query | csv | no | csv para el artefacto descargable (equivalente: Accept: text/csv). |
Respuestas
200
La transcripción (JSON) o el artefacto CSV.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
from | string (date-time) | sí | |
to | string (date-time) | sí | |
agent_id | string,null (uuid) | sí | El agente por el que se filtró el informe, o null para todo el tenant. |
total | integer | sí | |
rows | arreglo de AcademyTranscriptRow | sí | |
rows[].agent_id | string (uuid) | sí | |
rows[].agent_name | string | sí | |
rows[].agent_email | string | sí | |
rows[].agent_role | agent · supervisor · admin | sí | El rol del agente HOY (una unión, no una instantánea): quien cambió de rol no ha dejado de haber completado su formación. |
rows[].kind | lesson · badge | sí | |
rows[].item_id | string | sí | |
rows[].occurred_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 — 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"
}
}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"
}
}