Skip to content

Widget del agente (incrustación) ​

English version: Agent widget (embed).

El widget del agente es un <iframe> listo para incrustar que pone una superficie de llamadas con sesión iniciada dentro de tu propio CRM o aplicación web. Cada agente inicia sesión con su propio correo y contraseña dentro del marco — tu aplicación nunca maneja una clave de API de Dialer Digital ni ninguna credencial compartida — y cada acción de llamada que realiza es atribuible a esa persona, en tiempo real.

Dentro del marco el agente ve sus llamadas en vivo y puede actuar sobre ellas (poner en espera / quitar la espera, silenciar / quitar el silencio, colgar). La misma sesión ddw_ que alimenta el marco también está autorizada — a nivel de API — para registrar disposiciones, hacer marcaciones con vista previa, marcar un número tecleado y programar devoluciones de llamada (mira Qué puede llamar el token de sesión); la interfaz de referencia del marco expone hoy el control de llamadas y, una vez publicado, un teclado de marcación para teclear un número; las demás superficies se siguen en la Hoja de ruta. El marco se mantiene al día con el flujo de eventos en vivo de la plataforma; tu página puede suscribirse a un pequeño flujo de eventos postMessage de presencia y estado de llamada.

Disponibilidad: el carril del widget sale apagado

Todo el carril del widget está detrás de un interruptor del lado del servidor (widget_auth_enabled) que es una decisión de go/no-go del operador de la plataforma por entorno. Mientras el carril está apagado, POST /v1/auth/agent-login y GET /v1/auth/widget-config responden 404, y la página del widget sirve Content-Security-Policy: frame-ancestors 'none' — no puede incrustarse en ningún sitio. Todo lo que sigue documenta el contrato tal como está construido; confirma con tu operador que el carril está habilitado en tu entorno antes de integrar.

Audio en el marco: posible, y APAGADO salvo que tu operador lo arme

El widget era sólo una superficie de control, y ahora también puede llevar la voz: cuando el operador de la plataforma ha armado el carril de voz en el marco para tu entorno, el marco registra un softphone propio con una credencial SIP generada para esa única sesión de agente y la llamada suena dentro del marco.

Dos cosas que debes saber antes de planificar en torno a esto:

  • Es por entorno, y viene apagado por defecto. Donde el carril no está armado no se rompe ni cambia nada: la llamada sigue sonando en el dispositivo del puesto del agente (el softphone de navegador del dashboard o un dispositivo SIP externo) y el marco lo dice. Confírmalo con tu operador.
  • Tu <iframe> debe conceder el micrófono, y debería conceder la reproducción automática (allow="microphone; autoplay", mira Incrustar el marco). Los dos atributos cubren las dos direcciones y fallan de forma distinta: sin el micrófono, el agente oye la llamada y no se le oye; sin la reproducción automática, un navegador basado en Chromium puede negarse a reproducir el audio entrante, y entonces el agente no oye — algo que nadie al otro lado puede decirte, porque desde allí todo suena normal.

El marco también tiene un teclado de marcación para teclear un número — publicado en core v0.86.0 (publicado como v0.86.0-prod-0.1): mira Marcar un número tecleado (teclado de marcación) para saber qué hace y la Hoja de ruta para su estado.

Cómo encaja todo ​

your CRM page (https://crm.example.com)
└── <iframe src="https://app.usa.dialerdigital.com/widget?tenant=<workspace-id>">
    │     the widget frame — served from the Dialer Digital dashboard host,
    │     with a per-tenant CSP (frame-ancestors = YOUR allowlisted origins)
    │
    ├── talks DIRECTLY to the core API, cross-origin (fetch + WebSocket)
    └── postMessage bridge to your page (presence / call-state events only)

Producción sirve el marco desde https://app.usa.dialerdigital.com. Los niveles que no son de producción siguen app.<tier>.usa.dialerdigital.dev (app.dev.usa.dialerdigital.dev, app.staging.usa.dialerdigital.dev); cambia el host si integras contra uno de ellos.

El marco se sirve desde el host del dashboard de Dialer Digital y llama directamente a la API de core (entre orígenes a propósito: así el navegador adjunta una cabecera Origin en cada petición, que el carril del token del widget verifica). Tu página y el marco están separados por la frontera de origen del iframe del navegador: los scripts de tu página no pueden llegar al DOM del marco, a su heap de JS ni al token de sesión del agente.

Dos listas de permitidos — no las confundas ​

Intervienen dos comprobaciones de origen distintas, y divergen a propósito:

Lista de permitidosQué contieneQué controlaQuién la gestiona
Lista de incrustación (widget_origins, por tenant)Los orígenes de tu CRM, p. ej. https://crm.example.comQuién puede enmarcar el widget (CSP frame-ancestors) y con quién hablará el marco por postMessageTú, mediante /v1/widget_origins con la clave de API de tu tenant
Orígenes del host del widget (constante de despliegue)El host de Dialer Digital que sirve el marcoAutenticación: el inicio de sesión del agente y toda petición ddw_ a la API deben originarse en la página del host del widgetEl operador de la plataforma

Una consecuencia práctica: la superficie de integración soportada de tu lado es el iframe más el puente postMessage. Una página de navegador cuyo Origin no es el host del widget falla con cierre por defecto en el inicio de sesión y en las peticiones ddw_. Origin es una barrera de origen del navegador, no una garantía frente a un cliente HTTP arbitrario que pueda construir cabeceras, así que no montes un relé de tokens en el backend apoyándote en él.

Click-to-call para tu sitio web (ddc_) ​

El carril de click-to-call permite que un visitante pida a tu equipo que llame a un número desde tu sitio web. Usa una clave pública de sitio aparte con el prefijo ddc_. La clave es publicable: identifica el sitio de click-to-call configurado, pero no es una contraseña y no autentica al visitante.

Configuración y flujo de la petición ​

El administrador de tu tenant crea un sitio de click-to-call, elige su campaña de destino y sus límites, y recibe la clave ddc_ una sola vez. No la trates como una clave de API de tenant. Revocar el sitio invalida la clave. La plataforma guarda sólo un hash SHA-256 de la clave completa, prefijo incluido.

La clave la usa el marco de click-to-call que, igual que el widget del agente, se sirve desde el host del widget de Dialer Digital. Una petición ddc_ sólo se acepta cuando su Origin es un origen del host del widget; una enviada desde tu propia página recibe 403. Tu sitio web incrusta el marco, y tus widget_origins deciden dónde puede incrustarse (frame-ancestors). El marco y su fragmento de carga aún no están publicados: hasta que lo estén, no hay una forma soportada de enviar una petición desde tu sitio web.

El carril público expone exactamente dos rutas:

MétodoRutaPropósito
GET/v1/public/call-request-configLeer la configuración pública del sitio
POST/v1/public/call-requestsEnviar una petición de llamada inmediata o programada

El cuerpo de la petición admite sólo el número de teléfono E.164 normalizado, mode (immediate o scheduled), la hora programada cuando el modo es scheduled, la versión, el hash SHA-256 y el estado marcado del texto de consentimiento que muestra tu sitio, y un captcha_token opcional. El servidor aporta el sitio, el tenant y la hora de recepción; nunca aceptes esos valores de una entrada del navegador. El servidor también registra las cabeceras Origin y User-Agent de la petición. Cualquier otro campo del cuerpo es un 400 bad_request.

La respuesta al envío es deliberadamente uniforme y asíncrona: 202 significa sólo que la petición se aceptó para su procesamiento. No significa que vaya a hacerse una llamada. La plataforma resuelve después el número a una cuenta, aplica las barreras de cumplimiento y la ventana de llamadas configuradas, y puede registrar un rechazo, un bloqueo, una caducidad o un despacho en su lugar.

El carril está apagado salvo que estén encendidos a la vez el techo de la plataforma y el ajuste click_to_call_enabled de tu tenant. Mientras está apagado, las dos rutas públicas devuelven la respuesta 404 normal y no escriben ninguna petición. Pregunta a tu operador de plataforma antes de publicar el fragmento.

Idioma del marco: lang= ​

El marco de click-to-call acepta un parámetro de consulta opcional lang en la URL del <iframe>: lang=es o lang=en. Cualquier otro valor se ignora. Sin él, el marco usa el idioma por defecto de tu celda.

La URL del marco del fragmento ya lleva una cadena de consulta (?tenant=…), así que añade el parámetro con &, justo después de tenant=… y antes de cualquier fragmento #. Un segundo ? corrompería el valor de tenant.

html
<iframe src="https://app.usa.dialerdigital.com/…?tenant=YOUR-WORKSPACE-ID&lang=es"></iframe>

lang sólo elige el idioma del texto del marco. No autentica al visitante, no cambia para qué sitio o tenant es la petición ni altera el texto de consentimiento que muestra y reporta tu propia página.

Modelo de amenazas de la clave pública y de Origin ​

FronteraQué haceQué no hace
Clave de sitio ddc_Selecciona un sitio de click-to-call no revocado y la configuración de su tenantNo identifica al visitante, no prueba el control del teléfono ni actúa como clave de API de tenant
OriginDebe ser el origen del host del widget de Dialer Digital del marco; otro origen de navegador, incluida tu página, recibe 403No es un secreto, ni una prueba de identidad, ni consentimiento, ni un control frente a un cliente que no es un navegador y falsifica cabeceras
Lista de rutas públicasLimita la clave a la configuración y al envío de peticiones de llamadaNo da acceso a rutas de tenant, agente, reportes ni widget
Respuesta 202Confirma la recepción para su procesamiento asíncronoNo confirma el emparejamiento con una cuenta, el cumplimiento ni una llamada hecha

Da por supuesto que cualquiera que pueda cargar el marco de click-to-call puede copiar la clave ddc_ y reproducir peticiones. No pongas decisiones de confianza, identidad del visitante ni prueba de consentimiento en la clave ni en Origin. Protege contra XSS la página que incrusta el marco, no registres el número de teléfono ni la clave sin necesidad y mantén la clave fuera de las credenciales de administración del tenant y de los secretos del servidor. La v1 no tiene captcha: la ruta de la petición tiene un punto de enganche para uno, pero no se entrega ningún proveedor, así que lo que acota el abuso son los topes durables por tenant, por número y por sitio.

Los topes durables son independientes del limitador de tasa en memoria: las ventanas de tenant y de número cubren las 24 horas anteriores, y la ventana del sitio cubre la hora anterior. Si los datos de los topes no pueden leerse, la petición se rechaza en lugar de admitirse. Un rechazo por tope no es prueba de que el número coincidiera con una cuenta.

Lo que este producto no afirma ​

El carril de click-to-call no afirma que ninguno de estos hechos sea consentimiento de la persona cuyo número se introdujo:

  • publicar o poseer una clave ddc_;
  • enviar una petición con un Origin de apariencia permitida;
  • marcar una casilla de consentimiento o enviar la versión y el hash de un texto de consentimiento;
  • recibir 202, o una devolución de llamada, un despacho o una llamada exitosa posteriores.

La petición registra lo que el visitante declaró y qué versión del texto reportó tu sitio. No establece quién controla el teléfono, no crea un registro de consents y no sustituye la configuración del tenant ni las barreras de cumplimiento de la plataforma. Configura y evalúa el consentimiento exigido, o cualquier otra base para llamar, en tu flujo de cumplimiento habitual.

Guía de seguridad y modelo de amenazas ​

Trata el widget como un cliente de navegador, no como un servicio privado incrustado en tu aplicación. El valor ddw_ es una credencial pública de navegador: tiene que ser utilizable por el marco, así que un atacante que pueda inspeccionar el entorno de ejecución del marco o el tráfico del navegador puede obtenerla. El diseño limita lo que esa credencial puede hacer y durante cuánto tiempo sigue siendo útil; no hace secreto el token.

Qué significa cada frontera ​

FronteraQué prueba o limitaQué no prueba
Id de espacio de trabajo ?tenant=Selecciona el espacio de trabajo cuya configuración de widget se pideNo autentica a una persona ni da acceso por sí solo
Lista de incrustación (widget_origins)Qué orígenes exactos de CRM pueden enmarcar el widget y recibir sus metadatos de postMessageNo autoriza el inicio de sesión del agente ni convierte al CRM en un proveedor de identidad de confianza
Lista del host del widget (widget_host_origins)Qué origen de documento de widget desplegado puede generar o presentar un token ddw_No identifica al tenant, al agente ni a quien inició una acción
Origin de una peticiónUna barrera de origen de navegador para el carril del widget; una petición de otro origen falla con cierre por defectoNo es un secreto, ni una identidad de usuario, ni prueba de intención. No lo uses como consentimiento
Token ddw_Una credencial de vida corta, acotada al agente, para la lista cerrada de rutasNo es una clave de API de tenant, ni una credencial de administración, ni el consentimiento de la persona llamada

Las dos listas de orígenes son distintas a propósito. El origen de tu CRM se usa para incrustar y como interlocutor de los mensajes; el host del widget de Dialer Digital se usa para la autenticación. El id público del espacio de trabajo selecciona el tenant, mientras que la credencial y las comprobaciones de tenant en el servidor autorizan la sesión del agente. Nunca sustituyas esta separación por un proxy del mismo origen ni copiando el host del widget en la lista de incrustación de cada tenant.

Amenazas que el widget está diseñado para contener ​

AmenazaContenciónResponsabilidad del integrador
Una página de un origen que no está en la lista intenta enmarcar el widgetLa respuesta usa una política frame-ancestors con cierre por defectoRegistra cada origen https real, incluidos cada subdominio y cada puerto no predeterminado
Un navegador reproduce un token robado desde el backend de un CRM o desde otra páginaUna petición de navegador con un Origin de host de widget incorrecto falla; además, el token es de vida corta, revocable y limitado a ciertas rutasNo registres, copies ni hagas pasar por proxy el token, ni lo pongas en URLs, HTML, localStorage ni sessionStorage. Origin no es un control frente a un cliente que no es un navegador y puede falsificar cabeceras
La página que incrusta intenta leer el DOM o el token del marcoLa frontera entre orígenes del iframe mantiene separados el DOM y la memoria JavaScript del marcoMantén la integración en el iframe más los mensajes postMessage documentados
Una página inesperada envía datos por postMessageEl marco sólo acepta mensajes de un origen de incrustación habilitado; los mensajes salientes usan el origen exacto fijadoValida event.origin antes de manejar mensajes y nunca uses * como targetOrigin
Una página de CRM comprometida y presente en la lista observa los eventos del widgetEl puente expone sólo metadatos de presencia y estado de llamada, nunca credenciales ni números de teléfonoTrata el XSS en el CRM como un incidente de seguridad real; protege la página de la lista y sus dependencias
Un atacante sondea la existencia de un tenant mediante la configuración pública del widgetLos ids de espacio de trabajo desconocidos devuelven una lista de orígenes vacía en lugar de detalles del tenantTrata el id del espacio de trabajo como configuración publicable, no como un secreto

Estos controles son defensa en profundidad. No protegen frente a un despliegue del host del widget comprometido, un navegador comprometido, extensiones de navegador maliciosas ni una vulnerabilidad XSS en el CRM de la lista. Tampoco convierten una cabecera de navegador en una afirmación de identidad: los clientes que no son navegadores pueden construir cabeceras, y un origen legítimo puede seguir estando controlado por código hostil.

Lo que el producto no afirma ​

El widget no afirma que ninguno de los siguientes hechos sea consentimiento de la persona a la que se llama:

  • incrustar el marco o poner un origen de CRM en la lista de permitidos;
  • poseer el id público del espacio de trabajo o presentar una cabecera Origin válida;
  • iniciar sesión de un agente en el marco o presentar un token ddw_ válido;
  • pulsar un control del widget, teclear un número o enviar datos por postMessage.

La petición del widget sigue sujeta a las barreras de llamada y de cumplimiento de la plataforma. Una autenticación correcta, un origen de la lista y una ruta permitida establecen únicamente las condiciones técnicas de la petición del agente con sesión iniciada; no establecen consentimiento legal, no sustituyen la configuración del tenant ni hacen una determinación legal. Configura y evalúa el consentimiento exigido, o cualquier otra base para llamar, en el flujo de cumplimiento habitual del tenant.

Requisitos previos ​

#RequisitoCómo
1Carril del widget habilitado en tu entornoGo/no-go del operador — pregunta a tu contacto de Dialer Digital.
2El origen de tu CRM en la lista de incrustación del tenantPOST /v1/widget_origins con la clave de API (dd_) de tu tenant — mira abajo.
3Credenciales de inicio de sesión del agente habilitadasPOST /v1/agents/{id}/credential fija/rota la contraseña de widget de un agente; DELETE la deshabilita (y revoca su sesión de widget en vivo). El agente debe estar active. Fijarla o quitarla exige una clave de tenant (dd_) o un inicio de sesión account_admin; un ddu_ sólo sobre su propio asiento. Un inicio de sesión member sobre el asiento de otro agente recibe 403 role_forbidden.
4Tu id de espacio de trabajo (UUID público del tenant)Se entrega en la incorporación. Es un identificador publicable: selecciona el espacio de trabajo, no concede nada por sí solo.

Gestionar la lista de incrustación: /v1/widget_origins ​

Se autentica con la clave de API de tu tenant (Authorization: Bearer dd_...):

MétodoRutaNotas
GET/v1/widget_originsLista todos los orígenes (habilitados + deshabilitados).
POST/v1/widget_originsCuerpo {"origin": "https://crm.example.com", "label": "..."}. 201 devuelve la forma canónica.
DELETE/v1/widget_origins/{id}Deshabilitación blanda: el origen sale de frame-ancestors/postMessage. Los orígenes nunca se borran. No bloquea el inicio de sesión del agente (ése depende del host del widget).
POST/v1/widget_origins/{id}/enableVolver a habilitar.

Añadir, deshabilitar y volver a habilitar un origen exigen una clave dd_ o un inicio de sesión account_admin del dashboard; un inicio de sesión member recibe 403 role_forbidden y no cambia nada. Listar no cambia. La regla va detrás del ajuste por cuenta telephony_config_admin_only (encendido por defecto, fijado por la plataforma); ver Quién puede escribir la telefonía.

Los orígenes se normalizan a una forma canónica estricta y se comparan exactamente en ejecución:

  • Sólo https:// — los orígenes http simples se rechazan.
  • Host en minúsculas; un puerto :443 por defecto se elimina; los puertos no predeterminados se conservan y deben coincidir.
  • Sólo un origen web, nada más: sin ruta, consulta, fragmento, información de usuario ni barra final.
  • Sin comodines (https://*.example.com no es válido: añade cada host), sin null, sólo nombres de host ASCII (los nombres de host internacionalizados se rechazan).

Un valor mal formado da 400; un duplicado da 409.

Incrustar el marco ​

html
<iframe
  id="dd-agent-widget"
  src="https://app.usa.dialerdigital.com/widget?tenant=0d4f4f9e-1f2a-4b53-9d3c-8a5e2f7b1c10"
  title="Dialer Digital agent widget"
  allow="microphone; autoplay"
  style="width: 360px; height: 520px; border: 0;"
></iframe>
  • ?tenant= es tu id de espacio de trabajo (?tenant_id= también funciona). Un valor ausente o mal formado muestra un error dentro del marco y, como la CSP de incrustación se calcula a partir de él, una página que no puede incrustarse.
  • Carga el widget directamente desde el host de Dialer Digital. No hagas proxy ni sirvas de nuevo la página desde tu propio dominio: las cabeceras de seguridad por tenant se estampan en cada respuesta (Cache-Control: no-store), y el modelo de autenticación depende de que el marco viva en el origen del host del widget.
  • Evita el atributo sandbox si puedes. Si tu plataforma te obliga a ponerlo, el marco necesita al menos allow-scripts allow-same-origin allow-forms para funcionar.
  • allow="autoplay" es lo que permite que el agente OIGA. Un navegador sólo reproduce audio por iniciativa propia en un documento con el que el usuario ha interactuado, y un marco de otro origen no hereda la interacción de tu página salvo que le delegues la reproducción automática. Medido en Chrome 153 y Chromium 151: con el clic del agente cayendo en TU página y nunca dentro del marco, el audio de la llamada es mudo sin este atributo y se oye con él; Firefox 153 no aplica esa restricción. No sustituye a un clic — ningún navegador reproduce audio en un marco que nadie ha tocado, con o sin el atributo — y hoy el inicio de sesión del agente dentro del marco es esa primera interacción. Concédelo de todos modos: no cuesta nada, y es lo que mantiene el marco audible el día en que el audio empiece sin un clic propio.
  • allow="microphone" es lo que permite que al agente se le OIGA. Un marco de otro origen no obtiene micrófono salvo que la página que lo incrusta se lo delegue (Permissions Policy), y la denegación es silenciosa desde tu lado: el agente oye la llamada y la otra parte no oye nada. Concédelo aunque la voz aún no esté armada en tu entorno — no cuesta nada mientras el marco no pide el micrófono, y es la línea que más fácil se olvida el día que se enciende el carril.
  • No hace falta conceder la cámara, y no se pide: el marco es sólo de voz.
  • Si tu plataforma te obliga a poner un atributo sandbox, ten en cuenta que sandbox y allow son independientes: conceder el micrófono sigue exigiendo allow="microphone", y lo mismo vale para autoplay.

Si el navegador se niega a renderizar el marco («refused to connect»), mira Errores comunes: casi siempre es la CSP de incrustación con cierre por defecto.

La sesión del agente (token ddw_) ​

Cuando un agente envía el formulario de inicio de sesión dentro del marco, el marco llama a POST /v1/auth/agent-login con {tenant_id, email, password} y recibe un token de navegador ddw_ — una credencial de vida corta, acotada al agente, distinta de las claves de API de tenant (dd_) y de las sesiones del dashboard (ddu_).

Propiedades de la sesión, tal como las impone el servidor:

  • Sólo en memoria. El token vive exclusivamente en la memoria JS del marco — nunca en localStorage, sessionStorage, cookies ni URLs. Recargar la página significa que el agente vuelve a iniciar sesión. Es deliberado: un XSS en la página que incrusta nunca puede leer la credencial.
  • De vida corta, deslizante. El token tiene una caducidad deslizante (por defecto 15 minutos) que el marco renueva automáticamente (POST /v1/widget_tokens/renew), hasta un tope absoluto de sesión fijado en el inicio de sesión (por defecto 8 horas; el motor de almacenamiento impone un techo duro de 12 horas). Renovar desliza la caducidad — nunca mueve el tope y nunca cambia el texto en claro del token. Los valores por defecto son configuración por despliegue.
  • Una sesión en vivo por agente. Generar una sesión nueva para un agente sustituye a cualquier sesión en vivo anterior.
  • Origen comprobado en cada petición. Cada llamada ddw_ a la API vuelve a verificar el Origin de la petición contra la lista del host del widget. Una reproducción desde un navegador de otro origen falla con cierre por defecto con 403; esta barrera de origen de navegador no sustituye a tratar el token como público ni a defenderse de clientes arbitrarios que puedan construir cabeceras.
  • Superficie de API con denegación por defecto. El token sólo llega a un conjunto enumerado de rutas (abajo) — nunca puede generar claves de API, gestionar tokens u orígenes de widget, ni tocar las superficies de supervisión, administración o reportes.
  • Revocable. Las sesiones terminan cuando: la caducidad deslizante vence o se alcanza el tope; la credencial del agente se rota o se deshabilita (/v1/agents/{id}/credential — la revocación es transaccional con el cambio de credencial, así que ninguna sesión la sobrevive); o un administrador del espacio de trabajo revoca el token (DELETE /v1/widget_tokens/{id} con una clave dd_). Cualquier 401 dentro del marco descarta la sesión y devuelve al agente al formulario de inicio de sesión.

Los fallos de inicio de sesión son deliberadamente poco informativos: un espacio de trabajo desconocido, un correo desconocido, una contraseña incorrecta o un agente con el inicio de sesión deshabilitado devuelven el mismo 401. Un espacio de trabajo suspendido devuelve 403 sólo después de que la contraseña se verificó. Los intentos de inicio de sesión tienen límite de tasa por correo (ventana fija, 429 rate_limited, sin cabecera Retry-After).

Qué puede llamar el token de sesión ​

La superficie ddw_ completa — todo lo demás es 403:

MétodoRutaPropósito
GET/v1/calls, /v1/calls/{id}Las llamadas en vivo/recientes del agente (acotadas por el servidor al agente con sesión iniciada). Cada llamada lleva debtor_name, el nombre de su cuenta (null cuando no hay ninguna o la llamada no tiene deuda); se añade sólo para las llamadas del propio agente y una clave dd_ no lo recibe.
POST/v1/calls/{uuid}/hold · unhold · mute · unmute · hangupControl de llamada — condicionado a la propiedad: sólo las llamadas del propio agente.
POST/v1/call_attempts/{id}/dispositionRegistrar una disposición tipificada.
POST/v1/campaigns/{id}/dialMarcación con vista previa.
POST/v1/dial/manualMarcar un número tecleado, resuelto a una de las cuentas del tenant — el agente sólo puede nombrarse a sí mismo. Publicado en core v0.86.0 (publicado como v0.86.0-prod-0.1); mira Marcar un número tecleado (teclado de marcación).
POST/v1/callbacksProgramar una devolución de llamada.
POST/v1/widget_tokens/renewDeslizar la caducidad de la sesión (con límite de tasa por token).
POST/v1/widget_tokens/introspectEl contexto propio de la sesión (agente, relojes, orígenes habilitados).

Marcar un número tecleado (teclado de marcación) ​

Publicado en core v0.86.0 (publicado como v0.86.0-prod-0.1)

POST /v1/dial/manual y el teclado de marcación de la interfaz de referencia están en el contrato publicado, así que el verbo está en la Referencia de la API. Confirma con tu operador que tu entorno ejecuta una compilación que lo incluye.

El teclado de marcación es una herramienta para el agente libre: el marco lo ofrece una vez que ha leído la lista de llamadas del agente y sólo mientras el agente no tiene una llamada en vivo. El agente teclea un número E.164 (+ y de 7 a 15 dígitos; nunca se adivina un código de país). El marco elimina espacios, guiones, puntos y paréntesis y comprueba el formato antes de que nada salga del navegador; el servidor lo vuelve a comprobar y no elimina nada, así que quien llame por API debe enviar la forma E.164 desnuda.

Un número tecleado no es una marcación libre. Core lo resuelve contra los teléfonos de las propias cuentas de tu tenant:

  • exactamente una cuenta tiene el número → la llamada pasa por el mismo pipeline que una marcación con vista previa (la campaña de la propia cuenta, el puesto del agente y las barreras de cumplimiento — consentimiento, DNC, la ventana de llamadas en hora local del destino, frecuencia de Reg F, reglas estatales), y se atribuye a esa cuenta;
  • ninguna cuenta, varias cuentas, una cuenta sin campaña por la que marcar, un número en cuarentena o un número mal formado → 422 con un error.code legible por máquina y no se hace ninguna llamada. Un número que no pertenece a ninguna cuenta no es marcable desde el widget.
json
POST /v1/dial/manual
{ "number": "+13125550100", "agent_id": "<the signed-in agent's id>" }
ResultadoSignificado
200 { "status": "dialing", "call_id": "…" }La llamada se hizo.
200 { "status": "blocked", "reason": "…" }Una barrera de cumplimiento rechazó la llamada — no es una llamada hecha, igual que una marcación con vista previa bloqueada.
422 invalid_number · no_account · ambiguous_account · no_campaign · number_quarantinedEl número no se resuelve a exactamente una cuenta marcable; no se marcó nada.
503 audit_unavailable · resolution_unavailableNo se hizo y es reintentable: no se pudo registrar la petición, o falló la búsqueda de la cuenta.
403El token nombra a otro agente, o a ninguno (un ddw_ sólo puede nombrar su propio agent_id).
409 · 429 · 404 · otros códigos 503Los resultados del verbo de marcación con vista previa, sin cambios (por ejemplo, la campaña no está en marcha, o registration_unverifiable).

Cada petición se escribe en un rastro de auditoría de sólo anexar — quién tecleó qué número, y cada motivo de bloqueo — antes de marcar nada; si ese registro no puede escribirse, la llamada no se hace. Dos límites para planificar: una cuenta a la que sólo se llega por una lista de deudas (sin campaña propia) responde 422 no_campaign, y la campaña debe estar en marcha con un modo iniciado por una persona, como para una marcación con vista previa.

El marco también mantiene un WebSocket con el flujo de eventos en vivo para refrescarse; esa conexión es interna al marco — tu página no interactúa con ella.

Hablar con el marco: el puente postMessage ​

El marco expone a la página que lo incrusta un flujo de eventos mínimo y versionado. Son sólo metadatos de presencia/ciclo de vida — los tokens, las credenciales y los números de teléfono nunca cruzan el puente.

DirecciónMensajeSignificado
host → marco{ "type": "dd:hello" }Apretón de manos. El primer dd:hello de un origen de la lista fija ese origen como único interlocutor del marco.
marco → host{ "type": "dd:ready", "v": 1 }Apretón de manos reconocido; vendrán eventos.
marco → host{ "type": "dd:session", "v": 1, "state": "signed_in" | "signed_out" }El agente inició sesión / la sesión terminó.
marco → host{ "type": "dd:call_state", "v": 1, "live": <number> }Cambió el número de llamadas en vivo del agente.

Comportamiento de seguridad en torno al que debes diseñar:

  • El marco procesa un mensaje sólo si event.origin está en la lista de incrustación habilitada del tenant. Cualquier otra cosa se descarta en silencio — nunca se responde, así que no hay un oráculo para sondear. Si tu origen no está en la lista, el puente simplemente está sordo.
  • Los eventos salientes se envían con tu origen exacto fijado como targetOrigin (nunca *); en cada carga del marco sólo se fija un interlocutor.
  • El marco obtiene su lista antes de empezar a escuchar. Un dd:hello enviado demasiado pronto se pierde — reintenta el hello hasta recibir dd:ready.
html
<iframe id="dd-agent-widget" src="https://app.usa.dialerdigital.com/widget?tenant=YOUR-WORKSPACE-ID"></iframe>
<script>
  const WIDGET_ORIGIN = 'https://app.usa.dialerdigital.com';
  const frame = document.getElementById('dd-agent-widget');

  window.addEventListener('message', (ev) => {
    if (ev.origin !== WIDGET_ORIGIN) return; // trust nothing else
    const msg = ev.data;
    switch (msg?.type) {
      case 'dd:ready':      clearInterval(hello); break;
      case 'dd:session':    console.log('agent is', msg.state); break;
      case 'dd:call_state': console.log('live calls:', msg.live); break;
    }
  });

  // Handshake: retry until the frame answers dd:ready.
  const hello = setInterval(() => {
    frame.contentWindow?.postMessage({ type: 'dd:hello' }, WIDGET_ORIGIN);
  }, 500);
</script>

Cabeceras de seguridad de la página del widget ​

Para los curiosos (no tienes que configurar nada de esto):

  • GET /widget?tenant=<uuid> se sirve con una Content-Security-Policy por tenant cuyo frame-ancestors son exactamente tus orígenes de incrustación habilitados, obtenidos de la misma fuente que usa el inicio de sesión del agente (GET /v1/auth/widget-config, pública, en caché hasta 60 s). La barrera de incrustación y la de ejecución nunca pueden divergir.
  • Cierre por defecto: sin parámetro de tenant, con uno mal formado, con una lista vacía, con el carril apagado o con la fuente de configuración inalcanzable, todo se reduce a frame-ancestors 'none' — una página que no puede incrustarse.
  • X-Frame-Options se elimina sólo para esta ruta (no puede expresar una lista de permitidos y anularía la CSP). connect-src se restringe al propio marco más el origen de la API de core y su gemelo WebSocket.
  • GET /v1/auth/widget-config?tenant_id=<uuid> devuelve {"origins": [...]} — tus orígenes de incrustación habilitados. Es pública por diseño (las mismas cadenas ya viajan dentro de cabeceras de respuesta) y responde origins: [] para ids desconocidos — no es un oráculo de existencia de tenants.

Errores comunes ​

SíntomaCausa
El navegador se niega a renderizar el iframe («refused to connect» / en blanco)La CSP de incrustación es frame-ancestors 'none': el carril del widget está apagado, falta el parámetro ?tenant= o está mal formado, tu origen no está en la lista habilitada, o no se pudo obtener la lista. Tras corregir la lista, espera hasta 60 s por la caché de cabeceras.
El marco muestra «Missing or malformed ?tenant=»El parámetro de consulta tenant falta o no es un UUID. Revisa el fragmento de incrustación.
El marco carga en https://crm.example.com pero no en http://... ni en otro subdominioLos orígenes se comparan exactamente (esquema + host + puerto). Los orígenes http nunca pueden estar en la lista; cada subdominio necesita su propia entrada.
404 desde /v1/auth/agent-login o /v1/auth/widget-configEl carril del widget está deshabilitado en este entorno (interruptor del operador).
401 unauthorized al iniciar sesiónCorreo/contraseña incorrectos, agente inactivo, inicio de sesión no habilitado para el agente o id de espacio de trabajo desconocido — indistinguibles por diseño. También se devuelve cuando el inicio de sesión se intenta desde cualquier lugar que no sea el marco del widget.
403 forbidden («tenant is suspended») al iniciar sesiónLas credenciales eran correctas, pero el espacio de trabajo/la cuenta está suspendido.
429 rate_limited al iniciar sesiónFreno de inicio de sesión por correo (ventana fija). Reintenta más tarde; no se pone cabecera Retry-After.
403 «origin not allowed for this token»Una petición ddw_ cuyo Origin no es el host del widget — típicamente un token reproducido fuera del marco. Por diseño.
403 «this token may not use that endpoint»La ruta queda fuera de la lista de permitidos con denegación por defecto de ddw_. Usa en su lugar una clave de API de tenant desde tu backend.
El agente vuelve al formulario de inicio de sesión a mitad de turnoLa sesión alcanzó su caducidad deslizante o su tope absoluto, la credencial se rotó/deshabilitó, o un administrador revocó el token. Volver a iniciar sesión es la recuperación prevista.
Los eventos postMessage nunca lleganTu origen no está en la lista habilitada; o enviaste dd:hello antes de que el marco estuviera escuchando (reintenta hasta dd:ready); o otra ventana ya se fijó como interlocutor en esta carga del marco.
400/422 al añadir un origenNo es un origen https canónico — comodín, ruta, http o un nombre de host no ASCII (el puerto :443 escrito explícitamente está bien: se elimina).

Véase también ​

  • Autenticación — las claves de API de tenant (dd_), la credencial que usa tu backend.
  • Eventos WebSocket — el piso en vivo que el marco consume internamente.
  • Hoja de ruta — audio de softphone en el marco y otras superficies diseñadas pero no construidas.

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.