3つの会社でUTMトラッキングをエンドツーエンドで配線しました。毎回、同じ5つのことが同じ順序で壊れ、毎回修正は同じでした:テンプレートをキャンペーンレベルまで引き上げ、コンバージョン転送をサーバーに押し込み、その間にドライランを挟む。これがこの記事のほとんどです。残りは考えていなかったことを発見するQAチェックリストです。
これにはCustomer Data Platformは必要ありません。「4つの匿名のタッチを3つのデバイスにわたって1つのカスタマージャーニーに縫い合わせる」というアトリビューションの問題になった場合はいずれ必要になりますが、最もよく見るケース - 「すべてのアウトバウンドリンクに一貫してタグを付け、クリックをキャプチャし、コンバージョンをMetaとGA4にサーバーサイドで転送し、Safariを乗り越える」- では、テンプレート付きのURLショートナーとコンバージョンAPIで仕事が完了します。以下が機能するバージョンで、見てきた障害モードを指摘しています。
UTMトラッキングで何が壊れるか#
私が仕事をするマーケターはUTMが下手なわけではありません。問題はツールが1度のUTM入力を簡単にし、組織全体に適用することを難しくし、ローンチ後に修正することを不可能にするよう設計されていることです。4つの障害モードが繰り返し現れます。
ドリフト。 ある人が utm_source=newsletter と入力し、別の人が utm_source=Newsletter と入力し、3人目が utm_source=email と入力します。6ヶ月後、GA4の「newsletter」チャンネルが9つの文字列バリアントに分かれています。後から整理するのは正規表現と祈りの作業です。この規約を導入したオリジナルの urchinTracker() スクリプト - 2003年に一時的にオープンソース化された後に吸収されたGoogleのAnalytics以前のUrchinウェブ解析製品 - にもテンプレートレイヤーはありませんでした。規約は常に「一貫して入力する」でしたが、ツールはそれを強制しませんでした。
大規模な手動タグ付け。 4つの地域のストアフロントにわたる80の短縮リンクを持つフライヤーキャンペーンは、入力し、スプレッドシートに貼り付け、ショートナーにコピーして祈る320のURLです。その半分が間違った utm_content を得ます。キャンペーンが2週間経つまで誰も気づきません。
サーバーサイドのコンバージョンのギャップ。 ピクセルがサンクスページで発火し、GA4が取り込み、Metaが取り込み、帰宅します。次にSafariが別のITPバージョンをリリースし、広告ブロッカーのインストールが増加し、報告されたコンバージョンが3分の1減少します。AppleのITP 2.3リリースノートは正確なメカニズムを説明しています:リンクデコレーションはスロットリングされ、document.referrerは削除され、ブラウザがサードパーティのJSを実行することに依存するアナリティクスフローは静かに劣化します。コンバージョンはサーバーでまだ起きています。ただ広告サーフェスに届いていないだけです。
ドライランなし。 新しいパイプラインを流れる最初のコンバージョンが最初の本物の買い物客です。設定ミスがあると3日後に、最適化アルゴリズムがすでに実際に機能していたキャンペーンから予算を引いた後に気づきます。
この記事では最初の3つをテンプレート + 一括インポート + サーバーサイド転送で、4番目をスキップしやすくスキップすると高くつく検証ステップで対処します。
ワークスペースとキャンペーンのUTMテンプレート#
テンプレートは一貫性の問題をスタックの上に押し上げます。ワークスペースレベルでタグ付け規約を1度定義し、warranted な場所でキャンペーンごとのオーバーライドを重ね、すべてのリンクに継承させます。タイポが住む場所がなくなります。
まずワークスペースのデフォルトを定義します。リテラル値は組織で決して変わらない変数を固定します(ニュースレターキャンペーンには utm_medium = email)。プレースホルダーは作成時にリンクペイロードから埋められます:
curl -X PUT \
https://api.elido.app/v1/workspaces/1/utm-template \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-d '{
"utm_source": "{{ channel }}",
"utm_medium": "{{ medium }}",
"utm_campaign": "{{ campaign }}",
"utm_content": "{{ creative }}",
"utm_term": "{{ audience.segment }}"
}'
ここで重要な詳細がいくつかあります:
- プレースホルダーはクリック時ではなくリンク作成時に埋められます。アナリティクスツールに届くのは、リンクが作成された瞬間に意図されたものです - リンクの転送先がクリック時に計算したものではありません。これにより、6ヶ月後に何かがおかしく見える時の監査ログの再構築がはるかに簡単になります。
- 未知のプレースホルダーは早期に失敗します。一括インポートに
creativeカラムがなく、ワークスペーステンプレートが{{ creative }}を参照している場合、APIは未解決の変数名と共に422を返します。静かな部分適用はありません。 - リンクのタグ配列から読み取る
link.tag.<name>プレースホルダー(クライアント識別子をすべてのURLに埋め込む必要があるマルチテナントエージェンシーに有用)を含む完全なテンプレートリファレンスはドキュメントガイドにあります。
その後、キャンペーンテンプレートを重ねます。キャンペーンはワークスペースから継承し、キャンペーン固有のサブセットを置き換えます:
curl -X POST \
https://api.elido.app/v1/campaigns \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-d '{
"name": "Spring 2026 - DACH",
"utm_template": {
"utm_campaign": "spring_2026_dach",
"utm_term": "{{ audience.locale }}"
}
}'
キャンペーンに設定されていないものはワークスペースのデフォルトにフォールスルーします。2レベルの階層はほとんどの実際の組織構造をカバーします:ワークスペースレベルの共有規約、キャンペーンレベルのチーム固有または季節固有のオーバーライド。3つ目の継承レベルが欲しくなったとき、それは臭い - 通常、2つのキャンペーンをよりスマートなプレースホルダー値を持つ1つのキャンペーンにすべきことを意味します。
リンクごとのオーバーライドが諦めること:オーバーライドはテンプレートに関わらず発火します。保持すること:オーバーライドはアクター + タイムスタンプ + 解決値対最終値の差分と共に監査ログに記録されます。6ヶ月後、200リンクのキャンペーンの1つのリンクが utm_term=manual_override を持つ理由を誰かが尋ねるとき、答えることができます。
Sheetsからの一括インポート - マーケターが実際に使うワークフロー#
マーケターは一日中 curl を叩いているわけではありません。キャンペーンブリーフは転送先URLとキャンペーンメタデータを含むスプレッドシートとして届き、ローンチの締め切りは金曜日で、そのスプレッドシートが同じUTM文字列を200回入力せずに200の短縮リンクになる方法が問題です。
CSVのカラム名はテンプレートのプレースホルダー名に一致します(大文字小文字を区別しない)。Elidoが認識しないカラムは黙ってコピーされるのではなく、警告と共にドロップされます - これは意図的です。黙ってコピーすることで、誰かが内部メモのカラムを追加したために utm_brand_color がGA4に現れる結果になります。
destination_url,channel,medium,creative
https://shop.example.com/de,newsletter,email,hero_a
https://shop.example.com/fr,newsletter,email,hero_a
https://shop.example.com/de,paid_social,meta,carousel_v2
https://shop.example.com/fr,paid_social,meta,carousel_v2
multipartとしてPOSTします:
curl -X POST \
https://api.elido.app/v1/links/bulk \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-F "csv=@launch_q2.csv" \
-F "campaign_id=cmp_8a2f"
この検証フローが1リンクずつのUIでは得られない2つのものを提供します:
- オールオアナシングのコミット。 単一の不正な行がアップロード全体を中断し、問題のある行番号と理由を返します -
row 47: unresolved variable {{ creative }}は金曜日の午後4時に200リンクのうち47がプレースホルダー文字列に解決されたと発見するよりはるかによいエラーです。 - ローンチ前プレビュー。 ダッシュボードの一括インポートプレビュー行はコミット前に、レンダリングされた
utm_*クエリ文字列を含む解決されたURLを表示します。テンプレートが期待通りのことをしたことを確認するために2番目のリンクを見て、ファイルの下の行がドリフトしなかったことを確認するために最後のリンクを見ます。2回の一瞥、1分。
スプレッドシートに安定した形状がない場合 - カラムの順序が変わり、ヘッダーが名前変更される - 一括インポートエンドポイントは不快になります。修正は私たちのツールにはありません。修正はキャンペーンブリーフのCSVスキーマにコミットし、スキーマのドリフトをプロセスのバグとして扱うことです。より広いパターンについてはマーケターのソリューションページで説明しています。
Meta CAPIとGA4へのサーバーサイドのコンバージョン転送#
ピクセルのみのアトリビューションは、Safari ITP、広告ブロッカー、コンセントバナーでコンバージョンの20〜40%を失います。数値は業界によって異なります - DTC ecommerceは範囲の上限、B2B SaaSは下限 - しかしiOS 14後に私が見たすべての測定値は、広告プラットフォームが想定する95%マークをピクセルの信頼性が大幅に下回っています。最適化アルゴリズムはよりノイジーな入力を得て、CPAは実際より悪く見えます。
MetaのConversions APIドキュメントはこれについて明確です:サーバーイベントが欲しいものであり、ブラウザサイドのピクセルはサプリメントです。GA4のMeasurement Protocolも同じ主張をしています。両プロトコルは同じ形状を受け入れます:コンバージョンの詳細を含むサーバーサイドのイベント、重複排除のためのevent_id、そして理想的にはプラットフォームがコンバージョンを既知の訪問者に縫い合わせられるようにハッシュ化されたユーザー識別子。
ギャップを埋めるプランビングは機械的です。3つのステップ。
ステップ1 - click_idをキャプチャする。 すべてのElidoのリダイレクトレスポンスには X-Elido-Click-Id ヘッダーが付いています。TS / Python / Go SDKはリダイレクトレスポンスオブジェクトでそれを公開します。生のHTTPでも機能します:
curl -sI https://elido.me/launch | grep -i click-id
# X-Elido-Click-Id: clk_01HYZ7T8WV6KQX3M
転送先ページのファーストパーティCookieに保存します(elido_click_id、90日のTTL - 典型的なSaaS評価をカバーするのに十分な長さ、ePrivacyガイダンスを満たすのに十分な短さ)。チェックアウト時に読み直します。
ステップ2 - 転送先をワイアリングする。 転送したいサーフェスの認証情報をPUTします。サブセットのみでも機能し、欠けているサーフェスは黙ってスキップされます:
curl -X PUT \
https://api.elido.app/v1/workspaces/1/conversion-forwarding \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-d '{
"meta_capi": {
"pixel_id": "1234567890",
"access_token": "EAA…",
"test_event_code": null
},
"ga4_mp": {
"measurement_id": "G-ABC123",
"api_secret": "abc_def_ghi"
},
"mixpanel": {
"project_token": "pm_…",
"service_account": "[email protected]"
}
}'
ステップ3 - コンバージョンをPOSTする。 注文が発火したとき、click_idと注文の詳細を含むイベントを送信します。event_id がべき等性のキーです:
curl -X POST \
https://api.elido.app/v1/conversions \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-d '{
"click_id": "clk_01HYZ7T8WV6KQX3M",
"event_name": "purchase",
"event_id": "ord_98231",
"value": 89.00,
"currency": "EUR",
"user": {
"email": "[email protected]",
"phone": "+4915123456789",
"external_id": "cust_5128"
}
}'
ユーザーアイデンティティフィールドは、MetaとGA4への転送前にSHA-256でハッシュ化されます - これが両プラットフォームの要件です。UTMコンテキストは click_id にマッチするクリック行から取り出されます。そのため、転送されたイベントは、ユーザーがチェックアウトする前に1時間サイトを巡回していても、元のキャンペーンアトリビューションを持ちます。返金処理とマルチタッチアトリビューションモデルトグルを含む完全な仕組みはコンバージョン転送ガイドにあります。
これでギャップのほとんどが埋まります。残りのホールがあります - click_id Cookieをブロックする訪問者や、Elido以外のパスで来る訪問者 - しかし実際にトラフィックを誘導するキャンペーンについては、「ピクセルの信頼性60〜80%」から「サーバーの信頼性95%以上」に移行しました。
監査ログが助けてくれる3つのエッジケース#
テンプレートと転送はハッピーパスを処理します。以下のケースは、非自明なキャンペーンの3週目に現れます。これらすべてへの正しい答えは、より複雑なテンプレートを設計しようとすることではなく、監査ログとコンバージョンパネルにあります。
返金。 購入コンバージョンが発火し、顧客が1週間後に商品を返品し、報告された収益が8%多くなっています。修正は event_name: "refund" で同じ event_id をPOSTすることです。MetaとGA4はこれをオリジナルに対するネガティブなコンバージョンとして扱います。Mixpanelはファネルで引き算する別のイベントとして記録します。event_id がこの形になっている理由:イベントIDレベルのべき等性は返金を二重カウントできないことも意味します。完全なパターンはコンバージョン転送ガイドのエッジケースセクションに文書化されています - 返金、部分返金、店舗クレジットはそれぞれわずかに異なる形状を持ちます。
click_idのミス。 既知のクリックとマッチしない click_id でコンバージョンが発火します - タイポ、リテンション超過、間違ったワークスペース。コンバージョンはまだワークスペースに対して記録されますが、空のUTMコンテキストで転送されます。これは意図的です:キャッチオールのアトリビューションはコンバージョンを落とすより有用で、監査ログの click_id_unknown フラグはレポート時に帰属されないスライスでフィルタリングできます。スライスがコンバージョンの5%を超える場合、転送先ページでclick_idを永続化する方法に何か問題があります - 通常、CookieのSameSite属性またはパスのスコープです。
遅れて届くコンバージョン。 B2B SaaSの成約は元のクリックから47日後に来ます。Elidoのデフォルトのクリックリテンションは30日間なので、コンバージョンが発火する時点でクリックが期限切れになっており、上記のclick_id-missケースになります。販売サイクルによって2つの修正があります:ワークスペースのリテンションを90日に増やす(Proプラン以上)、またはclick_idを長期間有効なファーストパーティ識別子(顧客レコードの original_click_id カラム)にキャプチャして、Cookieが消えてもコンバージョン時に縫い合わせられるようにします。本番環境で両方のパターンを見てきました。
監査ログはリンクごとの解決値対最終値のUTM差分、コンバージョンごとの転送先ごとの転送レスポンスコード、click_id対コンバージョンの結合状態を示します。最適化アルゴリズムがパフォーマンスが低いように見えるキャンペーンから予算を引いたとき、監査ログが「いいえ、キャンペーンは問題ありません。ローテーションされたGA4 api_secretへの3日間の転送損失がありました」と言えるものです。見てください。
ローンチ前のQA - パイプライン全体のドライラン#
最初にこのパイプラインを流れるコンバージョンを本物の買い物客にしないでください。30分のドライランのコストはすべてあなたが負担します。設定ミスのあるパイプラインのコストは、最適化アルゴリズムが2日間、あなたが最も高いパフォーマンスをしているキャンペーンから予算を引いてから気づくことで負担されます。非対称性は悪いです。
3つのステップ、順番に。
一括インポートのドライラン。 一括インポートエンドポイントはクエリパラメータとして dry_run=true を受け入れます。検証を実行し、テンプレートを解決し、コミットせずに作成されるはずのリンクを返します。任意のJSONビューアでレスポンスを開きます。すべての行の解決されたURLが見えます。3〜5行をスポットチェックします:2番目のリンク、最後のリンク、ワークスペースのデフォルトをオーバーライドした行。utm_* クエリ文字列がキャンペーンブリーフが言うべき正確なものであることを確認します。
コンバージョン転送テストモード。 Meta CAPIは test_event_code パラメータを受け入れ、Events Managerのプロダクションの代わりにTest Eventsタブにイベントをルーティングします。ワークスペースの転送設定に設定し、10〜20のサンプルコンバージョンを送信し、到達することを確認します。GA4についても同様です:イベントに debug_mode: true を設定してDebugViewで確認します。どちらもリアルタイムです。ポイントはAPIが機能することをスポットチェックすることではありません。設定ミスした pixel_id やローテーションされて更新されなかった api_secret を発見することです。
エンドツーエンドのスモーク。 クリーンなブラウザセッションから本物の短縮リンクの1つをクリックします。Elidoダッシュボードのrecent-clicksパネルでクリックを見ます。何かを購入したふりをして - ターミナルからそのclick_idで purchase コンバージョンをPOSTします。コンバージョンが正しいUTMコンテキスト付きでMeta Test EventsとGA4 DebugViewに表示されることを確認します。一度やってしまえばループ全体が10分以内です。
3つすべてがパスしたら、test_event_code を削除し、debug_mode: false を設定し、リリースします。最初の本物の買い物客にはクリーンなパイプラインが待っています。
実際にCDPが必要になる場合#
テンプレート + 一括インポート + サーバーサイド転送でほとんどの方法ができます。それでは対応できない問題のクラスがあり、CDPに手を伸ばすのが正しい選択です。
クロスデバイスのアイデンティティスティッチング。 訪問者がモバイルでリンクをクリックし、コンバートせず、デスクトップで戻ってきてサインアップします。両方のタッチを同じ人物に帰属させたいです。UTMトラッキング + click_idはタッチレベルです。2つのタッチを1つのジャーニーにするユーザーアイデンティティレイヤーは、CDP(Segment、mParticle、RudderStack)が構築されている目的です。Elidoは訪問者ごとに最大30日間のクリックを保存し、そのウィンドウ内でラストタッチ / ファーストタッチ / ポジションベースのアトリビューションをサポートしますが、クロスデバイスの結合には意図的に運用しないアイデンティティグラフが必要です。
100ms以下のパーソナライゼーション。 リアルタイムで訪問者の以前のタッチに基づいて転送先ページをレンダリングしている場合 - フィーチャーストアからコホートを取得してヒーローのヘッドラインを変える - アイデンティティ解決はレンダリングに近い必要があります。それはCDPのテリトリーです。より頻繁には、PostHogやLaunchDarklyのような実験プラットフォームが上に重ねられます。
大規模なマルチタッチアトリビューション。 ほとんどのキャンペーンにはラストタッチで十分です。販売サイクルが4ヶ月にわたる6回のタッチを持ち、それぞれをクレジットする本当の必要性がある場合、マルコフ連鎖またはシャープレー値のアトリビューションが重要になるテリトリーにいます。Elidoはラストタッチ / ファーストタッチ / ポジションベースを行います。より洗練されたものは、適切なアイデンティティグラフとモデルレイヤーを持つツールが必要です。
それ以外のすべて - そして「それ以外のすべて」が私が仕事をしたほとんどのマーケティングチームです - には、テンプレート + 一括インポート + サーバーサイド転送パターンで十分です。ワークスペーステンプレートを一度設定し、ローンチごとにキャンペーンテンプレートを設定し、プラットフォーム統合ごとに転送設定を一度設定し、ローンチ前にドライランを実行します。4つすべてを行えば、私が監査したマーケティングチームの80%よりも堅固なUTMパイプラインを持つことになります。
一度構築し、各ローンチ前にドライランし、次のキャンペーンに進みます。
ブログの関連記事#
Elidoを試す
URLを貼り付けて短縮リンクを取得
登録不要。リンクは30日間有効。永久に保存するには登録してください。
Free、登録不要 · 1日あたり2件