> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuración de Google Tag Manager

> Añade OneSignal Web Push a tu sitio web usando Google Tag Manager (GTM), incluyendo la configuración del service worker, inicialización y configuración segura de etiquetas.

Esta guía te muestra cómo cargar e inicializar el SDK Web de OneSignal usando Google Tag Manager (GTM) y, opcionalmente, establecer el External ID y las etiquetas de OneSignal después de la inicialización.

## Requisitos previos

* Un sitio que soporte HTTPS.
* Puedes publicar cambios en GTM para el contenedor del sitio.
* Has completado el flujo de [configuración del SDK Web](./web-sdk-setup) de OneSignal hasta **Añadir código al sitio**. Esto te proporciona:
  * Una aplicación OneSignal Web Push y App ID.
  * La configuración del [OneSignal Service Worker](./onesignal-service-worker).

## Configuración

### 1. Configura tu aplicación web de OneSignal

Sigue la [configuración del SDK Web](./web-sdk-setup) hasta llegar al paso **Añadir código al sitio**. Aquí es donde obtendrás el OneSignal App ID.

<Frame caption="Una vez que llegues a este paso, necesitarás hacer algunos ajustes al código para que funcione con Google Tag Manager.">
  <img src="https://mintcdn.com/onesignal/Y9PryqrHCRmPv_BC/images/web-push/add-code-to-site.png?fit=max&auto=format&n=Y9PryqrHCRmPv_BC&q=85&s=445d73fafe6bd92c5a6dcfbac563c1ee" alt="Paso Añadir código al sitio en el panel de configuración del SDK Web de OneSignal" width="2588" height="1638" data-path="images/web-push/add-code-to-site.png" />
</Frame>

<Warning>
  Debes subir el archivo OneSignal Service Worker directamente a tu servidor. Consulta [OneSignal Service Worker](./onesignal-service-worker).
</Warning>

### 2. Crea variables de GTM

Crea variables de GTM para los valores que referencias en las etiquetas. Esto evita codificar valores de forma fija y hace que tu configuración sea más fácil de mantener.

**Crea una variable `ONESIGNAL_APP_ID`**

1. En GTM, ve a **Variables > New**.
2. Elige **Constant**.
3. Nómbrala `ONESIGNAL_APP_ID`
4. Establece el valor como tu OneSignal App ID.
5. Guarda

<Frame caption="Creando una variable de OneSignal App ID">
  <img src="https://mintcdn.com/onesignal/wJS3gHTEqDzyW0IP/images/web-push/gtm-app-id-variable.png?fit=max&auto=format&n=wJS3gHTEqDzyW0IP&q=85&s=8a7752f1d4095d4c8e6b8119a9de2bfc" alt="Creando una variable de OneSignal App ID en Google Tag Manager" width="2378" height="1656" data-path="images/web-push/gtm-app-id-variable.png" />
</Frame>

<Check>
  Ahora puedes referenciar tu App ID en cualquier lugar de GTM usando `{{ ONESIGNAL_APP_ID }}`.
</Check>

**Crea una variable `ONESIGNAL_EXTERNAL_ID` (Recomendado)**

Usa esta variable si asocias usuarios con un identificador externo (por ejemplo, un ID de usuario de tu base de datos o sistema de autenticación).

Elige el tipo de variable según cómo esté disponible el valor en tu sitio. Opciones comunes:

* Data Layer Variable (recomendado)
* First-Party Cookie
* DOM Variable (avanzado)

### 3. Crea la etiqueta de inicialización de OneSignal

1. En GTM, ve a **Tags > New**
2. Nombra la etiqueta: `OneSignal - Init`
3. Tag Type: **Custom HTML**
4. Pega el código a continuación.
5. En **Advanced Settings > Tag firing options**, establece **Once per page**.
6. En **Triggering**, selecciona **Initialization - All Pages**.

```html HTML theme={null}
<!--
  OneSignal – Inicialización del Web SDK usando Google Tag Manager

  Este fragmento:
  - Carga el OneSignal Web SDK
  - Inicializa OneSignal con tu App ID
  - Habilita la campana de suscripción (notifyButton)

  Funciona para la mayoría de los sitios de forma predeterminada.
-->

<!-- 1. Cargar el OneSignal Web SDK (v16) -->
<!-- Este script debe cargarse en cada página donde quieras que OneSignal esté disponible -->
<script
  src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js"
  defer>
</script>

<script>
  // Asegurar que el dataLayer de GTM exista
  // Se usa aquí solo para opcionalmente enviar un evento "OneSignalInitialized"
  window.dataLayer = window.dataLayer || [];

  // OneSignalDeferred es una cola que se ejecuta una vez que el SDK está completamente cargado
  window.OneSignalDeferred = window.OneSignalDeferred || [];

  // 2. Inicializar OneSignal una vez que el SDK esté listo
  window.OneSignalDeferred.push(function (OneSignal) {

    OneSignal.init({
      /*
        REQUERIDO
        Se recomienda establecer el OneSignal App ID como variable de GTM.
        Puedes encontrarlo en tu Panel de OneSignal en:
        Settings > Keys & IDs
      */
      appId: "{{ONESIGNAL_APP_ID}}",

      /*
        OPCIONAL – SOLO NECESARIO SI TU SERVICE WORKER NO ESTÁ EN LA RAÍZ

        Si tu service worker está alojado en:
          /OneSignalSDKWorker.js

        …entonces NO debes establecer serviceWorkerPath o serviceWorkerParam.

        Descomenta y actualiza las opciones a continuación SOLO si tu service worker
        está alojado en un subdirectorio (por ejemplo: /push/onesignal/).
      */

      //serviceWorkerPath: "push/onesignal/OneSignalSDKWorker.js",
      //serviceWorkerParam: { scope: "/push/onesignal/" },

      /*
        OPCIONAL
        Habilita la campana de suscripción de OneSignal (notifyButton),
        que permite a los usuarios suscribirse o cancelar la suscripción a notificaciones.
        Para más opciones de aviso, consulta: https://documentation.onesignal.com/docs/en/permission-requests
      */
      notifyButton: {
        enable: true
      }
    })
    .then(function () {
      // OneSignal inicializado correctamente
      console.log("[OneSignal] init success");

      // Recomendado: enviar un evento a GTM para activar otras etiquetas
      window.dataLayer.push({
        event: "OneSignalInitialized"
      });
    })
    .catch(function (e) {
      // Inicialización fallida (App ID inválido, service worker faltante, etc.)
      console.log("[OneSignal] init failed", e);
    });
  });
</script>
```

<Frame caption="Configurando la etiqueta OneSignal - Init">
  <img src="https://mintcdn.com/onesignal/Y9PryqrHCRmPv_BC/images/web-push/gtm-init-tag.png?fit=max&auto=format&n=Y9PryqrHCRmPv_BC&q=85&s=6cec423472cb84777bddd893a59c83ff" alt="Configurando la etiqueta OneSignal - Init en Google Tag Manager" width="2442" height="2296" data-path="images/web-push/gtm-init-tag.png" />
</Frame>

<Warning>
  Si usas un banner de consentimiento / CMP, consulta las opciones de [Consent Mode y consideraciones de privacidad](#consent-mode-and-privacy-considerations) a continuación.
</Warning>

### 4. Establece External ID y etiquetas

Establecer el [External ID](./users#external-id) es opcional pero recomendado porque te permite identificar usuarios entre dispositivos y sincronizar con tu backend.

**Enviar `ONESIGNAL_EXTERNAL_ID` al dataLayer**

Este ejemplo muestra cómo puedes enviar un ID de usuario al dataLayer para que GTM pueda leerlo a través de la variable `ONESIGNAL_EXTERNAL_ID` (creada en el paso 2).

```html HTML theme={null}
<script>
  window.dataLayer = window.dataLayer || [];

  // Obtén tu ID de usuario de tu base de datos o sistema de autenticación.
  // Asegúrate de que sea un valor de cadena.
  var userId = "your_user_id_here";

  dataLayer.push({
    ONESIGNAL_EXTERNAL_ID: String(userId),
  });
</script>
```

**Crea una etiqueta GTM para establecer el External ID**
Configuración de etiqueta:

* Nombre de etiqueta: `OneSignal – Set External ID`
* Tipo de etiqueta: **Custom HTML**
* Tag firing options: **Once per page**
* Trigger:
  * Crea un activador de evento personalizado para `OneSignalInitialized` (establecido en la etiqueta **OneSignal - Init** anterior) y
  * Opcionalmente si sabes que el ID de usuario está disponible en la carga de la página.

<Warning>
  El método requerido para establecer el External ID es `OneSignal.login(externalId)` donde `externalId` es una cadena.

  Si `{{ONESIGNAL_EXTERNAL_ID}}` está vacío (o GTM sustituye "undefined" / "null"), la llamada a login se omitirá y el External ID no se establecerá. Este es un problema de tiempo común de GTM.
</Warning>

<CodeGroup>
  ```html Ejemplo básico para establecer el External ID theme={null}
  <script>
    // OneSignalDeferred asegura que esto se ejecute después de que el SDK de OneSignal esté listo
    window.OneSignalDeferred = window.OneSignalDeferred || [];

    window.OneSignalDeferred.push(function (OneSignal) {
      /*
        Leer el External ID desde Google Tag Manager.
        Esto debería ser una variable de GTM (Data Layer Variable o Custom JS Variable).
      */
      var externalId = "{{ONESIGNAL_EXTERNAL_ID}}";

      console.log("[OneSignal] raw external ID from GTM:", externalId);

      /*
        Validación básica:
        - GTM puede sustituir undefined/null como cadenas
        - OneSignal.login requiere una cadena
      */
      if (!externalId || externalId === "undefined" || externalId === "null") {
        console.log("[OneSignal] External ID missing, skipping login");
        return;
      }

      // Asegurar que el External ID sea una cadena limpia
      externalId = String(externalId).trim();

      console.log("[OneSignal] Calling OneSignal.login with External ID:", externalId);

      /*
        Iniciar sesión del usuario en OneSignal usando el External ID.
        Esto vincula el navegador/dispositivo actual a este usuario.
      */
      OneSignal.login(externalId)
        .then(function () {
          console.log("[OneSignal] External ID set successfully:", externalId);
        })
        .catch(function (e) {
          console.log("[OneSignal] Failed to set External ID", e);
        });
    });
  </script>
  ```

  ```html Ejemplo avanzado para probar si el External ID no se está estableciendo theme={null}
  <script>
    window.dataLayer = window.dataLayer || [];
    window.OneSignalDeferred = window.OneSignalDeferred || [];

    OneSignalDeferred.push(function (OneSignal) {
      var rawExternalId = "{{ONESIGNAL_EXTERNAL_ID}}";

      // ---- Helpers ----
      function log() {
        console.log.apply(console, ["[OneSignal External ID]"].concat([].slice.call(arguments)));
      }

      function normalizeExternalId(v) {
        // GTM commonly substitutes these as strings
        if (
          v === undefined ||
          v === null ||
          v === "undefined" ||
          v === "null"
        ) return null;

        var s = String(v).trim();
        if (!s.length) return null;

        return s;
      }

      function pushDL(eventName, extra) {
        try {
          var payload = Object.assign({ event: eventName }, extra || {});
          window.dataLayer.push(payload);
        } catch (e) {
          // no-op
        }
      }

      function readStateSnapshot() {
        var snapshot = {
          onesignalId: null,
          externalId: null,
          pushSubscriptionId: null
        };

        try {
          snapshot.onesignalId = OneSignal.User && OneSignal.User.onesignalId;
          snapshot.externalId = OneSignal.User && OneSignal.User.externalId;
          snapshot.pushSubscriptionId =
            OneSignal.User &&
            OneSignal.User.PushSubscription &&
            OneSignal.User.PushSubscription.id;
        } catch (e) {
          log("Error reading OneSignal.User state", e);
        }

        return snapshot;
      }

      function isExternalIdApplied(targetExternalId) {
        var current = normalizeExternalId(OneSignal.User && OneSignal.User.externalId);
        return current === targetExternalId;
      }

      // ---- Initial logging ----
      log("Tag fired. rawExternalId:", rawExternalId, "type:", typeof rawExternalId);

      var externalId = normalizeExternalId(rawExternalId);
      log("Normalized externalId:", externalId, "type:", typeof externalId);

      if (!externalId) {
        log("Not calling login(): externalId missing/invalid");
        pushDL("OneSignalExternalIdMissing", { reason: "invalid_or_missing_external_id" });
        return;
      }

      // Optional: enable verbose OneSignal logs during testing
      if (OneSignal.Debug && OneSignal.Debug.setLogLevel) {
        OneSignal.Debug.setLogLevel("trace");
        log("Enabled OneSignal Debug log level: trace");
      }

      // ---- Attach User State observer ----
      var changeFired = false;

      OneSignal.User.addEventListener("change", function (event) {
        changeFired = true;

        log("User change event fired:", event);

        var snapshot = readStateSnapshot();
        log("User state snapshot:", snapshot);

        // Helpful: push snapshot-ish DL event (optional)
        pushDL("OneSignalUserStateChanged", {
          onesignal_id: snapshot.onesignalId || "",
          external_id: normalizeExternalId(snapshot.externalId) || "",
          push_subscription_id: snapshot.pushSubscriptionId || ""
        });
      });

      // ---- Login + confirm + retry ----
      var attempt = 0;
      var MAX_RETRIES = 3;
      var CONFIRM_WINDOW_MS = 1500;
      var BASE_BACKOFF_MS = 500;

      function doLogin() {
        attempt += 1;
        changeFired = false;

        log("Calling OneSignal.login()", { externalId: externalId, attempt: attempt });

        OneSignal.login(externalId)
          .then(function () {
            log("OneSignal.login() promise resolved");
            waitForConfirmation();
          })
          .catch(function (e) {
            log("OneSignal.login() promise rejected", e);
            retry("promise_rejected");
          });
      }

      function waitForConfirmation() {
        var start = Date.now();

        (function check() {
          if (isExternalIdApplied(externalId)) {
            log("Confirmed externalId applied via state check:", externalId);

            var snapshot = readStateSnapshot();
            log("Final state snapshot:", snapshot);

            pushDL("OneSignalExternalIdSet", {
              external_id: externalId,
              attempt: attempt,
              push_subscription_id: snapshot.pushSubscriptionId || ""
            });

            return;
          }

          if (changeFired) {
            log("Change event observed but externalId not yet reflected; waiting...");
          }

          if (Date.now() - start >= CONFIRM_WINDOW_MS) {
            log("No confirmation within window", {
              attempt: attempt,
              changeFired: changeFired,
              currentExternalId: normalizeExternalId(OneSignal.User && OneSignal.User.externalId)
            });

            retry("no_confirmation");
            return;
          }

          setTimeout(check, 100);
        })();
      }

      function retry(reason) {
        if (attempt >= MAX_RETRIES) {
          log("Giving up after max retries", { attempts: attempt, reason: reason });

          var snapshot = readStateSnapshot();
          log("State at give-up:", snapshot);

          pushDL("OneSignalExternalIdSetFailed", {
            external_id: externalId,
            reason: reason,
            attempts: attempt,
            push_subscription_id: snapshot.pushSubscriptionId || ""
          });

          return;
        }

        var delay = BASE_BACKOFF_MS * Math.pow(2, attempt - 1);
        log("Retrying login after delay", { delayMs: delay, reason: reason, nextAttempt: attempt + 1 });

        setTimeout(doLogin, delay);
      }

      // If already applied, don't spam login()
      if (isExternalIdApplied(externalId)) {
        log("ExternalId already applied; skipping login.", externalId);

        var snapshot = readStateSnapshot();
        log("Current state snapshot:", snapshot);

        pushDL("OneSignalExternalIdAlreadySet", {
          external_id: externalId,
          push_subscription_id: snapshot.pushSubscriptionId || ""
        });

        return;
      }

      // Kick it off
      doLogin();
    });
  </script>
  ```
</CodeGroup>

#### Establecer etiquetas

Esto envía [Etiquetas de OneSignal](./add-user-data-tags) usando nuestro SDK Web.

Configuración de etiqueta:

* Nombre: `OneSignal - Add Tags`
* Tag Type: **Custom HTML**
* Tag firing options: **Once per page**
* Trigger:
  * `OneSignalInitialized`, y
  * Tu condición para cuando los datos de la etiqueta estén disponibles (por ejemplo: después del inicio de sesión, en una página de perfil, después de una compra).

Pega este código y reemplaza el TAG y VALUE de ejemplo.

```html HTML theme={null}
<script>
  window.OneSignalDeferred = window.OneSignalDeferred || [];
  window.OneSignalDeferred.push(function (OneSignal) {
    OneSignal.User.addTags({
      TAG_1: "VALUE_1",
      TAG_2: "VALUE_2",
    });
  });
</script>
```

<Note> Solo envía etiquetas cuando tengas los datos de usuario disponibles (por ejemplo, después del inicio de sesión, después de que se carga un perfil o después de un evento de conversión conocido).</Note>

### Consent Mode y consideraciones de privacidad

Si tu sitio usa Consent Mode / un CMP, decide si OneSignal debe cargarse:

* Solo después del consentimiento (común para EU/UK), o
* Inmediatamente (común donde el almacenamiento "funcional" está permitido por defecto).

GTM admite un activador de inicialización de consentimiento y controles de consentimiento a nivel de etiqueta para administrar el comportamiento de las etiquetas según el consentimiento del usuario. Sin embargo, OneSignal también proporciona métodos de consentimiento de privacidad para controlar cuándo se carga el SDK.

* [Manejo de datos personales](./handling-personal-data)
* [Métodos de privacidad del SDK Web](./web-sdk-reference#privacy)

***

## Pruebas

1. En GTM, abre el modo Preview.
2. Carga tu sitio y confirma:
   * `OneSignal - Init` se activa una vez.
   * `OneSignalInitialized` aparece en la línea de tiempo de eventos de GTM (si mantuviste el evento push).
3. Suscríbete a tu sitio web. Consulta [Solicitudes de permisos web](./permission-requests) para obtener detalles sobre las solicitudes.
4. En el panel de OneSignal, ve a **Audience > Subscriptions** y confirma:
   * Una suscripción aparece después de aceptar.
   * Un External ID es visible si estableciste uno.
5. Envía un push de prueba desde **Messages > New Push**.

<Check> Si la inicialización está funcionando, verás las suscripciones apareciendo en OneSignal después de aceptar.</Check>

### Solución de problemas

* La etiqueta Init se activa, pero el SDK nunca se carga
  * Verifica si la Content Security Policy (CSP) está bloqueando `https://cdn.onesignal.com`.
  * Verifica los bloqueadores de anuncios/bloqueadores de scripts.

* Errores de `dataLayer`
  * Asegúrate de que `window.dataLayer = window.dataLayer || []` esté configurado antes de cualquier llamada `dataLayer.push()`.

* Avisos duplicados / carga duplicada del SDK
  * Asegúrate de no estar cargando OneSignal también a través del código del sitio, un plugin de CMS u otra etiqueta de GTM.

* Add Tags se ejecuta pero no aparece en OneSignal
  * Confirma que el Trigger Group espera `OneSignalInitialized`.
  * Confirma que tu activador de acción de usuario se activa realmente.
  * Confirma que las etiquetas son pares clave/valor válidos y están dentro de los [límites del plan](https://onesignal.com/pricing).

<Warning>
  Si aún necesitas ayuda, consulta [Solución de problemas del SDK Web](./troubleshooting-web-push) para correcciones comunes.
</Warning>

## Próximos pasos

* [Solicitudes de permisos web](./permission-requests)
* [Etiquetas](./add-user-data-tags)
* [Referencia del SDK Web](./web-sdk-reference)

***
