← Hub Guía Roadmap
Reparación de tracking Julio 2026

Reparación de tracking: por qué no sabemos de qué campaña viene la mitad del volumen

Cuatro fallos medidos sobre los datos de julio 2026, con la evidencia que los demuestra, qué se puede recuperar sin tocar la web y qué hay que arreglar en origen.

Resumen
FalloImpacto en julio 2026¿Recuperable?Dónde se arregla
1 · La campaña no llega a HubSpot 38 de 88 registros (43%) y 11 de 17 SQL (65%) sin campaña → 19 y 3 tras recuperar por gclid Recuperado 2026-08-03 · caduca 2026-10-28Web + pull mensual
2 · Talento contado como SQL de demand 1 de 17 SQL en julio · 3 en junio · 0 en mayo Sí, filtro en el scriptDefinición de KPI
2b · La excepción de la etiqueta GA4 no bloquea 8.384 page_view en app pese a estar excluido por hostname Sí, corrigiendo el triggerContenedor GTM
3 · Origen perdido en el salto www → app 641 de 1.385 registros GA4 como tráfico directo ParcialCross-domain / consent
4 · Clics facturados que no generan sesión DG_Supply: 8.543 clics → 416 sesiones (4,9%) Determinar causa primero
🔴
Fallo 1 · La campaña no llega a HubSpot
43% de los registros y 65% de los SQL sin campaña asignable · recuperado por gclid hasta 19 y 3
Crítico
Qué se ve

En HubSpot, esos contactos tienen hs_analytics_source = PAID_SEARCH (así que sabemos que vienen de Google Ads) pero hs_analytics_source_data_1 = "Auto-tagged PPC" en lugar del nombre de campaña. En el panel de inversión aparecen como Campaña sin resolver: cuentan en el objetivo del canal, pero no se pueden imputar a ninguna campaña ni entrar en ningún CPL por campaña.

La evidencia

Comparando la URL de primer contacto (hs_analytics_first_url) de los contactos que sí resuelven campaña contra los que no, en el mismo mes y con la misma fuente:

GruponLleva hstk_campaignLleva _gl=Tiene gclid guardado
Resuelven _brand_google_search__4545 / 450
Resuelven pmax_supply_registro_es2222 / 221
No resuelven (Auto-tagged PPC)350 / 3535 / 3535 / 35

La correlación es perfecta en los dos sentidos, así que no es casualidad ni una campaña concreta mal configurada.

La causa

Todas las campañas de la cuenta llevan la plantilla de seguimiento de HubSpot, que añade a la URL final hstk_campaign (el ID numérico de la campaña), hstk_creative, hstk_network=googleAds y hsa_acc=3453900141. HubSpot resuelve el nombre de campaña leyendo ese parámetro en la URL, no traduciendo el gclid.

Cuando el primer pageview que registra HubSpot no es la landing de la campaña sino una navegación posterior, esa URL ya no lleva los parámetros: solo queda el gclid, que Google propaga. HubSpot entonces sabe que es Paid Search pero no de qué campaña, y escribe el literal "Auto-tagged PPC". El marcador de que la URL es una navegación posterior es _gl=, la decoración que añade el enlazador cross-domain de Google Analytics, presente en los 35 casos fallidos y en ninguno de los 67 que funcionan.

Las páginas donde HubSpot engancha el primer pageview de esos 35 son variadas y muchas no son landings de campaña: /politica-de-privacidad, /we-are-shakers, app.shakersworks.com/login. Cinco de los 35 tienen la primera URL directamente en app.shakersworks.com.

Por qué HubSpot se pierde el primer pageview · confirmado en la web

Cargando una landing de campaña con parámetros reales y sin aceptar el banner, el estado del navegador es:

# cookies tras cargar /unirme-a-shakers?hstk_campaign=…&gclid=… cookieyes-consent = consent:no, necessary:yes, analytics:no, advertisement:no __hs_do_not_track = yes ← HubSpot en modo "no trackear" hubspotutk = NO EXISTE ← sin cookie de usuario, no hay pageview registrado _hsq.length = 0 # capa propia de tracking: esta SÍ funciona antes del consentimiento shk_source_ft = google shk_medium_ft = ppc shk_anon_id = a8f4e185-… localStorage.shk_clickids = {"v":{"gclid":"…"},"exp":…} localStorage.first_landing_path, first_landing_ts

Mientras __hs_do_not_track está puesta, HubSpot no crea hubspotutk ni registra visitas. El seguimiento arranca cuando el visitante acepta el banner, y lo hace desde la página en la que esté en ese momento. De ahí las dos poblaciones:

  • Acepta en la propia landing → HubSpot registra ese pageview, que lleva hstk_campaign → campaña resuelta. Son los 67.
  • Acepta después de navegar → el primer pageview que HubSpot ve es una página posterior, con _gl= y sin parámetros de campaña. Solo queda el gclid → "Auto-tagged PPC". Son los 35.

El dato del CRM lo confirma: esos 35 contactos sí tienen historial de navegación en HubSpot (mediana de 7 páginas vistas y 3 visitas), así que no es que HubSpot no les trackee nunca, es que empezó a trackearles tarde. Los 45 que resuelven Brand tienen mediana de 16 páginas y ninguno con cero.

CSP descartado como causa en www. La página no declara ninguna cabecera ni meta de CSP restrictiva y todos los scripts relevantes cargan sin error: js.hs-analytics.net/analytics/…/6704369.js (HubSpot), gtm.js y gtag/js. En app.shakersworks.com no se ha podido comprobar porque requiere sesión iniciada: queda pendiente ahí.
Los 35 comparten un mismo valor en hs_analytics_source_data_2 (c1d25565-f664-4089-9f0a-1734cc1b3a65), identificador que no hemos podido asociar a nada. Pista, no conclusión.
La solución no es solo técnica. Cambiar qué se registra antes del consentimiento afecta a RGPD. La vía limpia es la que ya está a medio construir: la capa propia de cookies shk_* guarda ya fuente, medio, gclid y landing de primera visita — solo falta la campaña — y viaja en el formulario, donde el usuario facilita sus datos de forma consciente. Antes de tocar el orden de carga o el comportamiento pre-consentimiento, validar con un experto humano en privacidad.
El dato no está perdido

Los 35 contactos tienen el gclid completo guardado en hs_google_click_id, y la API de Google Ads lo traduce a campaña con el recurso click_view. Verificado con match exacto:

# gclid guardado en HubSpot (contacto creado 2026-07-06) CjwKCAjwpK3SBhASEiwAtV1SPDzBraAaGvPSpiLq64ea87PG3F3zGaXgQxI2KI01MHVtmrt-A33s3xoC0-kQAvD_BwE # GAQL sobre la cuenta 3453900141 SELECT click_view.gclid, campaign.name, campaign.id FROM click_view WHERE segments.date = '2026-07-06' AND click_view.gclid = '<gclid>' # resultado campaign.name = "_BRAND_Google_Search__" campaign.id = 20417850941
Ventana de 90 días. click_view solo devuelve clics de los últimos 90 días, y exige filtrar por un único día. La recuperación de julio 2026 caduca el 2026-10-28; desde el 2026-09-28 se empieza a perder el arranque del mes. Pasado ese plazo el volumen se queda sin campaña para siempre.
Recuperación aplicada · julio 2026

Implementado en deploy/pull-google-ads.py. Para cada contacto sin campaña que tiene hs_google_click_id se consulta click_view: primero el día de la primera visita que registra HubSpot y, si no hay match, se barre el resto de la ventana (el clic puede ser de días antes de que el visitante aceptara cookies). La campaña recuperada entra en todas las lentes de origen: SQL por campaña, origen supply y panel de inversión y objetivos.

LenteSin campañaCon gclidRecuperadosSiguen sin resolver
SQL de demand11118 (73%)3
Contactos supply422719 (45%)23

Lo recuperado va casi entero a Brand: 6 de los 8 SQL y los 19 registros de talento son _BRAND_Google_Search__. Los otros dos SQL son SHT_Backend_Enterprise_Q2 y PMax_Supply_Registro_ES. Es coherente con la causa: quien llega por Brand navega más antes de aceptar el banner, así que es la campaña que más volumen perdía.

Efecto en las cifras publicadas: los registros de talento originados por Brand en el CRM pasan de 38 a 57, los SQL de Brand de 1 a 7, y el bloque sin resolver baja de 38 a 19 registros y de 11 a 3 SQL. Los totales del canal no se mueven (88 registros, 17 SQL): esto reparte, no crea volumen.

Qué queda fuera y por qué. De los 26 contactos que siguen sin campaña, 15 no tienen ni gclid guardado (nada que traducir), 1 tiene un clic de abril, fuera de la ventana de 90 días, y 10 tienen gclid pero no aparecen en click_view en el día de la primera visita. Estos últimos requieren el barrido completo de la ventana, que necesita credenciales de la Google Ads API en el entorno del script.
El gclid no se persiste. El JSON del mes solo guarda agregados por campaña y los contadores de la recuperación. El identificador de clic se usa en memoria durante el pull y no se escribe en el repositorio.
🟠
Fallo 2 · Talento contado como SQL de demand
El mismo campo de lifecycle sirve a los dos funnels
Definición
Qué se ve

El KPI SQL CRM de la pestaña Demand cuenta contactos creados en el mes con lifecyclestage en salesqualifiedlead, opportunity o customer y fuente original Paid Search. Pero el funnel de talento usa el mismo campo lifecyclestage, así que un talento que avanza en su propio ciclo entra en el conteo de SQL de empresas.

La evidencia

Contactos que cumplen la definición de SQL y además tienen lead_status_supply (es decir, son talento):

MesSQL contadosDe los cuales son talentoPeso
Julio 2026171 (lifecycle customer, supply status "Active user", sin empresa)6%
Junio 202618 publicados317%
Mayo 20263400%

Es el mismo tipo de contaminación que ya corregimos con los contactos de prueba de QA Shakers, y se arregla igual: un filtro en el script. La diferencia es que este cambia un número ya publicado, así que no lo he aplicado: hay que decidir si el SQL de demand excluye a los contactos con lead_status_supply.

Caso aparte, y este sí es legítimo: la campaña PMax_Supply_Registro_ES originó 2 SQL de demand. Los dos aterrizaron en /unirme-a-shakers, una página de talento. Uno es el talento del párrafo anterior; el otro es un contacto de empresa real que llegó por una landing de talento. Por eso su gasto se declara pero no se reparte: el volumen es pequeño y la mitad se explica por el fallo de definición, no por un cruce real de audiencias.
🟠
Fallo 2b · La etiqueta GA4 – Pageview y su excepción
La excepción no bloquea nada y no hace lo que parece querer hacer
Configuración

Configuración actual de la etiqueta GA4 – Pageview: se activa con el trigger Todas las páginas (Hubspot) (Page View, cuando Page Hostname no contiene app.shakersworks.com) y tiene como excepción App Exclude (tipo Inicialización, Page URL coincide con app\.shakersworks\.com(?!/register/talent)).

Dos problemas, independientes del de HubSpot:

  1. La excepción nunca se evalúa. En GTM una excepción solo bloquea si se cumple en el mismo evento que activa la etiqueta. La etiqueta se dispara en un evento Page View (gtm.js) y la excepción es de tipo Inicialización (gtm.init), que ocurre antes y es otro evento distinto. Nunca coinciden, así que la excepción es decorativa: no bloquea ni un disparo.
  2. Una excepción no puede volver a permitir. Si el lookahead (?!/register/talent) buscaba "excluir app salvo la página de registro de talento", eso no se consigue con una excepción, que solo sabe bloquear. Y tampoco hace falta: el trigger principal ya excluye todo app por hostname. Para incluir /register/talent hay que ampliar el trigger de activación (hostname no contiene app o la ruta empieza por /register/talent), no añadir una excepción.

Además la regex no está anclada, así que si alguna URL lleva la propia dirección dentro de un parámetro (un redirect_uri, por ejemplo) puede coincidir por esa segunda aparición y bloquear páginas que no toca.

Y hay algo que no cuadra con los datos: pese a que el trigger excluye app por hostname, GA4 registró 8.384 page_view en app.shakersworks.com en julio. Alguien más los está enviando: la propia aplicación, otra etiqueta del contenedor o la medición mejorada de la configuración de GA4. Hay que revisarlo en el contenedor completo, porque implica que la exclusión no tiene el efecto que se busca y puede haber doble medición.

Para contexto, así se reparten los eventos por dominio en julio 2026: registro_talent ocurre al 100% en app (1.385 eventos) y supply_form al 100% en www (584). El registro vive en la aplicación y el formulario en la web, así que la continuidad entre dominios es justo lo que decide si el registro se puede atribuir.

🟠
Fallo 3 · Origen perdido en el salto www → app
El registro se completa en otro dominio
Por verificar

El registro de talento termina en app.shakersworks.com, mientras la landing está en www.shakersworks.com. En GA4, de los 1.385 eventos registro_talent de julio, 641 (46%) tienen origen (direct)/(none) y 39 más aparecen como (not set).

Marcado como por verificar. Parte de ese tráfico directo será gente que efectivamente vuelve a la web escribiendo la URL, así que el 46% es el techo del problema, no su tamaño. Lo que sí está confirmado es que el mecanismo existe: los 35 contactos del fallo 1 llevan la decoración _gl= del enlazador cross-domain, y 5 de ellos tienen su primer pageview ya dentro de app. Para dimensionarlo hace falta una prueba de navegación completa (landing en www con parámetros → registro en app) comprobando qué llega a GA4 y a HubSpot en cada paso.

Esto es lo mismo que Jose describió como pérdida de tracking entre MQL y producto: mismo salto de dominio, mismo síntoma.

🟡
Fallo 4 · Clics facturados que no generan sesión
Puede ser tráfico inválido o puede ser medición
Por verificar

DG_Supply_TierA_AILLM_ES_Prospecting facturó 8.543 clics en julio y GA4 registró 416 sesiones (4,9%). Las otras campañas de la cuenta se mueven entre el 19% y el 61%, así que no es el nivel de pérdida normal por rebote o bloqueo de cookies.

La landing (/unirme-a-shakers) está en www y sí está medida, así que no es un dominio fuera de GA4. Los 416 que llegan se comportan de forma razonable: 68,5% de sesiones con engagement y 293 segundos de media. Es decir, la audiencia que aterriza no es basura; lo que no cuadra es el volumen facturado.

Consecuencia práctica: el CPC declarado de €0,05 es en realidad €1,08 por sesión. Antes de decidir si se pausa la campaña hay que distinguir entre clics accidentales en placements de Demand Gen (tráfico inválido, se reclama a Google) y un problema de medición o redirección. La prueba: comparar clics de Google Ads contra peticiones al servidor de la landing en el mismo periodo.
🛠️
Plan de reparación
Ordenado por impacto sobre la atribución
  1. Recuperar la campaña por gclid antes de que caduque. Hecho el 2026-08-03 para julio. Implementado en deploy/pull-google-ads.py: los contactos sin campaña que tienen hs_google_click_id se resuelven contra click_view de la Google Ads API y la campaña recuperada entra en todas las lentes de origen (SQL por campaña, origen supply, panel de inversión). Resultado en julio: 8 de 11 SQL (73%) y 19 de 42 contactos supply (45%). Detalle en el fallo 1 y en Salud Operativa de la ficha. Es una capa de reporting: no arregla el CRM, y la ventana de 90 días vence el 2026-10-28.
  2. Guardar la campaña en la capa propia y enviarla en el formulario. Ya se capturan fuente, medio, gclid y landing de primera visita en cookies shk_* antes del consentimiento; falta añadir la campaña (de utm_campaign o hstk_campaign) y mandarla como propiedad de contacto en el submit. Es la reparación de raíz del fallo 1 y no depende de cuándo acepte el visitante. Validar el enfoque con un experto humano en privacidad antes de implementarlo.
  3. Arreglar la etiqueta GA4 – Pageview en GTM. La excepción de tipo Inicialización no bloquea nada: si se quiere medir /register/talent, va en el trigger de activación. Y averiguar de dónde salen los 8.384 page_view de app que el trigger debería estar excluyendo.
  4. Cerrar la continuidad www → app. Propagar campaña (no solo el gclid) en el salto de dominio y verificar que el registro que se completa en app conserva el origen.
  5. Decidir la definición de SQL. Si el SQL de demand excluye contactos con lead_status_supply, aplicarlo en el script y reexpresar junio (18 → 15). Cambia un número publicado, así que es decisión de negocio.
  6. Distinguir tráfico inválido de medición en la Demand Gen. Clics de Google Ads contra peticiones de servidor a la landing. Según el resultado: reclamación a Google o corrección de medición.
Mientras 1 a 4 no estén cerrados, los CPL por campaña se calculan sobre la mitad del volumen y la ficha lo dice explícitamente en cada tabla afectada y en Salud Operativa. Los totales del canal (88 registros, 17 SQL) no están afectados: el volumen es correcto, lo que falta es saber a qué campaña imputarlo.