Analytics

Encuentra exactamente qué campo mata tu formulario de leads

Un formulario de contacto con una tasa de abandono del 60 % no es un misterio que tengas que resolver adivinando. Beaconry registra tres señales por formulario (form_start, form_abandon, lead) y las agrupa en una tabla de abandono que nombra el campo exacto donde la gente se rinde. Funciona solo con GA4, nunca llega a un canal publicitario y almacena únicamente nombres de campo y recuentos, jamás un solo valor que un visitante haya tecleado.

Tiempo de lectura: ~7 minPublicado: 2026-06-08

La pregunta que un embudo responde de verdad

"Mi formulario convierte al 40 %" es un número. Por sí solo no es accionable. La versión accionable es "de la gente que empieza el formulario, la mayoría de quienes se rinden lo hace en el campo del número de teléfono". Esa frase te dice qué cambiar: haz que el campo de teléfono sea opcional, o muévelo más adelante, o explica por qué lo necesitas. No puedes llegar ahí desde un único porcentaje de conversión. Llegas ahí desde un embudo que sabe en qué punto del formulario se detiene la gente.

Beaconry construye ese embudo a partir de tres eventos, todos vinculados al mismo identificador de formulario:

  • form_start se dispara al primer foco de cualquier campo del formulario. El visitante se ha involucrado.
  • form_abandon se dispara al salir de la página cuando un formulario se inició, tiene al menos un campo rellenado y no se envió. Lleva el nombre del último campo que el visitante tocó.
  • El envío de lead es el evento del lado del servidor que Beaconry ya dispara cuando el formulario se envía de verdad (generate_lead, contact, subscribe, lo que sea que el formulario mapee).

Inicios menos envíos te da el abandono total. El last_field de cada abandono, agregado, te da el dónde. Ese segundo número es el que cambia lo que construyes a continuación.

Cómo los tres eventos encuentran el camino al mismo formulario

Todo esto solo funciona si el inicio y el abandono del lado del navegador caen sobre la misma form-key que el envío del lado del servidor. Un embudo que cuenta 100 inicios bajo una key y 40 leads bajo otra distinta es peor que ningún embudo, porque parece preciso y está mal.

Beaconry vincula los tres a un único identificador. En el cliente, el detectFormKey() de nl-data emite fluent_<id> o kadence_adv_<post_id>. Esa cadena es exactamente lo que el servidor produce con sanitize_key( plugin . '_' . form_id ) cuando dispara el lead. Así que para Fluent Forms y Kadence Advanced Forms, inicio, abandono y envío se agregan todos en una misma fila. Para plugins de formularios sin coincidencia de detectFormKey() en el navegador, el envío sigue registrándose bajo la form-key del servidor mientras el inicio y el abandono caen bajo el id del DOM, de modo que siempre obtienes recuentos de leads para cada plugin y el embudo de abandono completo para los dos que coinciden limpiamente.

La detección de abandono en sí es deliberadamente conservadora. Del código real del cliente:

// form_abandon on page-leave: started, >=1 filled field, not submitted
var filledCount = 0;
for ( var f in st.filled ) {
    if ( Object.prototype.hasOwnProperty.call( st.filled, f ) ) filledCount++;
}
if ( filledCount === 0 ) continue; // started but typed nothing -> not an abandon

sendEvent( 'form_abandon', {
    form_id:       keyFor( formEl ),
    last_field:    st.lastField || 'unknown',
    fields_filled: filledCount,
    fields_total:  st.total || filledCount,
} );

Un visitante que pone el foco en un campo y se va de inmediato sin teclear nada no es un abandono. Eso es un rebote, y contarlo inflaría la tasa de abandono de todos los formularios del sitio. Solo cuenta un formulario iniciado, parcialmente rellenado y no enviado. La carga útil son cuatro keys: el id del formulario, el nombre del último campo y dos recuentos. Mira lo que no está ahí.

Por qué solo nombres de campo y recuentos, y nada más

La propiedad más importante de esta función es lo que se niega a recopilar. El evento de abandono lleva last_field (el nombre del campo, como phone o company) y dos enteros. Nunca lleva el valor que el visitante tecleó en ese campo. La clase que lo almacena en el lado del servidor es explícita al respecto:

* PII-safe by construction: only field NAMES and counts are stored, never
* any entered value. The store therefore needs no GDPR special handling;
* the {@see RETENTION_DAYS}-day prune is a courtesy bound, not a
* data-protection requirement.

El servidor lo refuerza por partida doble. La cadena del último campo pasa por sanitize_text_field(), se limita a 60 caracteres, y los valores empty o unknown se mantienen por completo fuera de la lista de campos principales. El almacén guarda como máximo 40 nombres de campo distintos por formulario y día, ordenados por frecuencia, de modo que un formulario con cientos de nombres de campo dinámicos no puede inflar la fila de opciones.

Esto importa más allá de los buenos modales. Un embudo que registrara la dirección de correo a medio teclear o el número de teléfono parcial que un visitante abandonó sería un montón de datos personales reposando en wp_options sin base de consentimiento y sin plan de borrado. Al almacenar solo el nombre del campo, el embudo de Beaconry no necesita ningún tratamiento especial de RGPD. La purga a 90 días está ahí para acotar el tamaño de la opción, no para satisfacer una obligación de conservación, porque no hay datos personales que conservar. Aprendes que la gente se atasca en el campo de teléfono sin aprender un solo número de teléfono.

Por qué es solo GA4 y nunca toca un canal publicitario

Esta es la parte que la mayoría hace mal cuando la construye a mano, y la parte que Beaconry trata como innegociable. form_start y form_abandon son señales analíticas de comportamiento. No son conversiones. Meta no quiere un evento de "alguien abandonó un formulario" en su conjunto de optimización, TikTok no lo quiere, Google Ads no lo quiere. Enviar ruido de comportamiento a la API de conversiones de una plataforma publicitaria contamina la señal de optimización que la plataforma usa para encontrar compradores, y en algunas plataformas activa advertencias de validación.

Beaconry impone la separación con dos puertas independientes, cualquiera de las cuales por sí sola haría el trabajo:

  1. La puerta de opt-in vive en el observador del embudo. Siempre registra el abandono en el almacén interno del plugin, y luego, para form_abandon, devuelve null para cancelar el envío por completo a menos que el cliente haya activado explícitamente form_funnel_ga4. Así que, por defecto, un abandono solo toca el almacén local del embudo. Nada sale por la red en absoluto.
  2. La puerta de enrutamiento central vive en el forwarder. Justo después del filtro previo al envío, coteja el nombre del evento contra ANALYTICS_ONLY_EVENTS. Una coincidencia enruta el evento solo a GA4 y se detiene en seco antes de que se ejecute ningún canal publicitario. Esta es la única fuente de verdad para la separación entre "señal interna" y "conversión de proveedor publicitario".

La segunda puerta es la que se gana el sustento con el tiempo. Como la regla está centralizada en el nombre del evento, un canal publicitario recién añadido la hereda automáticamente. No hay ninguna lista de exclusión por canal que recordar actualizar. Cuando Beaconry añada la próxima API de conversiones, form_abandon ya está excluido de ella, con cero código nuevo, porque la puerta se ejecuta antes del fan-out y el nombre del evento está en la lista de solo analítica.

Esto fue un bug real antes de ser una regla. Hasta la v0.24.3, la puerta de opt-in devolvía el evento al fan-out completo, de modo que un cliente que activaba el reenvío a GA4 para abandonos, sin saberlo, filtraba un Custom Event form_abandon también a Meta. La puerta central arregló esa fuga concreta y, más importante, hizo imposible toda la clase de fuga para cualquier canal futuro. Efecto neto, independientemente del estado del toggle: un abandono llega a GA4 solo cuando lo pides, y nunca llega a un proveedor publicitario.

Qué muestra la tabla de abandono

La pestaña Forms renderiza una fila por formulario con cualquier actividad, extraída de snapshot(). Cada fila son inicios, envíos, abandonos, una tasa de conversión, una tasa de abandono y los tres campos de abandono principales por frecuencia:

$rows[] = [
    'form_key'        => $form_key,
    'label'           => $labels[ $form_key ] ?? $form_key,
    'starts'          => $starts,
    'submits'         => $subs,
    'abandons'        => $abnd,
    'conversion_rate' => $denom > 0 ? (int) round( ( $subs / $denom ) * 100 ) : 0,
    'abandon_rate'    => $starts > 0 ? (int) round( ( $abnd / $starts ) * 100 ) : 0,
    'top_fields'      => array_slice( $fields, 0, 3, true ),
];

Las filas se ordenan por tráfico (inicios más envíos), de modo que tus formularios con más actividad quedan arriba. La etiqueta proviene del catálogo de formularios que Beaconry ya mantiene, así que lees "Contact (Homepage)" y no kadence_adv_5. La ventana es conmutable y está limitada a la retención de 90 días, de modo que puedes mirar hoy, los últimos 7 días o los últimos 30. Un botón "Clear list" vacía el almacén sin detener el seguimiento futuro, ya que el cron de retención se reprograma a sí mismo en el siguiente arranque del plugin.

El número sobre el que actúas es top_fields. Una fila que dice "120 inicios, 48 leads, 60 % de abandono, campo principal: phone" es una orden de trabajo. Vas y haces opcional el campo de teléfono y observas cómo la tasa de abandono se mueve la próxima semana.

Por qué los checkouts de comercio quedan excluidos a propósito

Un checkout de WooCommerce, EDD o SureCart es un formulario en el sentido del DOM, pero no es un formulario de leads, y contarlo aquí te confundiría activamente. Un checkout ya tiene su propio embudo (view_item a begin_checkout a purchase). Su envío ocurre sobre la Store API o AJAX sin un evento nativo de envío de formulario, de modo que desde el punto de vista del embudo de formularios cada compra completada parece un formulario iniciado que nunca se envió, lo que dispararía un form_abandon en cada venta. Eso convertiría tu página de mejor conversión en tu formulario de peor aspecto.

Beaconry bloquea esto de dos formas. Primero, un flag de configuración formFunnelExclude agnóstico al render, fijado en el lado del servidor mediante las APIs de la plataforma (is_checkout(), is_cart(), edd_is_checkout()), desactiva todo el embudo en una página de checkout o de carrito de comercio, incluido el checkout de bloque con markup propio de EDD que ningún selector CSS coincidiría de forma fiable. Segundo, una comprobación isCommerceForm() en nl-data actúa como respaldo de clase CSS para las clases de componentes estándar de checkout y carrito de WooCommerce, EDD y SureCart. El abandono en pasos del checkout es trabajo del embudo de comercio, un begin_checkout sin purchase, y ahí se queda.

Contraste: el embudo de GA4 construido a mano

Puedes aproximar esto directamente en GA4. La gente lo hace. Esto es lo que realmente requiere, y por qué el resultado es peor.

Primero necesitas que los eventos existan. GA4 no tiene un evento integrado de "formulario abandonado en el campo X", así que cableas disparadores de Google Tag Manager en el foco de campo de formulario y en la salida de página, escribes JavaScript personalizado para seguir qué campo se tocó por última vez y si el formulario se envió, lo empujas al data layer y registras form_id y last_field como dimensiones personalizadas o parámetros de evento en el admin de GA4. Ese contenedor de GTM se carga desde googletagmanager.com, que es exactamente el dominio de terceros que un adblocker descarta, así que tus datos de embudo ya están sesgados hacia los visitantes con menos probabilidad de ser quienes abandonan por frustración. Luego construyes un embudo de exploración en GA4, esperas que las dimensiones personalizadas se llenen y aguardas el retraso de procesamiento antes de que los datos sean utilizables.

Ahora la parte que muerde más tarde. Si tu etiqueta de GTM para el evento de abandono está en el mismo contenedor que reenvía conversiones a Meta y TikTok, es realmente fácil dejar que ese evento de comportamiento fluya a la señal de una plataforma publicitaria por accidente, la misma fuga que la puerta central de Beaconry existe para evitar, salvo que en GTM no hay una única puerta, solo condiciones de disparador por etiqueta que tienes que acertar y mantener bien. Y el push al data layer de "último campo tocado" termina a menudo llevando el valor del campo junto al nombre a menos que tengas cuidado, lo que siembra GA4 con PII de forma silenciosa.

Beaconry colapsa todo eso en un toggle. Los eventos son first-party y del mismo origen, así que el sesgo de adblock desaparece. La frontera de PII se impone en el código, no en tu disciplina de GTM. El enrutamiento solo a GA4 es una puerta centralizada, no un conjunto de condiciones por etiqueta. No construyes un embudo, lees uno.

Conclusión

Una tasa de conversión te dice que un formulario tiene fugas. Un embudo te dice dónde. El embudo por formulario de Beaconry registra form_start, form_abandon y lead, vincula los tres al mismo formulario para que las cuentas sean honestas, y saca a la luz el campo exacto donde la gente se rinde. Se mantiene solo en GA4 tras dos puertas independientes, de modo que el ruido de comportamiento nunca contamina un canal publicitario, y almacena únicamente nombres de campo y recuentos, así que no hay PII que proteger ni plan de RGPD que redactar. Obtienes el único dato que cambia lo que construyes a continuación, qué campo arreglar, sin heredar el sesgo de adblock, el riesgo de fuga ni la exposición de PII de un embudo de GA4 construido a mano.