Appearance
Inteligencia post-llamada
English version: Post-call intelligence.
Esta guía cubre lo que la plataforma deriva de una llamada terminada: la búsqueda en transcripciones, la lectura del resumen y la disposición sugerida por IA de una llamada, y el freno de gasto que acota a los proveedores de pago detrás de ambas. Para la transcripción en sí, quién puede leerla y cómo se redacta la PII, véase Recording transcripts, audio redaction and the screen-monitoring notice (en inglés); esta página no lo repite.
Los nombres de campo, enums, límites y códigos de estado salen de openapi/dialer-v1.yaml vendorizado (info.version: 0.105.0). Donde la spec calla, la afirmación cita un fichero de core en el tag v0.105.0. La API Reference es la autoridad sobre las formas.
Qué gobierna la disponibilidad
La spec no define ningún flag de tenant para estas rutas. Lo que decide si existen datos es, según core v0.105.0:
| Condición | Efecto | Fuente |
|---|---|---|
| Tier del plan del tenant | La transcripción y el análisis sólo están incluidos en los tiers scale y enterprise; cualquier otro tier, incluido uno desconocido o vacío, no tiene derecho. | lib/dialer/post_call/entitlement.ex |
| Elegibilidad de la grabación | Transcribir exige estado available, duración conocida entre el mínimo y el máximo configurados (por defecto 10 y 3600 segundos), una tipificación terminal de clase contacted, una llamada que no sea de IA y que no haya transcripción previa. | lib/dialer/post_call/eligibility.ex, config/runtime.exs (INFERENCE_TRANSCRIBE_MIN_SECS, INFERENCE_TRANSCRIBE_MAX_SECS) |
| Configuración de inferencia de la celda | El proveedor y el techo mensual se fijan por celda mediante entorno (INFERENCE_TRANSCRIPTION_PROVIDER, INFERENCE_ANALYSIS_PROVIDER, INFERENCE_MONTHLY_CAP_MICRO_USD). Véase Freno de gasto. | config/runtime.exs |
Una llamada que no califica no tiene transcripción ni análisis, y las rutas de lectura responden 404 (getRecordingTranscript, getCallAttemptAnalysis). Ninguna de las dos respuestas tiene un estado "pendiente". Qué tenant está en qué tier, y qué celda tiene qué proveedor o techo, no lo dice la spec y no se dice aquí.
Búsqueda en transcripciones
http
GET /v1/transcripts/searchOperación searchTranscripts. Encuentra las grabaciones del tenant que llama cuya transcripción contiene q, de la más nueva a la más antigua.
| Parámetro | Obligatorio | Regla |
|---|---|---|
q | sí | La palabra o frase: de 1 a 8 palabras. |
from | sí | Cota inferior RFC 3339 sobre el inicio de la grabación, inclusiva. |
to | sí | Cota superior RFC 3339; como máximo 31 días después de from. |
agent_id | no | Sólo grabaciones de este agente (uuid). |
campaign_id | no | Sólo grabaciones de esta campaña (uuid). |
limit | no | De 1 a 50, por defecto 20; un valor mayor se recorta, no se rechaza. |
cursor | no | Cursor opaco por keyset: el next_cursor de la página anterior. |
from debe ser anterior a to y la ventana no puede superar 31 días; si no, 400.
bash
curl -sG "$API/v1/transcripts/search" \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode "q=promesa de pago" \
--data-urlencode "from=2026-10-01T00:00:00Z" \
--data-urlencode "to=2026-10-08T00:00:00Z"Cómo coincide
- Las palabras de
qse buscan en un índice ciego: un hash con clave que lleva el tenant dentro, de modo que la misma palabra produce un hash distinto en dos tenants y ninguna palabra se guarda en claro. Sólo se descifran los segmentos candidatos de las grabaciones candidatas, y la frase se verifica sobre el texto descifrado. (Descripción desearchTranscripts;lib/dialer/post_call/term_search.ex.) - Una frase coincide sólo si sus palabras aparecen seguidas dentro de un segmento. La coincidencia es exacta sobre la palabra normalizada: se ignoran mayúsculas, acentos y puntuación; no hay raíces ni sinónimos.
- Una llamada examina como máximo 100 grabaciones candidatas y 500 segmentos. Cuando ese tope corta la búsqueda antes de llenar la página,
cappedestrueynext_cursorcontinúa desde donde quedó. Una página puede por tanto salir corta concapped: true; siguenext_cursor.
Respuesta
TranscriptSearchPage tiene results, next_cursor y capped. Cada resultado tiene recording_id, started_at, agent_id y campaign_id (ambos pueden ser null) y matches. Cada coincidencia tiene seq, channel (0 o 1), start_ms, end_ms y fragment: el texto del segmento alrededor de la coincidencia, con unos 40 caracteres de contexto a cada lado.
Quién puede buscar y qué se audita
La puerta es la de la transcripción individual: un login de dashboard (ddu_) con asiento de supervisor o admin, o un login account_admin sin asiento. Una clave dd_, un token de widget y un asiento de agente reciben 403 role_forbidden.
Cada búsqueda que se ejecuta añade un evento en la misma transacción: quién, cuándo, el hash de la consulta, la ventana, los filtros y cuántas grabaciones se examinaron y devolvieron; nunca el texto. Si el evento no se puede escribir no hay resultado y la respuesta es 503.
Códigos de estado: 200, 400, 401, 403, 503.
Retención y legal hold
Una grabación cuyo audio se purgó no tiene transcripción y nunca aparece en los resultados; una bajo legal_hold sí (searchTranscripts). La purga borra la transcripción junto con el audio, y una grabación bajo legal_hold conserva su transcripción (getRecordingTranscript). El hold se pone o se levanta con PUT /v1/recordings/{id}/legal-hold (setRecordingLegalHold), ámbito de gestión del tenant: una clave dd_ o un inicio de sesión account_admin; un member recibe 403 role_forbidden (core#2438), y la lectura no cambia. La retención es la política de grabación del tenant (setRecordingPolicy); cambiarla nunca re-fecha las grabaciones existentes.
La transcripción original (sin redactar)
http
POST /v1/recordings/{id}/transcript/originalOperación readRecordingTranscriptOriginal. El método es POST, no GET: la vista por defecto GET /v1/recordings/{id}/transcript tiene la PII redactada (redacted: true), y esta ruta es el único camino al texto original (redacted: false).
- Quién llama: una clave
dd_del tenant o un login de dashboard con el rolaccount_admin. Cualquier otro recibe403 role_forbidden. - Cuerpo:
reason, obligatorio, no vacío, de hasta 500 caracteres. - El actor, el motivo y el número de segmentos (nunca el texto) se escriben en un registro de acceso append-only antes de devolver el texto. Si la fila no se puede escribir, no sale texto y la respuesta es
503. 404igual que en la vista por defecto.
Códigos de estado: 200, 400, 401, 403, 404, 503. La forma RecordingTranscript y las reglas de redacción están en la guía de transcripciones.
Transcripciones de conversaciones de IA: redactadas por defecto
http
GET /v1/ai/conversations/{id}Operación getAiConversation. Devuelve una conversación de IA de voz con su transcripción y su resultado. El text de cada evento de la transcripción sale con la PII del catálogo cerrado redactada; role, at y policy_version no cambian.
Este comportamiento está en main de core (core#2462, 2026-10-23) y todavía no está en la spec vendorizada (info.version: 0.128.0), cuyo getAiConversation no lista el parámetro include_pii ni un 403; lo que sigue cita lib/dialer/pii.ex, lib/dialer/api/render.ex y lib/dialer/api/v1_router.ex de core. La API Reference lo mostrará cuando se refresque la spec.
Qué se redacta
El catálogo es cerrado y lo fija la plataforma: número de tarjeta de pago, número de seguro social, cuenta bancaria y claves/PIN. Nada más se toca: un teléfono o un id de agente salen tal cual. La máscara es:
| Dato | Sale como |
|---|---|
| Número de tarjeta (13 a 19 dígitos que pasan Luhn) | ****-****-****-1234 (los últimos cuatro) |
| Número de seguro social | ***-**-1234 (los últimos cuatro) |
Cuenta bancaria (IBAN, CLABE o dígitos tras un rótulo como account o routing) | ****1234 (los últimos cuatro) |
Valor tras PIN, CVV, password, clave y rótulos parecidos | [REDACTED] (no se conserva nada) |
La detección son reglas deterministas con dígito de control, no un modelo: es una red, no un clasificador. Una tarjeta pegada a otro número con un espacio no se enmascara, y una racha larga de dígitos que pasa el control por azar sí (lib/dialer/pii.ex, sección «Límites conocidos»).
Pedir el original: include_pii
Añade include_pii=true a la query para recibir el texto tal como se guardó.
- Quién: una llave
dd_del tenant o un login de dashboard con el rolaccount_admin. Cualquier otro que envíeinclude_pii=truerecibe403 role_forbidden, no una respuesta redactada en silencio. - Ausente,
falseo"false": la vista redactada. Cualquier otro valor es400. - Auditoría: cada petición con
include_pii=trueque encuentra la conversación deja constancia de quién la pidió y de qué tenant, como evento de telemetría y línea de log estructurada. A diferencia de la transcripción original de una grabación, no se pide motivo, no se escribe una fila y nada espera a la escritura: la auditoría es el evento y la línea de log (core#2462, mensaje del commit). - Es la misma regla y el mismo rol de las exportaciones que aceptan
include_pii(pii_mode/2env1_router.ex).
Lo que el runtime de voz IA ya enmascaró
Lo que el consumidor dijo en voz alta (tarjeta, seguro social, cuenta) lo enmascara el runtime de voz IA antes de emitir el evento de la conversación, así que core lo guarda enmascarado. include_pii=true devuelve el original de lo guardado: para esos datos dichos, el texto enmascarado. La IA responde una frase fija y ofrece una persona cuando el traspaso está disponible (ADR 17 de voice-ai, Módulo 22, §9).
Códigos de estado añadidos a esta ruta: 400 y 403 por include_pii; los demás son los de la spec.
Resumen de la llamada y disposición sugerida
http
GET /v1/call_attempts/{id}/analysisOperación getCallAttemptAnalysis. Devuelve el resumen de una llamada terminada y la disposición que la IA sugiere. Es una sugerencia, nunca un resultado: no se aplica nada.
Campos de analysis
| Campo | Significado |
|---|---|
id | uuid. Cítalo como suggestion_id al tipificar. |
call_attempt_id | El intento. |
source | transcript (una transcripción de grabación) o ai_conversation (una conversación de voice-AI). |
summary | Resumen de la llamada, hasta 1200 caracteres. Datos personales; el schema dice que nunca se registra en logs. |
suggested_code | Un code ACTIVO en el catálogo del tenant, o null. |
suggested_class | Clase de suggested_code en el catálogo hoy: contacted, no_contact, busy, machine, bad_number, do_not_call, other, o null. |
suggestion_note | Por qué suggested_code está o no fijado: suggested, model_none, not_in_catalog o inactive. |
confidence_pct | Entero de 0 a 100 o null; la confianza del modelo, presente sólo con un código sugerido. |
provider, model, prompt_version | Qué lo produjo. |
created_at | date-time. |
Una sugerencia de clase do_not_call se devuelve tal cual, y un cliente no debe preseleccionarla (getCallAttemptAnalysis, CallAnalysis).
Quién puede leerlo
El agente dueño del intento (un login ddu_ cuya persona está vinculada al asiento, o el token ddw_ ligado a ese agente) y un supervisor o admin. Una clave dd_ es un servicio y recibe 403 role_forbidden, igual que un login sin asiento y sin rol de supervisor.
404 cuando el intento no tiene análisis en este tenant: pertenece a otro agente u otro tenant, nunca se analizó (no elegible, sin derecho, o aún pendiente), su audio se purgó (la purga borra el análisis con él), o el id está mal formado. Un cliente trata el 404 como "aún no hay sugerencia" y mantiene la tipificación manual. Códigos de estado: 200, 401, 403, 404.
El análisis de una llamada humana se construye con la vista redactada de la transcripción; el de una llamada de IA, con los eventos de la conversación (lib/dialer/post_call/analyze_job.ex). La elegibilidad es el derecho por tier de arriba; el análisis sólo existe si la llamada produjo texto.
Confirmarla: suggestion_id
El agente confirma tipificando con POST /v1/call_attempts/{id}/disposition (operación recordDisposition, véase la API Reference), citando opcionalmente suggestion_id con el id del análisis:
json
{
"disposition": "promise_to_pay",
"suggestion_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"promise": { "amount_cents": 12500, "currency": "USD", "promised_date": "2026-07-21" }
}suggestion_idsólo registra que la sugerencia se mostró y qué tecleó el agente. El resultado es siempre ladispositiondel cuerpo, nunca la sugerida, y la sugerencia no cambia nada de la política de reintentos.- Un id mal formado es
400. Un id que no es el análisis de este intento es404y no se añade nada. - Citar dos veces la misma sugerencia registra una sola adopción.
En core la fila de adopción guarda el código sugerido (null si no hubo) y el código que tecleó el agente, sin PII (lib/dialer/post_call/analysis_adoption.ex).
Freno de gasto (falla cerrado)
Los proveedores de pago detrás de la transcripción y el análisis están acotados por un techo mensual de gasto. La spec no describe este freno (el documento OpenAPI no tiene ningún campo de techo de gasto para estas rutas), así que todo lo de esta sección sale de core v0.105.0.
- Techo. El techo mensual es un entero en micro-USD de
INFERENCE_MONTHLY_CAP_MICRO_USD, fijado por celda. Ausente, vacío, no numérico o0significa sin techo, y entonces todo proveedor de pago queda bloqueado; sólo sigue corriendo el proveedorlocal, que cuesta 0. (config/runtime.exs,lib/dialer/inference/provider.ex.) - Proveedor.
INFERENCE_TRANSCRIPTION_PROVIDERaceptalocalodeepgram;INFERENCE_ANALYSIS_PROVIDERaceptalocalogroq; cualquier otro valor impide que el nodo arranque. Si faltan, resuelven al proveedor de pago, que el freno bloquea sin techo. Nunca caen alocalpor omisión. - Reservar antes de gastar. Cada job pide una reserva al freno antes de llamar a un proveedor. Sólo
{:ok, reserve_id}autoriza la llamada. Una denegación (cap_unset,price_unknown,cap_reached,local_priced) termina el job sin gastar y no se reencola. Un libro que no se puede leer hace fallar ese intento sin gastar, y el job se reintenta hasta 5 intentos (lib/dialer/inference/brake.ex,lib/dialer/inference/ledger.ex,lib/dialer/post_call/transcribe_job.ex,lib/dialer/post_call/analyze_job.ex). - Falla cerrado en el libro. Un error de conexión, el timeout del lock, una excepción de la función o una respuesta que no se entiende se tratan como "no ejecutar"; un motivo de denegación desconocido también bloquea (
ledger.ex). - Falla cerrado ante el proveedor. Si el servicio de voz o de análisis responde con un proveedor distinto del reservado, core no guarda nada, libera la reserva y cancela sin reintentar (
transcribe_job.ex,analyze_job.ex). La transcripción también falla cerrada si no puede redactar la PII: no se guarda nada. - Tier. Un tier desconocido no tiene derecho y no se gasta en él (
entitlement.ex).
Para un cliente de la API el efecto visible es el mismo en todos los casos: no hay transcripción ni análisis y las rutas de lectura responden 404.
Lo que no cubre esta guía
- Si un tenant o celda concretos tienen hoy un techo distinto de cero, un proveedor de pago o un tier que califique. La spec no expone nada de eso.
- La forma de la transcripción, la redacción y el audio original: véase la guía de transcripciones.