Meta広告のコンバージョンAPI(CAPI)をサーバサイドGTMとGA4を使って実装する

自社でMeta（旧：Facebook）のコンバージョンAPIの導入をした際の備忘録です。
TikTokのイベントAPIと仕様がよく似ていますので、TikTokのイベントAPIの実装を進めている方にも参考になると思います。あわせて[こちらの記事](https://qiita.com/Shuni/items/587f37d978157d0fe68c)もご覧ください。

## コンバージョンAPIについて

* コンバージョンAPI（以下CAPI）とは、一言で言うと....
    * 広告主が持っている顧客データ（例えば氏名や電話番号など）を
    * Facebookの保有する顧客データと突合することで
    * 広告クリック後のサイト内におけるユーザ行動をCookieを使わず捕捉することができる仕組み

欧州を中心とした[Cookieの利用規制の潮流](https://qiita.com/Shuni/items/bc4862d4bd9ad6fd0c18)の中で
ウェブ広告によるユーザのトラッキングに対する規制がとても厳しくなっています。

AppleやGoogleといったプラットフォーマーがCookieを使わないCV計測手段を提供し始めるなかで
Metaが提供する計測支援システムがCAPIというわけです。

https://anagrams.jp/blog/facebook-conversions-api/

今回は、そんなCAPIをサーバサイドGTM（ssGTM）を用いて実装します！

### サーバサイドGTM（ssGTM）とは

今までのウェブ用コンテナではなく、GCPなどの仮想サーバ上に構築するGTMコンテナ。
よりセキュアでユーザ環境に負荷が小さいのが特徴です。

> <a href="https://developers.google.com/tag-manager/serverside/intro?hl=ja"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/b0931494-e16f-3624-9141-1975812cc4dc.png" alt="https://developers.google.com/tag-manager/serverside/intro?hl=ja"></a>

* `sGTM`とも、`Server-side Tagging`とも言う
* 2021年10月に新たに正式版がリリース
* 従来のGTMとは異なり、GCP等の仮想サーバ上に構築してタグを発火させる
* 大前提、従来のGTMを代替するものではなく、セットで用いることになる

### ssGTMでCAPIはどのように動くのか

通常のFacebook広告では、PageViewやPurchaseといった広告経由のイベントは`Pixelタグ`で計測されていました。
この`Pixelタグ`は、通常、従来のGTM等を用いてサイト内に埋め込まれます。

ここで使われている3rd Party CookieはOSやブラウザによってはブロックの対象となり、
コンバージョン等のサイト内行動はFacebookでは十分に捕捉できないことがありました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/77a550fa-6d12-a2e2-35a5-7997448a9b5e.png)

しかし、CAPIでは、広告主のサーバー（サブドメイン）にデプロイされたサーバサイドGTM（ssGTM）から、
Facebookの広告サーバに直接、3rd Party Cookieを用いない形で送信します。

こうして送られたイベントデータには、広告主の保有する顧客データを暗号化（ハッシュ化）した形で含むため
Meta自身が保有する顧客データと突合することで、ユーザを特定（＝イベントを計測）できるんですね！

### 重複構造と部分重複構造

いざ、CAPIを実装する際には、どのイベントをサーバサイドから送信するかによって実装工程や難易度が変わります。
その実装方法には、大きく`重複構造`・`部分重複構造`・`分割構造`に分けることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/bf103281-d302-c3da-20f8-53cd935b02a9.png)

現状、多くの広告主が、従来のGTM（ウェブコンテナ）と`Pixelタグ`を用いて
「PageView」や「Purchase」といった標準イベントやカスタムイベントを定義していると思います。

`重複構造`では、それら全てのイベントがCAPIと`Pixelタグ`両方で発火している環境を作ります。
それぞれのイベントが一意の`event_id`で紐づいていることにより、異なるサーバから送信されてもFacebookで同一と識別することができます。

一方、`部分重複構造`では、全てのイベントを`Pixelタグ`で実装した上で、
例えばコンバージョンに近いイベントのみCAPIを介してFacebookにデータを送信します。
実装工数や難易度がそこまで高くなく、最低限、大事なイベントのみ確実に計測することを重視します。


#### 重複構造のメリット
* 全ての`Pixelタグ`のイベントで計測の補完ができる

#### 重複構造のデメリット
* `event_name`と一意の`event_id`を使って、`Pixelタグ`と`CAPIタグ`間の重複排除が必要
* マーケターだけで実装するにはやや難易度が高い
* 実装に開発コスト・サーバ代として費用がかかる
* PageViewなど、発火回数が多いイベントによってサーバ負担がかかる

:::note info
Facebookは`重複構造`を推奨していますが、ハードルが高ければ`部分重複構造`や`分割構造`から始めてもいいと思います。
特に一番計測したいポイント（予約など）にCAPIが入っていれば、ある程度の効果が見込めるはずです。
:::

`分割構造`という手段もありますが、CAPIの恩恵を受けづらいことから、今回は`重複構造`を使って実装することにしました。

# デプロイしてみる

今回はサーバサイドGTM（ssGTM）と`CAPIタグ`を用いて、`重複構造`を構築するところまでやってみます！
構成図はざっくりこんなイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/0df3fe50-d899-31c5-becb-9f69855dcd17.png)


## 実装手順

1. ビジネスツール利用規約の同意
1. GoogleAnalytics4とssGTMの構築
1. GTM（ウェブコンテナ）の設定
1. ssGTM（サーバコンテナ）の設定
1. 発火・データ送信テスト

## ビジネスツール利用規約の同意

CAPIを用いる際は、[Metaビジネスツール利用規約](https://www.facebook.com/legal/terms/businesstools)が適用されます。
場合によってはプライバシーポリシー等の見直しが必要になります。

:::note warn
特に、2022年4月から新たに施行される改正個人情報保護法では
広告配信のために顧客情報を利用する広告主に対して、より厳格な規制がなされています。
:::

## GoogleAnalytics4とssGTMの構築

今回のサーバサイドGTM（ssGTM）は`GoogleAnalytics4（GA4）`とセットで実装します。
既存のGoogleAnalytics（`UA`）とGTMを利用している方であれば、GA4の設定自体はポチポチで進みます。
後で、このデータ送信経路ををウェブ用コンテナ経由から、サーバサイド経由に変更します。

https://ayudante.jp/column/2021-01-26/11-00/

:::note info
既にGA4を導入している方であっても、既存のWebタグの`transport_url`フィールドを置き換えて
サーバサイドからデータストリームを送るのではなく、タグを新規作成することをお勧めします。
:::

サーバサイドGTMそのものの構築については、[弊社エンジニアの記事](https://qiita.com/takaHAL/items/38781b4f7d3f022508f1)を参考にしてください。

## GTM（ウェブコンテナ）の設定

GA4とサーバコンテナが構築できたら、まずは従来のGTM（ウェブコンテナ）の設定をしましょう。

当然のことですが、Metaに送られる顧客データはサーバコンテナの`CAPIタグ`から送信されます。
しかし、顧客データは変数としてウェブコンテナにあるGA4タグを介してサーバコンテナに転送するため、データレイヤー等の設定をする必要があります。

### パラメータ（変数）の設定

CAPIを通じてMetaにデータを送信する際には、下記4種類のパラメータを準備する必要があります。

| パラメータ | 内容 |
|:-----------|:------------|
| event_name | イベントの名称 | 
| event_time | イベントが発生した日時 |
| user_data | Metaに提供する顧客データ群|
| event_id | 一意のイベントID（重複構造の場合必須) |

これらのデータは、データレイヤー変数等を用いてウェブコンテナに送信します。
ウェブコンテナの`Google アナリティクス: GA4 イベント`タグに変数として一緒に持たせることで、サーバコンテナ側に送信します。

#### event_name（イベント名）

計測したいイベントを`Google アナリティクス: GA4 イベント`タグに設定します。

イベント名は、GA4のタグで設定した値が`CAPIタグ`でもそのまま利用されます。
`CAPIタグ`と`Pixelタグ`の`（部分）重複構造`を構築する場合は、既存のPixelタグで用いるイベント名と同一の名称を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/ff084699-8a8c-4094-8a0c-1c3e41242c92.png)

ちなみに、既にGA4用に作られている下記イベントは、自動的にFacebook用のイベント名に変換されて使用できます。

|イベント名(GA4タグ)|イベント名(FBタグで自動変換)|
|:----|:----|
|add_payment_info|AddPaymentInfo|
|add_to_cart|AddToCart|
|add_to_wishlist|AddToWishlist|
|begin_checkout|InitiateCheckout|
|generate_lead|Lead|
|page_view|PageView|
|purchase|Purchase|
|search|Search|
|signup|CompleteRegistration|
|view_item|ViewContent|

また、自分で作ったオリジナルのイベント名はそのまま`CAPIタグ`にインポートされます。

#### event_time（イベント発生時間）

`event_time`には、イベントが発生した時間を`UNIX時間`で格納します。
GTM上で`カスタムJavascript`の変数を生成すれば良いと思います。

```javascript:
function() {
  return Math.round(new Date().getTime() / 1000);
}
```

もし、CAPIで作成するイベントがGA4標準のヒットではない場合、
`CAPIタグ`が発火したタイミングを自動的に送信してくれるため、必ずしも`event_time`を設定する必要はありません。

#### user_data（顧客データ）

`user_data`には
例えば、`user_data`として、下記のようなデータを持たせてあげます。

| 項目           | Key Type          | Key Name          |
|----------------|-------------------|-------------------|
| メールアドレス | Email             | em                |
| 電話番号       | Phone Number      | ph                |
| 性別           | Gender            | ge                |
| 生年月日       | Date of Birth     | db                |
| 名             | Last Name         | ln                |
| 姓             | First Name        | fn                |
| 市             | City              | ct                |
| 外部ID         | External ID       | external_id       |
| IPアドレス     | Client IP address | client_ip_address |

データを送信する際には、ハッシュ化(SHA-256方式)が必須な項目が多いので注意。
また、他にも送信できるデータはあるので、Metaの公式サイトをチェックしてください。

> [Customer Information Parameters - Meta for Developers](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters)

最終的に、`user_data`として送られたデータは、Metaが保有する顧客データと突合します。
**顧客のマッチ率を高めるために、送信するデータの種類は多ければ多いほど良い**とのことです。

##### IPアドレスやOS・ブラウザ

他にも、IPアドレスやOS・ブラウザを上書きで定義することができます。
上書きしない場合は、サーバサイドのタグが自動的にIPアドレス・OS・ブラウザを転送して用います。

* `ip_override`（IPアドレス）
* `user_agent`（ユーザエージェント）

##### GA4との共通定義パラメータ

* `user_data.email_address`（メールアドレス）
* `user_data.phone_number`（電話番号）
* `user_data.address.first_name`（名）
* `user_data.address.last_name`（姓）
* `user_data.address.city`（市区町村）
* `user_data.address.region`（地域）
* `user_data.address.postal_code`（郵便番号）
* `user_data.address.country`（国）

> [一般的なイベントデータ  |  Google タグ マネージャー - サーバー側  |  Google Developers](https://developers.google.com/tag-manager/serverside/common-event-data?hl=ja)

上記のような、GA4用に共通定義されているパラメータは、そのまま利用可能です。
サーバ側のタグが自動的にハッシュ化してくれます。

##### Meta固有のパラメータ

また、Metaで固有の顧客データは決まったフィールド名で個別に設定する必要があります。
基本的には`x-fb-ud-`や`x-fb-ck-`で始まります。

* `x-fb-ud-ge`（性別）
* `x-fb-ud-db`（生年月日）
* `x-fb-ud-external_id`（顧客ID）
* `x-fb-ud-subscription_id`（サブスクリプションID）
* `x-fb-ck-fbp`（ブラウザID）
* `x-fb-ck-fbc`（クリックID）

:::note warn
これらのデータは自動的にハッシュ化されないので
変数として持たせるタイミングでハッシュ化する必要があります。
:::

#### event_id（イベントID）

イベントIDは、`重複構造`や`部分重複構造`など、`CAPIタグ`と`Pixelタグ`で同一イベントを送信する場合は実装必須です。
異なるソースから送信される同一イベントに付与することで、Metaが重複計測してしまわないように使われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/51c259c0-c120-fb5f-cd6b-19b80d11f3c0.png)

データレイヤー等を用いて一意な乱数を生成しなければならないため、`event_id`の実装はやや面倒です。特にウェブコンテナとサーバコンテナで同一のevent_idを持たせなければならないため、Javasrcript変数で乱数生成を生成しても、呼び出される度に値が変わらない様にする必要があります。

そのため、サードパーティの`変数テンプレート`を使っても良いと思います。ページがロードされる度にユニークなIDが変数として生成されます。

#### カスタムデータパラメーター（任意）

広告の最適化に用いることができるパラメータも、GTMから持たせることができます。

* `currency`（通貨）
* `value`（金額）
* `ge`（性別）
* `db`（生年月日）

詳細は[公式ドキュメント](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/custom-data?locale=ja_JP)にて。

### Googleタグにパラメータを追加する

変数の準備ができたら、`Googleタグ`（旧：GA4設定タグ）を編集します。

~~先ほどの手順通り進めてあれば、`サーバーコンテナに送信する`にチェック、および測定IDとサーバーコンテナのURLが入力されている状態です。~~ サーバコンテナにデータを送信する際には、`server_container_url`というパラメータを追記して、サーバサイドGTMのURLを指定します。また、GA4にページビューのデータを送りたい場合は`send_page_view` = `true`を指定します。

2024.04 追記：GA4の設定タグは、`Googleタグ`に名称が変わりました。スクリーンショットはUIが古いのでご注意ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/4ce1128f-5aad-a1b9-a03f-8b973488ff7c.png)

ここで、パラメータ`first_party_collection`に`true`が格納されていることを確認してください。GA4に（GA4にとっての）外部サーバにユーザデータを送信しても良いことを明示するものです。

:::note warn
この設定は必ず顧客データの利用同意があることを確認した上で行ってください。
:::


続けて、ウェブコンテナに変数として定義した顧客データを、`Googleタグ`に設定していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/7a44de4c-23b4-6354-9a9e-9c6c0c8661fc.png)

#### ハッシュ化について

Metaに送信するデータのうち、いくつかはハッシュ化した上での送信が求められています。
サーバサイドGTMを用いる場合は、`CAPIタグ`がデータをハッシュ化してくれるため、Metaが復号可能な状態で受け取ることはありません。

ただし、`IPアドレス`や`fbc`など、一部ハッシュ化不可のパラメータもあります。
詳しくは、[公式ドキュメント](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters)を参照してください。


### PixelタグにもイベントIDを追加する

重複構造または部分重複構造を採用している場合、既存のタグにもイベントIDを持たせましょう。
GTMのカスタムテンプレートを使っている場合は、GUI上で設定することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/a4adaca1-0b4c-a9f2-f4cc-59925b3ad909.png)

また、HTMLでタグを設定している場合でも下記の通り、引数に持たせてあげます。

```JavaScript:
fbq('track', 'Purchase', {value: 12, currency: 'JPY'}, {eventID: '{{Event Id}}'});
fbq('trackCustom', 'ReserveWeb', {}, {eventID: '{{Event Id}}'});
```

> [ピクセルイベントとサーバーサイドイベントの重複除外 - コンバージョンAPI](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events?locale=ja_JP)

これで、先ほど設定したGA4タグと`Pixelタグ`には共通の`イベントID`を付与することができました。

## ssGTM（サーバコンテナ）の設定

ここからは、サーバサイドGTM（ssGTM）に移動して設定を進めていきます。

### Metaのタグ追加

まずはMeta広告の管理画面から、`イベントマネージャ`＞`データソース`と進みます。
そこから`Pixel ID`とコンバージョンAPI用の`アクセストークン`を控えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/2d5d12a2-89fb-10bd-760d-20741766a6bb.png)

次に、サーバサイドGTM（ssGTM）のカスタムテンプレートから`Conversions API Tag`を選択してください。
先ほど控えた値を`Pixel ID`と`API Access Token`に入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/7bccdb86-236c-4022-4262-2b38b10d5226.png)

トリガーとして、`Client Name`が`GA4`で発火するように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/5b613a64-8ab1-a387-f5ae-479aea8cde14.png)

これでサーバサイドのMetaタグの設定は完了です。

### GA4に送信しないパラメータの除外

先ほどウェブコンテナ上でGoogleタグに設定したパラメータは、自動的にGA4にも送信されます。
そのため、GA4に送信したくないデータはあらかじめ除外する必要があります。

除外設定は、サーバサイドGTMのGA4タグから行えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/29610d77-5dac-e7a9-f473-b51d6ebec946.png)

Google はユーザーのプライバシーを保護するため、Google が個人情報（PII）として
使用または認識できるデータを Google に送信することをポリシーで禁止しています。

## 発火・データ送信テスト

ここまで設定ができたら、一通り完了です。
ここからは、設定したCAPIが正しく発火しているかを試してみましょう。

### サーバサイドGTMのプレビュー

ウェブコンテナとサーバサイドGTMのプレビューを開いた上で、タグが発火するページにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/e878c430-f798-2bdc-665b-581eccfa6d1d.png)

あらかじめハッシュ化処理したデータがしっかりサーバサイドに受け渡されています。
キャプチャでは見切れてしまっていますが、`event_id`も発行されていました。

### Metaのテストイベント

再びMeta広告の管理画面から、`イベントマネージャ`＞`データソース`と進みます。
`テストイベント`タブから、`TEST`から始まるテストコードを控えてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/f7058835-7e36-0e0c-49ed-dc87e4e9adc0.png)

その値を、ウェブコンテナにある`Google アナリティクス: GA4 設定`タグに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/e88ffd9a-fe4a-291e-6610-2371fb34f09c.png)

同様に、サーバコンテナに作成したMetaタグにもテストコードを記入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/7bccdb86-236c-4022-4262-2b38b10d5226.png)

それぞれのタグ公開したうえで、`イベントマネージャ`＞`データソース`に戻ります。
テストイベントを実行してみると、しっかり同一イベントがブラウザとサーバで発火しており、重複処理されていますね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/98a65399-009b-24c7-1cb3-94e595c69ba9.png)

上記の赤で囲った部分が`event_id`です。
ブラウザとサーバ側で同じ値がしっかり付与されています。

また、CAPIを使ってデータを送った場合は`マッチングクオリティ`という指標が確認できます。
これは、パラメーターの有効性を示すスコアで、高いほど顧客獲得単価を削減できる可能性があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/65469360-eee7-264f-c430-63f51e9bbf45.png)

イベントの`マッチングクオリティ`のスコアは6.0以上を目標とします。
このキャプチャでは、いずれも7点台ですので及第点と言えるでしょう。

> <a href="https://www.facebook.com/business/help/308855623839366?id=818859032317965"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319121/53c5dc75-d2e6-4d1f-108b-c360cafdb612.png" alt="" width="60%"></a>

ちなみに、Metaでは、[優先的に送信すべきパラメータ](https://www.facebook.com/business/help/308855623839366?id=818859032317965)を開示しています。
やはりメールアドレスや電話番号の有用性は高いようです。

これで、一通りのサーバサイドGTMを用いた設定とテストは終わりです。

# まとめ

2021年10月に正式版がリリースされたサーバサイドGTM（ssGTM）を使ってCAPIを実装しました。

これで、Cookieが破棄されてしまったユーザのコンバージョンもある程度は補完できるのではないでしょうか。
手間とコストはかかるものの、ある程度の投資額がある広告主は対応しても十分に費用対効果が合う施策のはずです。

今回実装したCAPIは、Google広告でいうところの[拡張コンバージョン](https://support.google.com/google-ads/answer/9888656?hl=ja)に近い機能です。
Google広告を利用している方であれば、同時にサーバサイドGTMを用いてタグを仕込むことをオススメします。

https://qiita.com/Shuni/items/bc4862d4bd9ad6fd0c18

ターゲティングに関しては、引続きアフィニティやカスタマーマッチを用いながら最適化をしていくことになりそうです。
しかし、サーバサイドGTMを使ったタグの設定は、Cookie規制に対する計測面での回答と言えます。

本記事が少しでも参考になれば幸いです！

## 参考

https://www.facebook.com/business/help/2041148702652965?id=818859032317965

https://www.simoahava.com/analytics/facebook-conversions-api-gtm-server-side-tagging/

https://aitrigger.co.jp/blog/programmatic-advertising/facebook-conversions-api/

https://www.youtube.com/watch?v=JNResY4jJZA

https://anagrams.jp/blog/facebook-conversions-api/