Appearance
Transcripciones de grabaciones, redacción de audio y aviso de monitoreo de pantalla
English version: Recording transcripts, redaction, screen recording.
Esta guía cubre dos cosas que la plataforma hace con una llamada grabada y las rutas que las exponen: la transcripción posterior a la llamada y la redacción de datos personales en el audio. Otras secciones dan el contrato de las rutas del aviso de monitoreo de pantalla, de la grabación de pantalla y de las sesiones de monitoreo de pantalla en vivo. Los nombres de campo, los enums y los códigos de estado están copiados del documento OpenAPI vendorizado (core v0.105.0); la Referencia de la API generada a partir de él es la autoridad sobre las formas.
Disponibilidad
| Capacidad | Disponibilidad |
|---|---|
GET /v1/recordings/{id}/transcript | Publicado en core v0.103.0 (core#1971, core#2064) |
GET / POST /v1/recordings/{id}/redaction, POST /v1/recordings/{id}/original-url | Publicado en core v0.103.0 (core#2016) |
GET /v1/screen-monitor/notice, POST / GET /v1/screen-monitor/acks | Publicado en core v0.103.0 (core#1989) |
PUT /v1/screen-recordings/{id}/chunks/{n}, POST /v1/screen-recordings/gaps | Primero en core v0.104.0 (core#2036); presente en la spec de v0.105.0 |
PUT /v1/screen-monitor/armed, POST /v1/screen-monitor/sessions, POST .../sessions/{id}/signal, POST .../sessions/{id}/end | Primero en core v0.104.0 (core#1990); presente en la spec de v0.105.0 |
GET /v1/screen-monitor/sessions | Primero en core v0.105.0 (core#1990) |
Toda ruta de screen-monitor y screen-recordings responde 404 not_found mientras la bandera screen_monitor_enabled del tenant esté apagada, salvo las lecturas del libro (GET /v1/screen-monitor/acks, GET /v1/screen-monitor/sessions) y POST .../sessions/{id}/end, que según la spec no la consultan.
Que una grabación dada tenga transcripción depende de la elegibilidad (ver abajo) y de que la celda tenga configurado un motor de transcripción. Una grabación sin transcripción responde 404, no un documento vacío.
Transcripción
http
GET /v1/recordings/{id}/transcriptEl cuerpo 200 es { "transcript": { ... } } con los campos del esquema RecordingTranscript:
| Campo | Tipo | Significado |
|---|---|---|
recording_id | uuid | La grabación. |
provider | string | Proveedor de voz a texto que la produjo. |
model | string | Modelo que la produjo. |
language | string o null | Idioma que reportó el proveedor, cuando reportó alguno. |
audio_seconds | integer | Segundos de audio facturados. |
segment_count | integer | Número de segmentos con tiempos. |
created_at | date-time | Cuándo se guardó la transcripción. |
segments | array | Segmentos con tiempos, ordenados por seq. |
Cada entrada de segments tiene seq (entero desde 0), channel (canal de audio, 0 o 1), start_ms, end_ms y text.
Quién puede leerla
Una transcripción es el dato personal en bruto de una llamada, así que la ruta es estrecha:
- Permitido: un login del dashboard (
ddu_) con un asiento de supervisor o administrador, o un loginaccount_adminsin asiento. - No permitido: una llave de API
dd_del tenant, un token de widget y un asiento de agente reciben todos403 role_forbidden.
Qué se audita
Cada entrega anexa un evento de lectura en la misma transacción que la lectura: quién la leyó, cuándo, qué grabación y cuántos segmentos. El evento nunca contiene el texto. Si el evento no se puede escribir, no se devuelve texto y la respuesta es 503; reintenta más tarde.
Cuándo es 404
404 es una sola respuesta para varias situaciones, de modo que la ruta no sirva para sondear otros tenants: la grabación pertenece a otro tenant, su audio fue purgado (la purga borra con él la transcripción), nunca fue elegible para una transcripción, o el id está mal formado. Una grabación bajo legal_hold conserva su transcripción.
Códigos de estado
| Código | Significado |
|---|---|
200 | La transcripción. |
401 | Credenciales ausentes o inválidas. |
403 | El rol de quien llama no puede leer transcripciones. |
404 | No hay transcripción para esta grabación en este tenant. |
503 | No se pudo registrar el evento de lectura, así que no se devolvió texto. |
Si el proveedor de transcripción no está disponible
La transcripción corre después de la llamada, fuera de la ruta de la petición, y falla cerrado:
- El gasto en transcripción de pago tiene un tope mensual por el freno de gasto de la plataforma. Un proveedor de pago sólo se usa cuando el tope es positivo; con un tope de 0 el trabajo se detiene sin gastar y no recurre en silencio a un motor local.
- Si el motor de transcripción rechaza el proveedor solicitado, el trabajo termina sin guardar nada y se libera el gasto reservado. Si responde con un proveedor distinto del reservado, core tampoco guarda esa respuesta.
- La elegibilidad se decide antes de cualquier gasto: el plan debe incluirla (Scale y Enterprise), la grabación debe estar disponible, dentro de los límites de duración, atendida por una persona y no ser una llamada de IA, y no tener una transcripción previa. Lo que no califica no se reserva, no se factura y no se vuelve a encolar.
El efecto visible para un cliente de la API es el mismo en todos los casos: la grabación no tiene transcripción y GET .../transcript responde 404. La respuesta no tiene un estado «pendiente».
Redacción de audio
La redacción nunca edita la grabación almacenada. La plataforma conserva el original intacto, con su retención y su retención legal, y construye una copia derivada con los tramos sensibles silenciados o sustituidos por un tono. Esa copia es la vista por defecto.
Qué se redacta
Un tramo es un rango de tiempo más una categoría de una lista cerrada. La petición no lleva ningún valor, y la redacción almacenada tampoco guarda ninguno:
| Campo | Tipo | Notas |
|---|---|---|
start_ms | integer, mín. 0 | Inicio del tramo. |
end_ms | integer | Fin del tramo. |
category | enum | card, ssn, bank_account, pin. |
Sólo se pueden redactar grabaciones WAV. Cualquier otro formato es 422 unsupported_codec.
Pedir una redacción
http
POST /v1/recordings/{id}/redactionjson
{
"method": "silence",
"segments": [
{ "start_ms": 12400, "end_ms": 15800, "category": "card" }
]
}segmentses obligatorio: de 1 a 500 entradas.methodessilence(por defecto) obeep.- Los tramos se unen con los ya almacenados, y la copia se reconstruye a partir del original. La llamada es idempotente;
versionsube sólo cuando se añadió algo nuevo. - Quien llama necesita el mismo rol que para el original (ver abajo).
El cuerpo 200 es { "redaction": { ... } } con recording_id, method, segments, redacted_ms, version, codec, requested_by, created_at y updated_at. Códigos de estado: 400, 401, 403, 404, 409, 422.
Leer la redacción
http
GET /v1/recordings/{id}/redactionDevuelve el mismo objeto redaction: tiempos, categorías, método y versión, nunca los valores y nunca llaves del almacén de objetos. 404 cuando la grabación no tiene redacción. Códigos de estado: 200, 401, 403, 404.
Reproducción y el original
POST /v1/recordings/{id}/playback-url devuelve la copia redactada cuando la grabación la tiene. Si la fila de redacción existe pero su copia ya no está (purgada), la ruta no recurre al original.
El audio sin redactar tiene su propia puerta:
http
POST /v1/recordings/{id}/original-urljson
{ "reason": "Dispute 2026-0417: customer denies the payment promise" }- Quién puede pedirlo: una llave
dd_del tenant o un login del dashboard con el rolaccount_admin. Cualquier otro recibe403 role_forbidden. reasones obligatorio, no vacío, de como máximo 500 caracteres.- El actor y el motivo se escriben en un registro de acceso de sólo-anexar antes de devolver la URL; si la entrada del registro no se puede escribir, no se emite ninguna URL.
- El cuerpo
200es una URL firmada de corta vida (url,expires_at). Códigos de estado:400,401,403,404,409.
Aviso de monitoreo de pantalla y acuses de recibo
Antes de que se pueda observar la pantalla de un asiento, el asiento acusa recibo de un aviso versionado. Esta sección es sólo el contrato de las rutas. El texto legal del aviso no se publica en esta guía; ver Lo que no cubre esta guía.
Leer el aviso vigente
http
GET /v1/screen-monitor/noticeEl cuerpo 200 es { "notice": { ... } } con version, lang, title, body, text_sha256 (SHA-256 en hexadecimal de body en UTF-8), effective_at y published_at. El aviso es un catálogo global de sólo-anexar: publicar una version más nueva invalida todos los acuses de las anteriores sin tocarlas.
- Lo alcanzan una llave
dd_y un loginddu_; un token de widgetddw_recibe403. 404 not_foundcuando la banderascreen_monitor_enableddel tenant está apagada, y también cuando aún no se ha publicado ninguna versión.- Códigos de estado:
200,401,403,404.
Acusar recibo desde el propio asiento
http
POST /v1/screen-monitor/acksjson
{ "notice_version": 3, "scope": "live_monitoring" }| Campo | Tipo | Notas |
|---|---|---|
notice_version | integer, mín. 1 | Obligatorio. Debe ser la versión vigente. |
scope | enum | Obligatorio: live_monitoring o screen_recording. |
agent_id | uuid | Opcional. Si está presente, debe ser el asiento de quien llama. |
- Los dos alcances se acusan por separado: aceptar el monitoreo en vivo no autoriza la grabación, y una versión nueva del aviso invalida ambos.
- Sólo puede acusar un login
ddu_con un asiento vinculado. Una llavedd_recibe403 forbidden; unddu_sin asiento activo recibe403 seat_not_linked; unagent_idque no es el asiento de quien llama recibe403 forbidden. - El asiento sale del bearer, nunca del cuerpo.
- Repetir la misma versión y alcance devuelve el primer acuse (
200, sin fila nueva). El libro es de sólo-anexar. 404 not_foundmientrasscreen_monitor_enabledesté apagada. Con la bandera activa:409 no_notice_publishedcuando no existe ninguna versión,409 stale_noticecuandonotice_versionno es la vigente.- El cuerpo
200es{ "ack": { ... } }conid,agent_id,notice_version,scope,text_sha256(copiado del aviso que vio el asiento),channel(dashboard_checkin) yacked_at. Códigos de estado:200,400,401,403,404,409.
Listar los acuses
http
GET /v1/screen-monitor/acks?limit=50&cursor=...&agent_id=...Del más reciente al más antiguo, paginado por cursor: limit de 1 a 200 (por defecto 50; un valor mayor se recorta, no se rechaza), cursor es el next_cursor de la página anterior y agent_id filtra a un solo asiento. El cuerpo es { "acks": [...], "next_cursor": ... } y next_cursor es null en la última página.
Sólo se permite a un asiento cuyo rol de plantilla sea supervisor o admin (403 role_forbidden en otro caso; una llave dd_ y un ddu_ sin asiento reciben 403). No depende de la bandera screen_monitor_enabled: es el registro de lo que se aceptó, y apagar la función ni lo borra ni lo oculta. Nada cruza entre tenants. Códigos de estado: 200, 400, 401, 403.
Grabación de pantalla
El navegador del agente captura su propia pantalla y la sube a core en trozos. Todo esto está detrás de la bandera del tenant screen_monitor_enabled (ver la nota bajo Disponibilidad) y detrás de un acuse aparte.
Consentimiento: el acuse screen_recording
Grabar necesita un acuse vigente con scope: "screen_recording" (ver Acusar recibo desde el propio asiento). Es independiente de live_monitoring: aceptar uno nunca autoriza el otro, y una versión nueva del aviso invalida ambos. GET /v1/me lo reporta por alcance en screen_monitor.scopes.<scope>.ack_current (ScreenMonitorStatus); un alcance está autorizado sólo cuando enabled es true y ack_current es true.
Subir un trozo
http
PUT /v1/screen-recordings/{id}/chunks/{n}?call_uuid=...
Content-Type: video/webmides un UUID que elige el cliente, uno por llamada, de modo que un reintento sea idempotente.nes el número de trozo, de 0 a 1 000 000.call_uuides obligatorio (de 1 a 128 caracteres) y debe ser una llamada del asiento de quien llama.- El cuerpo es
video/webmovideo/mp4, de como máximo 4 000 000 de bytes por trozo (413por encima de eso,415para otro tipo). - Sólo puede subir un login
ddu_con un asiento vinculado; una llavedd_, unddu_sin asiento y un tokenddw_reciben403. - El asiento, la llamada y la retención vienen de core, no del cliente. El trozo se sella con la llave del tenant (AES-256-GCM) antes de llegar al almacén, ligado a
idyn. - Sin un acuse
screen_recordingvigente la respuesta es403 screen_recording_not_acknowledged, no se guarda nada y se registra un hueconot_armed.
201 significa que el trozo se selló y se guardó; 200 significa que esos mismos bytes ya estaban guardados (status: duplicate). El cuerpo es { "chunk": { ... } } con recording_id, seq, bytes (tamaño antes de sellar), status (stored o duplicate) y retained_until (una fecha, heredada de la grabación de voz de la misma llamada).
| Código | Significado |
|---|---|
404 | Bandera apagada, o call_uuid desconocido o de la llamada de otro agente (misma respuesta para ambos). |
409 chunk_conflict | Ese n ya contiene otros bytes. |
409 recording_conflict | La llamada ya tiene una grabación bajo otro id, o id pertenece a otra llamada. |
410 | La grabación de pantalla se purgó al terminar su retención. |
503 | El almacén no tomó el trozo; reinténtalo. Se registra un hueco upload_failed. |
Otros códigos: 400, 401, 403, 413, 415.
Declarar un hueco
http
POST /v1/screen-recordings/gapsjson
{ "call_uuid": "...", "reason": "agent_stopped", "note": "capture ended by the agent" }reason es not_armed, agent_stopped o upload_failed; note es opcional, de como máximo 500 caracteres. Es idempotente por llamada, motivo y origen, no necesita acuse (un hueco es evidencia, no contenido) y aplica las mismas reglas de asiento y de llamada que la subida. El cuerpo 200 es { "gap": { "call_uuid", "reason", "source" } } con source siempre agent para esta ruta; core escribe su propio hueco not_armed cuando llega un trozo sin acuse.
Retención, lecturas y purga
retained_until en la respuesta del trozo se hereda de la grabación de voz de la misma llamada, y subir a una grabación purgada al terminar su retención da 410. La spec vendorizada de v0.105.0 no publica una ruta para leer de vuelta una grabación de pantalla, ni una URL firmada para ella, ni una ruta de purga, así que esta guía no las describe.
Sesiones de monitoreo de pantalla en vivo
Un supervisor puede ver en vivo la pantalla de un agente. Core no ve el video: valida, escribe una fila en el libro y señala a ambos navegadores, que negocian el medio de par a par.
- Armar.
PUT /v1/screen-monitor/armedcon{ "armed": true, "tab_id": "..." }(tab_idde 1 a 64 caracteres) desde el propio asiento del agente una vez que su dashboard tiene una captura. Armar exige que el asiento esté registrado (409 not_checked_in) y tenga un acuselive_monitoringvigente (409 no_ack). Una pestaña por asiento: armar con otrotab_iddesplaza a la anterior. El estado armado vive en el proceso, así que un reinicio desarma a todos. - Abrir.
POST /v1/screen-monitor/sessionscon{ "agent_id": "..." }desde un asientosupervisoroadmin.201devuelve{ "session": { ... } }conresult: "started". Todo resultado escribe una fila en el libro, también los rechazados. Códigos409:no_ack,not_armed,agent_busy,agent_unreachable. Un agente desconocido, un agente de otro tenant y un agente inactivo dan el mismo404. - Señalizar.
POST /v1/screen-monitor/sessions/{id}/signalconkind(offer,answerocandidate) ypayload.offersólo desde el agente,answersólo desde el supervisor. El payload se reenvía y nunca se guarda; como máximo 16 384 bytes (413 payload_too_large) y 60 mensajes por 10 segundos por sesión (429 rate_limitedconRetry-After).202devuelvedelivered,falsecuando la otra parte no tenía un socket vivo. Una sesión cerrada es409 session_closed. - Terminar.
POST /v1/screen-monitor/sessions/{id}/end, cuerpo opcional{ "reason": "peer_failed" }. Cualquiera de las partes puede llamarlo, es idempotente y funciona con la bandera apagada. El sistema también cierra una sesión viva al hacer checkout, al perder el socket, al caducar la credencial, o cuando dejan de cumplirse la bandera, el rol del supervisor o el acuse del agente.
GET /v1/screen-monitor/sessions lista el libro (asiento supervisor o admin, del más reciente al más antiguo, limit de 1 a 200 con 50 por defecto, cursor, agent_id, y result en started, ended, failed, refused). Una fila (ScreenMonitorSession) tiene id, supervisor_user_id, supervisor_id, agent_id, notice_version, result, reason (flag_off, role, agent_not_found, no_ack, not_armed, agent_busy, agent_stopped, credential_lapsed, checkout, peer_failed o null), started_at y ended_at. Nunca lleva payload de señalización y no depende de la bandera.
Quién puede cambiar la configuración de compliance
Escribir la configuración de compliance del tenant exige un administrador: una clave dd_ o un inicio de sesión account_admin (core#2438). Un inicio de sesión member recibe 403 role_forbidden en toda escritura. En el entorno de esta guía son:
PUT /v1/recordings/{id}/legal-hold, para poner o levantar una retención legal.PUT /v1/recording-policy, la política de grabación del tenant.
La lectura no cambia: GET /v1/recording-policy y los metadatos de la propia grabación, donde se ve la retención, siguen abiertos a los mismos lectores que antes. La misma regla cubre la divulgación de voz artificial, las reglas de reintento y la pausa y reanudación de la IA; esas rutas se describen en la referencia de API generada, no en esta guía.
Lo que no cubre esta guía
La redacción de datos personales en la grabación de pantalla (core#2017) no está publicada en la spec de v0.105.0, así que esta guía no afirma nada sobre ella.
El texto legal del aviso de monitoreo y la matriz legal jurisdicción por jurisdicción no se publican en esta guía. Esperan la decisión que sigue macro-tasks#415, y el Roadmap los lista como no servidos. Las rutas de arriba sólo transportan el texto de aviso que publique la plataforma; no afirman nada sobre lo que exige la ley.
Cómo se detectan los tramos no forma parte de las rutas de redacción: el endpoint recibe tramos, no los encuentra. La redacción de audio Opus no está servida; sólo WAV, como indica el 422 de arriba.