> ## 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.

# Google Tag Manager セットアップ

> Google Tag Manager（GTM）を使用してウェブサイトに OneSignal Web Push を追加する方法。Service Worker のセットアップ、初期化、タグの安全な設定を含みます。

このガイドでは、Google Tag Manager（GTM）を使用して OneSignal Web SDK を読み込み、初期化する方法を説明します。その後、オプションで初期化後に External ID と OneSignal タグを設定できます。

## 前提条件

* HTTPS をサポートするサイト。
* サイトのコンテナに対して GTM で変更を公開できること。
* OneSignal の [Web SDK セットアップ](./web-sdk-setup) フローを **サイトにコードを追加** まで完了していること。これにより以下が得られます：
  * OneSignal Web Push アプリと App ID。
  * [OneSignal Service Worker](./onesignal-service-worker) のセットアップ。

## セットアップ

### 1. OneSignal Web アプリをセットアップする

[Web SDK セットアップ](./web-sdk-setup) を **サイトにコードを追加** まで進めます。ここで OneSignal App ID を取得します。

<Frame caption="このステップに到達したら、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="OneSignal Web SDK セットアップダッシュボードのサイトにコードを追加ステップ" width="2588" height="1638" data-path="images/web-push/add-code-to-site.png" />
</Frame>

<Warning>
  OneSignal Service Worker ファイルをサーバーに直接アップロードする必要があります。[OneSignal Service Worker](./onesignal-service-worker) を参照してください。
</Warning>

### 2. GTM 変数を作成する

タグ全体で参照する値の GTM 変数を作成します。これによりハードコーディングを避け、セットアップの保守が容易になります。

**`ONESIGNAL_APP_ID` 変数を作成する**

1. GTM で、**Variables > New** に移動します。
2. **Constant** を選択します。
3. `ONESIGNAL_APP_ID` という名前を付けます
4. 値を OneSignal App ID に設定します。
5. 保存

<Frame caption="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="Google Tag Manager で OneSignal App ID 変数を作成する" width="2378" height="1656" data-path="images/web-push/gtm-app-id-variable.png" />
</Frame>

<Check>
  GTM のどこでも `{{ ONESIGNAL_APP_ID }}` を使用して App ID を参照できるようになりました。
</Check>

**`ONESIGNAL_EXTERNAL_ID` 変数を作成する（推奨）**

ユーザーを外部識別子と関連付ける場合（たとえば、データベースまたは認証システムからのユーザー ID）、この変数を使用します。

サイトで値がどのように利用可能かに基づいて変数タイプを選択します。一般的なオプション：

* Data Layer Variable（推奨）
* First-Party Cookie
* DOM Variable（高度）

### 3. OneSignal init タグを作成する

1. GTM で、**Tags > New** に移動します
2. タグに名前を付けます：`OneSignal - Init`
3. Tag Type：**Custom HTML**
4. 以下のコードを貼り付けます。
5. **Advanced Settings > Tag firing options** で、**Once per page** に設定します。
6. **Triggering** で、**Initialization - All Pages** を選択します。

```html HTML theme={null}
<!--
  OneSignal – Google Tag Manager を使用した Web SDK 初期化

  このスニペット：
  - OneSignal Web SDK を読み込みます
  - App ID で OneSignal を初期化します
  - サブスクリプションベル（notifyButton）を有効にします

  ほとんどのサイトでそのまま動作します。
-->

<!-- 1. OneSignal Web SDK (v16) を読み込む -->
<!-- このスクリプトは OneSignal を利用可能にしたいすべてのページで読み込む必要があります -->
<script
  src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js"
  defer>
</script>

<script>
  // GTM dataLayer が存在することを確認
  // ここでは「OneSignalInitialized」イベントをオプションでプッシュするためにのみ使用
  window.dataLayer = window.dataLayer || [];

  // OneSignalDeferred は SDK が完全に読み込まれた後に実行されるキュー
  window.OneSignalDeferred = window.OneSignalDeferred || [];

  // 2. SDK の準備ができたら OneSignal を初期化
  window.OneSignalDeferred.push(function (OneSignal) {

    OneSignal.init({
      /*
        必須
        OneSignal App ID を GTM 変数として設定することを推奨します。
        OneSignal ダッシュボードの以下で確認できます：
        Settings > Keys & IDs
      */
      appId: "{{ONESIGNAL_APP_ID}}",

      /*
        オプション – SERVICE WORKER がルートにない場合のみ必要

        service worker が以下にホストされている場合：
          /OneSignalSDKWorker.js

        …serviceWorkerPath や serviceWorkerParam を設定すべきではありません。

        service worker がサブディレクトリにホストされている場合のみ、
        以下のオプションのコメントを解除して更新してください
        （例：/push/onesignal/）。
      */

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

      /*
        オプション
        OneSignal サブスクリプションベル（notifyButton）を有効にします。
        ユーザーが通知を購読または購読解除できるようにします。
        その他のプロンプトオプションについては：https://documentation.onesignal.com/docs/en/permission-requests
      */
      notifyButton: {
        enable: true
      }
    })
    .then(function () {
      // OneSignal の初期化成功
      console.log("[OneSignal] init success");

      // 推奨：他のタグをトリガーするために GTM にイベントをプッシュ
      window.dataLayer.push({
        event: "OneSignalInitialized"
      });
    })
    .catch(function (e) {
      // 初期化失敗（無効な App ID、service worker の欠落など）
      console.log("[OneSignal] init failed", e);
    });
  });
</script>
```

<Frame caption="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="Google Tag Manager で OneSignal - Init タグを設定する" width="2442" height="2296" data-path="images/web-push/gtm-init-tag.png" />
</Frame>

<Warning>
  同意バナー / CMP を使用している場合は、以下の [Consent Mode とプライバシーに関する考慮事項](#consent-mode-and-privacy-considerations)オプションを参照してください。
</Warning>

### 4. External ID とタグを設定する

[External ID](./users#external-id) の設定はオプションですが、推奨されます。デバイス間でユーザーを識別し、バックエンドと同期できるようになります。

**`ONESIGNAL_EXTERNAL_ID` を dataLayer にプッシュする**

この例では、GTM が `ONESIGNAL_EXTERNAL_ID` 変数（ステップ 2 で作成）を介して読み取れるように、ユーザー ID を dataLayer にプッシュする方法を示します。

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

  // データベースまたは認証システムからユーザー ID を取得します。
  // これが文字列値であることを確認してください。
  var userId = "your_user_id_here";

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

**External ID を設定する GTM タグを作成する**
タグの設定：

* タグ名：`OneSignal – Set External ID`
* タグタイプ：**Custom HTML**
* Tag firing options：**Once per page**
* Trigger：
  * `OneSignalInitialized` のカスタムイベントトリガーを作成（上記の **OneSignal - Init** タグで設定）し、
  * オプションでページ読み込み時にユーザー ID が利用可能であることがわかっている場合。

<Warning>
  External ID を設定するために必要なメソッドは `OneSignal.login(externalId)` です。ここで `externalId` は文字列です。

  `{{ONESIGNAL_EXTERNAL_ID}}` が空（または GTM が "undefined" / "null" に置換した場合）、login 呼び出しはスキップされ、External ID は設定されません。これは一般的な GTM のタイミング問題です。
</Warning>

<CodeGroup>
  ```html External ID を設定する基本的な例 theme={null}
  <script>
    // OneSignalDeferred は OneSignal SDK の準備が整った後にこれが実行されることを保証します
    window.OneSignalDeferred = window.OneSignalDeferred || [];

    window.OneSignalDeferred.push(function (OneSignal) {
      /*
        Google Tag Manager から External ID を読み取ります。
        これは GTM 変数（Data Layer Variable または Custom JS Variable）であるべきです。
      */
      var externalId = "{{ONESIGNAL_EXTERNAL_ID}}";

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

      /*
        基本的な検証：
        - GTM は undefined/null を文字列として置換する可能性があります
        - OneSignal.login は文字列を必要とします
      */
      if (!externalId || externalId === "undefined" || externalId === "null") {
        console.log("[OneSignal] External ID missing, skipping login");
        return;
      }

      // External ID がクリーンな文字列であることを確認
      externalId = String(externalId).trim();

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

      /*
        External ID を使用してユーザーを OneSignal にログインします。
        これにより、現在のブラウザ/デバイスがこのユーザーにリンクされます。
      */
      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 External ID が設定されない場合に試す高度な例 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>

#### タグを設定する

Web SDK を使用して [OneSignal タグ](./add-user-data-tags) を送信します。

タグの設定：

* 名前：`OneSignal - Add Tags`
* Tag Type：**Custom HTML**
* Tag firing options：**Once per page**
* Trigger：
  * `OneSignalInitialized`、および
  * タグデータが利用可能な条件（例：ログイン後、プロフィールページ、購入後）。

このコードを貼り付けて、サンプルのタグ TAG と VALUE を置き換えます。

```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> ユーザーデータが利用可能な場合にのみタグを送信してください（例：ログイン後、プロフィールの読み込み後、または既知のコンバージョンイベント後）。</Note>

### Consent Mode とプライバシーに関する考慮事項

サイトで Consent Mode / CMP を使用している場合、OneSignal をいつ読み込むかを決定します：

* 同意後のみ（EU / UK で一般的）、または
* 即座に（デフォルトで「機能的」ストレージが許可されている場所で一般的）。

GTM は、Consent Initialization トリガーとタグレベルの同意コントロールをサポートしており、ユーザーの同意に基づいてタグの動作を管理します。ただし、OneSignal は SDK がいつ読み込まれるかを制御するためのプライバシー同意メソッドも提供しています。

* [個人データの取り扱い](./handling-personal-data)
* [Web SDK プライバシーメソッド](./web-sdk-reference#privacy)

***

## テスト

1. GTM でプレビューモードを開きます。
2. サイトを読み込んで確認します：
   * `OneSignal - Init` が一度発火します。
   * `OneSignalInitialized` が GTM イベントタイムラインに表示されます（イベントプッシュを保持した場合）。
3. ウェブサイトに登録します。プロンプトの詳細については、[Web パーミッションプロンプト](./permission-requests) を参照してください。
4. OneSignal ダッシュボードで、**Audience > Subscriptions** に移動し確認します：
   * オプトイン後にサブスクリプションが表示されること。
   * External ID を設定した場合、External ID が表示されること。
5. **Messages > New Push** からテストプッシュを送信します。

<Check> 初期化が機能している場合、オプトイン後に OneSignal にサブスクリプションが表示されます。</Check>

### トラブルシューティング

* Init タグが発火するが、SDK が読み込まれない
  * Content Security Policy（CSP）が `https://cdn.onesignal.com` をブロックしていないか確認してください。
  * 広告ブロッカー / スクリプトブロッカーを確認してください。

* `dataLayer` エラー
  * `dataLayer.push()` 呼び出しの前に `window.dataLayer = window.dataLayer || []` が設定されていることを確認してください。

* 重複プロンプト / 重複 SDK 読み込み
  * サイトコード、CMS プラグイン、または別の GTM タグ経由で OneSignal を読み込んでいないことを確認してください。

* Add Tags が実行されるが OneSignal に表示されない
  * Trigger Group が `OneSignalInitialized` を待っていることを確認してください。
  * ユーザーアクショントリガーが実際に発火していることを確認してください。
  * タグが有効なキー / 値のペアであり、[プラン制限](https://onesignal.com/pricing) 内であることを確認してください。

<Warning>
  それでもヘルプが必要な場合は、[Web SDK トラブルシューティング](./troubleshooting-web-push) を参照して、一般的な修正方法を確認してください。
</Warning>

## 次のステップ

* [Web パーミッションプロンプト](./permission-requests)
* [タグ](./add-user-data-tags)
* [Web SDK リファレンス](./web-sdk-reference)

***
