Eventos (Relay)
Reacciona en el navegador cuando tus clientes interactúan con la cotización.
El Puente de Mensajes
El iframe de Cord Elements está diseñado para nunca aislar a tu sitio web de lo que ocurre dentro de él. Usando un puente bidireccional mediante window.postMessage, Cord hace “relay” (retransmisión) de los eventos de la tarjeta directamente a tu entorno window padre.
Esto es increíblemente poderoso para disparar píxeles de analítica (como Meta Pixel o Google Analytics), lanzar animaciones de confeti cuando se aprueba una propuesta, o redirigir al usuario a una página de “Gracias” tras un pago exitoso.
Escuchar Eventos
Puedes escuchar los eventos de forma nativa atando un addEventListener al nodo del DOM del elemento embebido: el div de embed.js o el propio <cord-quote>, que emiten los mismos nombres.
const contenedor = document.querySelector('[data-cord-token]');
contenedor.addEventListener('cord:approved', (evento) => {
console.log('¡La cotización fue aprobada!');
console.log('Aprobada por:', evento.detail.signed_by);
});
contenedor.addEventListener('cord:change', (evento) => {
if (evento.detail.paid) console.log('Cord registró el pago');
});
En TypeScript, addEventListener('cord:approved', …) ya trae el tipo de su detail. En Angular, la sintaxis (evento) de las plantillas lee los dos puntos como un destino (como en window:resize): escucha con addEventListener sobre el elemento.
Un nombre por evento en cada superficie
| Qué pasa | Web Component y embed.js |
React | Vue |
|---|---|---|---|
| Terminó de cargar | cord:ready |
onReady |
@ready |
| Cambió el estado en vivo | cord:change |
onChange |
@change |
| No se pudo mostrar o ya venció | cord:error |
onError |
@error |
| El documento ya se ve | cord:viewed |
onViewed |
@viewed |
| El cliente aprobó | cord:approved |
onApproved |
@approved |
| El cliente firmó | cord:signed |
onSigned |
@signed |
| El cliente rechazó | cord:rejected |
onRejected |
@rejected |
| Comentario o contraoferta | cord:message |
onMessage |
@message |
| Comentario en una partida | cord:item_comment |
onItemComment |
@item-comment |
| Inició el pago | cord:pay |
onPay |
@pay |
| El vendedor cambió la propuesta | cord:updated |
onUpdated |
@updated |
| Cambió el estado del documento | cord:status_changed |
onStatusChanged |
@status-changed |
| La factura quedó pagada | cord:paid |
onPaid |
@paid |
| Cambió la altura | cord:resize |
onEvent |
@event |
| Cualquiera de los anteriores | — | onEvent |
@event |
Hasta la 2.2 el Web Component emitía los nombres sin prefijo (approved, item_comment, statechange…). Siguen llegando como alias hasta la 3.0, salvo message: salía como un evento message que alcanzaba cada window.addEventListener('message') de tu página (el chat de soporte, tu analítica) sin data ni origin. Escucha cord:message. En React, onStateChange es alias de onChange; en Vue, @state lo es de @change.
Diccionario de Eventos
Todos los eventos tienen el prefijo cord: para evitar colisiones de nombres con otras librerías. Cada detail trae solo los campos que se listan con su evento: ninguno trae el token (ya lo tiene tu página) y el folio llega en cord:ready y cord:paid.
Ciclo de vida del componente
cord:ready: Disparado en el momento exacto en que el documento termina de renderizarse dentro del iframe, con su altura final y las fuentes tipográficas ya cargadas. Traestatus,folio,monedaytotal, para que tu página dibuje su propio resumen sin otra llamada, ylivemode, que esfalseen un documento de prueba (el que lleva la franja de prueba). En la factura trae tambiénsaldo.cord:change: El estado en vivo cambió. Eldetailes el estado completo (ready,status,total,approved,paid,invoiced,error…), el mismo de la propiedadstate; sus campos están en Web Components y Vue.cord:error: El documento no se pudo mostrar o ya no se puede aprobar. Eldetailtrae elcode; los cuatro códigos están en Errores de carga, más abajo.cord:resize: La altura del contenido cambió. El SDK ya la aplica al iframe; eldetailtraeheighten píxeles, por si tu página necesita reaccionar.cord:viewed: Se dispara junto concord:ready, en cuanto el cotizador termina de renderizarse dentro del iframe — es la señal de que el contenido ya es visible para tu página anfitriona. Sudetailva vacío. El registro oficial en la base de datos de que el cliente vio la cotización corre por un mecanismo interno independiente, que exige que la pestaña esté realmente visible (para no contar como “vista” al propio vendedor previsualizando su link); no asumas que ambos ocurren en el mismo instante.
Interacción comercial
cord:approved: Emitido en el instante en que el usuario aprueba los términos de la propuesta comercial. Devuelve en eldetaildatos útiles como el hash de validación y quién firmó (signed_by).cord:signed: Se dispara en el MISMO instante quecord:approved, con el mismodetail(signed_by,hash) — en Cord, aprobar y firmar son una sola acción del cliente, no dos pasos separados en el tiempo. Escucha el que te resulte más claro semánticamente; no esperes a que “llegue después” del otro.cord:pay: Emitido cuando el usuario hace clic en el botón para iniciar su flujo de pago en línea (tarjeta, transferencia o los demás métodos que el negocio tenga habilitados). Eldetailtrae laurlabsoluta de la página de pago. Cord ya la abre en una ventana propia (el pago nunca corre dentro del iframe): no navegues tu página a esa URL, úsala solo para tu analítica.cord:rejected: Disparado si el cliente marca explícitamente la propuesta como declinada. Eldetailincluye elcomentarioque haya dejado.cord:message: Se activa cada que el cliente deja un comentario general o una contraoferta en el chat de la cotización.cord:item_comment: Se dispara cuando el usuario deja un comentario específico sobre una partida (línea de producto) de la cotización, con elitem_idcorrespondiente en eldetail.
Estado
cord:updated: La cotización cambió mientras estaba abierta (el vendedor editó partidas, importes o condiciones). El cotizador ya se redibujó solo; úsalo para refrescar lo que tu página muestre alrededor.cord:status_changed: El estado cambió (sent,viewed,approved,rejected,expired,paid,invoiced; en la factura,open,paid,voidouncollectible). Eldetailtrae el nuevostatus(ysaldoen la factura).cord:paid: Solo en<cord-invoice>: la factura quedó saldada. Eldetailtraefolio,monedaysaldo.
Errores de carga
cord:error completa el trío cord:ready, cord:change y cord:error: dice por qué el documento no se ve o ya no se puede aprobar, para que tu página muestre su propio respaldo.
code |
Cuándo | Qué hace el SDK |
|---|---|---|
not_found |
No existe un documento con ese token, o ya no está disponible. | Muestra dentro del iframe el aviso de Cord. |
expired |
La cotización venció: se ve, pero ya no se puede aprobar ni pagar. Llega después de cord:ready, o en vivo si vence con la página abierta. |
Nada: el documento sigue a la vista. |
frame_blocked |
El iframe cargó y no llegó ningún mensaje de Cord: tu dominio no está en la lista de Ajustes › Cotizador embebible, o baseUrl no es el origen de Cord. |
Oculta el iframe. |
timeout |
No llegó cord:ready en el tiempo límite (10 segundos por defecto). |
Sigue esperando; si cord:ready llega después, state.error vuelve a null. |
not_found, frame_blocked y timeout también se avisan en la consola del navegador, con un enlace a su código en Errores de integración.
<cord-quote token="a1b2c3d4e5f60718" loading="eager" ready-timeout="15000"></cord-quote>
<p id="respaldo" hidden>No pudimos mostrar la cotización. Escríbenos y te la mandamos por correo.</p>
<script>
document.querySelector('cord-quote').addEventListener('cord:error', (evento) => {
if (evento.detail.code === 'expired') return; // el documento sigue a la vista
document.querySelector('#respaldo').hidden = false;
});
</script>
Con loading="lazy" (por defecto), el navegador no carga el iframe mientras está lejos de la pantalla, y el tiempo límite empieza a contar cuando se ve. Dentro de un modal o una pestaña oculta usa loading="eager": carga de inmediato y está listo al abrirse. ready-timeout="0" desactiva el tiempo límite. Con embed.js son data-cord-loading y data-cord-ready-timeout; en React y Vue, loading y readyTimeout.
Modo de solo lectura
Si tu organización no tiene dominios permitidos en Ajustes › Cotizador embebible, cualquier sitio puede mostrar el cotizador, pero en solo lectura: no ofrece aprobar, firmar, pagar ni escribir, y el botón lleva al link público de la cotización en una pestaña nueva. En ese modo los eventos llegan sin datos del documento: una página que no está en tu lista no recibe quién firmó, importes ni la URL de pago. Solo viajan la altura de cord:resize, el code de cord:error y el livemode de cord:ready.
Con dominios en la lista, el navegador no deja que una página fuera de ella muestre el iframe (frame-ancestors): no hay solo lectura para esa página, y el SDK emite cord:error con frame_blocked.
Barra de depuración
Con debug activo, Elements dibuja en tu sitio un panel con los eventos del iframe, cada petición a la API con su request_id y los avisos de configuración (por ejemplo, valores de appearance que Cord descartó).
<cord-quote token="a1b2c3d4e5f60718" debug></cord-quote>
<!-- o con embed.js -->
<div data-cord-token="a1b2c3d4e5f60718" data-cord-debug></div>
En React, <CordProvider debug>. El panel solo aparece con una llave pk_test_; con pk_live_ se limita a un aviso en la consola. Sin llave (Web Component o embed.js), solo aparece en localhost o si la URL lleva ?cord_debug=1, para que un atributo olvidado no se lo muestre a tus clientes.