Collect
Paid and social measurement
Paid and social measurement. One conversion, named once, then read by every pixel and every server API. This page builds the plan, the links, and the payloads. It sends nothing and stores nothing.
- 1Business KPI
- 2Conversion event
- 3Shared taxonomy
- 4Tags and server
- 5QA
Start with the measurement plan
Write the business number first. Revenue, qualified leads, or activated accounts. Then name the conversion that proves it. Then freeze the event names. Pixels come after that sheet exists.
The taxonomy is the only vocabulary. Every platform maps to it. Parameters that travel with every event: event_id, value, currency, and the item id when there is a catalog. event_id is shared by the browser tag and the server call so the platform can drop the duplicate.
Canonical names on this page are page_view, view_item, lead, sign_up, add_to_cart, begin_checkout, and purchase. GA4 recommended events use generate_lead for the lead. The sheet records that alias. People do not invent a second lead name in one tag.
| Canonical | When it fires |
|---|---|
| page_view | A landing page or other key page loads. |
| view_item | A product or offer is viewed. |
| lead | A form or demo request is submitted. |
| sign_up | An account is created. |
| add_to_cart | An item is added to the cart. |
| begin_checkout | Checkout starts. |
| purchase | An order is paid. |
Names on each platform
Google Ads, Microsoft UET, and X identify conversions by an id you create in the ad account. The cell says so. The other cells are the standard event names those platforms publish. LinkedIn still needs the conversion rule id even when the category has a standard word.
| Canonical | GA4 | Google Ads | Meta | TikTok | Snapchat | X | Microsoft | |||
|---|---|---|---|---|---|---|---|---|---|---|
| page_view | page_view | Conversion you name | PageView | KEY_PAGE_VIEW | ViewContent | PageVisit | PAGE_VIEW | Event id you create | Goal you name | PageVisit |
| view_item | view_item | Conversion you name | ViewContent | Rule you name | ViewContent | PageVisit | VIEW_CONTENT | Event id you create | Goal you name | ViewContent |
| lead | generate_lead | Conversion you name | Lead | LEAD | Lead | Lead | Custom, or SIGN_UP | Event id you create | Goal you name | Lead |
| sign_up | sign_up | Conversion you name | CompleteRegistration | SIGN_UP | CompleteRegistration | Signup | SIGN_UP | Event id you create | Goal you name | SignUp |
| add_to_cart | add_to_cart | Conversion you name | AddToCart | ADD_TO_CART | AddToCart | AddToCart | ADD_CART | Event id you create | Goal you name | AddToCart |
| begin_checkout | begin_checkout | Conversion you name | InitiateCheckout | START_CHECKOUT | InitiateCheckout | Custom event you name | START_CHECKOUT | Event id you create | Goal you name | Custom event you name |
| purchase | purchase | Conversion you name, with order id | Purchase | PURCHASE | Purchase | Checkout | PURCHASE | Event id you create | Revenue goal you name | Purchase |
- GA4. Recommended events. transaction_id on purchase is the order id.
- Google Ads. A conversion action you create, often imported from the GA4 event.
- Meta. Standard Pixel events. The same event_name goes to the Conversions API.
- LinkedIn. Category on the conversion rule. The API call sends that rule id.
- TikTok. Current standard names. CompletePayment still arrives, Purchase is the name to use.
- Pinterest. Checkout is the purchase event. There is no standard begin-checkout name.
- Snapchat. Standard event names are uppercase. A lead that is not a sign-up is a custom event.
- X. The event id comes from Ads Manager. The pixel and the API share it.
- Microsoft. A UET goal or custom event you name. Revenue goals take value and currency.
- Reddit. Standard pixel events. Begin checkout has no standard name.
Campaign links and click ids
utm_source is the place. utm_medium is the channel. utm_campaign is the effort, shared by every ad in it. utm_content is the creative. utm_term is the keyword. Lowercase, hyphens, no spaces, no email addresses.
Click ids are not UTMs. They are case-sensitive and they expire on the platform clock. Persist them in a first-party cookie or on the server session, first touch and last touch, and send the one the platform documents. Do not hash them. This page does not set that cookie.
| Channel | Medium | Source | Click ids |
|---|---|---|---|
| Paid search | cpc | google, bing, microsoft | gclid, gbraid, wbraid, msclkid |
| Paid social | paid-social | facebook, instagram, tiktok, linkedin, pinterest, snapchat, reddit, x | fbclid, ttclid, li_fat_id, epik, ScCid, twclid, rdt_cid |
| Display | display | the network name | the click id that network documents |
| the list or product name | none | ||
| Affiliates | affiliate | the partner slug | the partner click id, if they send one |
| Parameter | Platform | How to keep it |
|---|---|---|
| gclid | Google Ads | First-party cookie or the server session. The value is case-sensitive. |
| gbraid | Google Ads, app to web | Store it with gclid. It is the iOS app-to-web click. |
| wbraid | Google Ads, web to app | Store it with gclid. It is the iOS web-to-app click. |
| fbclid | Meta | The browser tag turns it into _fbc. Do not hash fbc or fbclid. |
| ttclid | TikTok | First-party. Send it on the Events API call. |
| li_fat_id | First-party ad tracking id. Send it with the conversion. | |
| msclkid | Microsoft Ads | First-party. UET attribution needs it. |
| ScCid | Snapchat | Keep this spelling. Do not lowercase the parameter name. |
| twclid | X | First-party click id for the X pixel and the conversions API. |
| epik | First-party. The tag may also set a cookie named _epik. | |
| rdt_cid | First-party click id for the Reddit Conversions API. |
UTM builder and linter
Source, medium, and campaign are required. Values are lowercased. Spaces fail the check. The UTM builder is the same naming rule. This one also flags a medium that disagrees with the source.
Channel group: Unassigned
Fix the required fields to build the link.
Click id detector
Paste a landing URL, or read the URL of this page. The detector looks only at the query string. It does not read cookies and it does not store the values.
| Parameter | Platform | On this URL |
|---|---|---|
| gclid | Google Ads | absent |
| gbraid | Google Ads, app to web | absent |
| wbraid | Google Ads, web to app | absent |
| fbclid | Meta | absent |
| ttclid | TikTok | absent |
| li_fat_id | absent | |
| msclkid | Microsoft Ads | absent |
| ScCid | Snapchat | absent |
| twclid | X | absent |
| epik | absent | |
| rdt_cid | absent |
One data layer
The page pushes one object. Every tag reads it. The dataLayer lab shows that queue for this site. Hash email before it is placed on the object. Leave the raw address out.
Purchase push
The same event_id is the GA4 transaction id and the dedup key for the ad platforms.
dataLayer.push({
event: "purchase",
event_id: "order-1001",
ecommerce: {
transaction_id: "order-1001",
value: 42.5,
currency: "USD",
items: [{ item_id: "sku-1", item_name: "Notebook", price: 42.5, quantity: 1 }]
},
user_data: {
sha256_email_address: "<sha256 of the trimmed lowercase email>"
},
campaign: {
source: "google",
medium: "cpc",
campaign: "spring-shoes",
content: "static-offer",
term: "running-shoes",
click_ids: { gclid: "<from the landing url>" }
},
consent: { analytics_storage: "granted", ad_storage: "granted" }
});Tag manager architecture
Google Tag Manager: a data layer variable for each field, one trigger for the event name, one tag per platform. Consent is a tag setting, not a second trigger someone forgets. Firing order does not matter when every tag reads the same push.
Adobe Experience Platform Tags, still called Launch: a data element per field, one rule, one action per platform, and a condition for consent. Event Forwarding sends the server copy from the edge with the same event id. Adobe Target may swap the headline. It should read the campaign object, not invent a new event name.
Google Tag Manager
Variables, one trigger, one tag per platform, consent on the tag.
// One data layer push. Tags only read it.
// Variables: DLV - event_id, DLV - ecommerce.value, DLV - ecommerce.currency, DLV - campaign.source
// Trigger: Custom Event, event name equals purchase
// Tags, each with its own consent setting:
// GA4 event purchase requires analytics_storage
// Google Ads conversion requires ad_storage and ad_user_data
// Meta Pixel Purchase requires ad_storage, eventID = {{DLV - event_id}}
// One more tag per platform, same trigger, same event id
// Do not chain tags so that tag 2 reads a variable tag 1 invented.
// Server container: the same event id is forwarded. It is not a second purchase.Adobe tags and Target
Data elements and one rule. Event Forwarding is the server copy.
// Adobe Experience Platform Tags (Launch)
// Data element: event_id, from the data layer path event_id
// Data element: order_value, from ecommerce.value
// Rule: Event equals purchase
// Condition: consent ad_storage equals granted
// Action: one action per platform, each reading the same data elements
// Event Forwarding rule: same event, same event_id, sent from the edge
// Adobe Target reads campaign.source and campaign.content. It does not invent a second name.Tag alignment matrix
Purchase is the example. The same columns apply to every canonical event. Swap the pixel name from the mapping table.
| Platform | Client tag | Server API | Dedup key | Consent |
|---|---|---|---|---|
| GA4 | gtag event | Measurement Protocol | transaction_id, or event_id | Analytics |
| Google Ads | Google tag conversion | Enhanced conversions and offline import | order id or transaction_id | Ads |
| Meta | fbq track | Conversions API | event_id and event_name | Ads |
| Insight Tag | Conversions API | event id on the conversion rule | Ads | |
| TikTok | ttq track | Events API | event_id | Ads |
| pintrk track | API for Conversions | event_id | Ads | |
| Snapchat | snaptr track | Conversions API | client_dedup_id | Ads |
| X | twq event | Conversions API | conversion_id | Ads |
| Microsoft | UET tag | Offline and enhanced conversions | msclkid plus conversion time, or the order id | Ads |
| rdt track | Conversions API | conversion_id | Ads |
event_id …
canonical
purchase
One event. Each card is a tag that would read it. Nothing is sent.
Server events
The browser tag misses people. The server that already recorded the order can send the same event. Share the dedup key. Hash email and phone once, after trim and lowercase for email, and digits plus country code for phone. Do not hash click ids, IP addresses, or user agents. The CAPI lab shows the Meta rules in the browser. The offline conversions lab is the Measurement Protocol shape.
A server-side GTM container can forward one incoming event to these APIs. It is still one event id. It is not a second conversion.
- GA4. Measurement Protocol. Related lab
- Google Ads. Enhanced conversions, plus offline import. Related lab
- Meta. Conversions API. Related lab
- LinkedIn. Conversions API.
- TikTok. Events API.
- Pinterest. API for Conversions.
- Snapchat. Conversions API.
- X. Conversions API.
- Microsoft. UET offline and enhanced conversions.
- Reddit. Conversions API.
Consent and privacy
Google Consent Mode v2 has four signals: analytics_storage, ad_storage, ad_user_data, and ad_personalization. A consent management platform should set them before tags run. Denied means the ads tags do not fire. Advanced mode can let Google tags send a cookieless ping. That ping is still a Google feature you choose. It is not a silent exception.
Meta limited data use is a data-processing option for regional limits. It does not replace a consent choice. GDPR and ePrivacy expect a choice before non-essential cookies. CPRA is an opt-out of sale or share. Global Privacy Control is a signal to honor, not a banner to ignore.
On this site, Google Analytics waits for Accept, and Global Privacy Control forces measurement off. The dry runs follow the same idea: consent off omits the payload. The consent lab is the control itself.
Attribution and reconciliation
The ad platform, GA4, and the CRM will not print the same total. Windows differ, and they are set in each account. View-through counts a conversion after an impression. Modeled conversions fill gaps the platform estimates. Read the window and the model flag in the interface before you compare a number.
Compare order ids, not dashboard totals. A purchase in the platform and a purchase in GA4 with the same id is a match even when the credit splits differently. The attribution lab scores one journey six ways so you can see the split.
Incrementality is a holdout or a geo test, not a column in the ads report. Media mix models use spend and outcomes over weeks. They sit beside multi-touch, they do not replace the order-id check.
- Same order id in the ad platform, GA4, and the CRM.
- event_id present on both the pixel and the server call.
- Click window and view window written down for each platform.
- Modeled conversions labeled, and left out of a raw reconciliation.
- Time zone and currency the same on every export.
- Consent denied counted as not sent, not as a lost sale.
- A lift test planned before you treat a platform lift number as causal.
QA and monitoring
Use the helper the platform ships. A passing helper and a missing order id is still a failed launch.
| Platform | Tool |
|---|---|
| GA4 | DebugView |
| Google tags | Tag Assistant |
| Meta | Events Manager Test Events, and the Pixel Helper |
| Insight Tag helper | |
| TikTok | Pixel Helper |
| Microsoft | UET Tag Helper |
| Pinterest Tag Helper | |
| Snapchat | Pixel Helper |
- The taxonomy sheet names every event, parameter, value, currency, and event id.
- A campaign link is lowercase and uses the channel medium in the table above.
- The data layer push is the only input. No tag builds its own event name.
- Each platform has one client tag and, where you use it, one server call, sharing the dedup key.
- Consent denied fires no ads tag. Analytics waits for the choice this site already uses.
- DebugView, Tag Assistant, and each platform helper show the same event id.
- A test order is excluded from bidding, with a test event code where the platform has one.
- The order id appears once in the ad platform and once in GA4. The totals are not required to match.
After launch, alert when volume drops, when event_id is empty, or when the server count and the pixel count diverge. The data quality lab and the anomalies lab are the checks this site runs on its own events.
Advanced measurement setups
These are the systems around the tag. Each row is a pattern and a boundary. Official links are at the bottom of the page. Closed loop, from the click to the CRM and back:
- 1 Click
- 2 Lead
- 3 Opportunity
- 4 Revenue
- 5 Ad platform
| Setup | What to do |
|---|---|
| Server-side tagging | A server container on Cloud Run or App Engine, a first-party subdomain such as metrics.example.com, and the web container forwarding there. Adobe Event Forwarding is the same job on the Adobe edge. |
| Cookie durability | Safari ITP limits JavaScript cookies. A server-set first-party cookie on your domain lasts longer than a third-party cookie. Do not work around the browser. Say what you store and why. |
| Google Ads leads | Enhanced conversions for leads hashes the email from the form. Offline import sends the closed deal later, with the gclid or the order id. A conversion adjustment restates the value when the CRM changes. |
| Value and profit | Value-based bidding needs a value on the conversion. Send revenue, or profit, or a predicted lifetime value your model produced. Label the number. Do not pretend the ad platform calculated the margin. |
| Meta value | Offline events can use the Conversions API with action_source system_generated. Value optimization needs value and currency on the event. A predicted lifetime value is your number, sent as the value. |
| Clean rooms | Ads Data Hub, Amazon Marketing Cloud, Meta Advanced Analytics, and a LiveRamp or Snowflake clean room join ad exposure to your data without handing either side the raw rows. |
| Incrementality | Geo-lift, conversion lift, and a holdout are experiments. Meta publishes GeoLift. Robyn and Google Meridian are media-mix models. A platform-reported conversion is not a lift result. |
| MMM and MTA | Mix models use spend and outcomes over time. Multi-touch uses user paths. They answer different questions. Neither replaces a holdout. |
| Warehouse | Export GA4 to BigQuery. Land ad platform tables with the official API or a loader such as Fivetran. Model them with dbt. Join on the order id, not on a platform total. |
| Reverse ETL | Send hashed audiences back to Customer Match and custom audiences. Consent still applies. A suppressed customer should be in the exclusion list. |
| Cross-domain | One first-party measurement id across domains you own, plus a linker parameter. The cross-domain lab shows the toy version. |
| App and web | Firebase for the app stream. SKAdNetwork and AdAttributionKit for iOS. An MMP such as AppsFlyer or Branch when the app store is the conversion. The app tracking lab is the webview case. |
| Closed loop | Lead, then opportunity, then revenue. Each stage is a named conversion. The revenue row goes back with the original click id or order id. |
| Observability | Alert when a tag stops, when event_id is missing, or when server volume diverges from the pixel. A data contract names the required parameters. The data quality and anomalies labs are the same idea for this site. |
Salesforce offline row
A display sample for Google Ads offline import. This site does not upload it.
{
"Google Click ID": "<gclid from the lead>",
"Conversion Name": "Closed Won",
"Conversion Time": "2026-10-10 15:04:00+0000",
"Conversion Value": 1800,
"Conversion Currency": "USD",
"Order ID": "opp-440"
}Conversion value adjustment
Use this when the closed value changes. The order id is the join.
{
"Order ID": "opp-440",
"Adjustment Time": "2026-10-12 09:00:00+0000",
"Adjusted Value": 1400,
"Currency": "USD",
"Reason": "Restatement"
}Cross-domain details are in the cross-domain lab. App events and a webview are in the app tracking lab. A server you call yourself is the server-side lab and the webhooks lab.
Campaign-driven personalization
The same campaign object can change the page. Message match uses utm_term and utm_content. A click id says which platform paid for the visit. A campaign name can pick a landing variant. Referrer and source name the social platform. Geo, weather, and hour are context. Device, new or returning, and first touch versus last touch are persistence choices. Account-based tools such as Demandbase, 6sense, or Clearbit add a company. This demo uses a labeled company instead of calling one.
CRM audiences and lifecycle stage change the call to action. A retargeting sequence should continue, not restart. A promo code in the ad should be the code on the page. Locale picks a landing. Cart or browse history continues the visit. An intent score can swap a soft button for a direct one. A converted visitor should not see the acquisition offer.
Measure the swap. Hold out a share of the campaign, or run the experiment the experiments lab describes, and attribute the experience back to the campaign name. A higher click rate on the variant is not lift until the holdout says so.
Implement it once. The page writes campaign_context. Google Tag Manager and Adobe Target read it. The lightweight client below is enough when you do not want a testing tool to own the headline. The segments lab, the source lab, the personas lab, and the weather lab are the live versions of those signals on this site.
Lightweight client
The page picks the variant. The tag manager measures it. This sample does not run here.
// Lightweight client. The page decides the variant. Tags only measure.
const params = new URLSearchParams(location.search)
const clickIds = ["gclid", "fbclid", "ttclid", "li_fat_id", "msclkid", "ScCid", "twclid", "epik", "rdt_cid"].filter((key) => params.get(key))
dataLayer.push({
event: "campaign_context",
campaign: {
source: (params.get("utm_source") || "").toLowerCase(),
medium: (params.get("utm_medium") || "").toLowerCase(),
campaign: (params.get("utm_campaign") || "").toLowerCase(),
content: (params.get("utm_content") || "").toLowerCase(),
term: (params.get("utm_term") || "").toLowerCase(),
click_ids: Object.fromEntries(clickIds.map((key) => [key, params.get(key)]))
}
})
// Render the headline from those fields, then let the tag manager fire.Landing context
First touch is held in this tab only. Refresh clears it. Nothing is written to storage.
Dry-run landing
Default page, consent off
The campaign rule is ready, and it is withheld. This site treats personalization and measurement as the same choice.
Accept to preview the variant
Offer: Withheld
Consent withheld the variant
No campaign rule matched, so the page stays on the generic offer.
- skip converted
- skip promo
- skip click
- skip term
- skip creative
- skip retarget
- skip company
- skip lifecycle
- skip source
- skip weather
- skip hour
- skip device
- skip returning
- skip intent
- skip locale
The preview stays in this tab. It does not call an ad platform, a firmographic vendor, or the weather service. Consent off keeps the default page. The rule that would have fired is still listed.
Official docs
- GA4 recommended events
- GA4 event reference
- GA4 Measurement Protocol
- Google tag and Tag Manager
- Server-side Tag Manager
- Consent Mode
- Google Ads enhanced conversions
- Enhanced conversions for leads
- Google Ads offline conversions
- Google Ads conversion adjustments
- Customer Match
- Ads Data Hub
- Meridian
- GA4 BigQuery export
- Meta Pixel standard events
- Meta Conversions API
- Meta data processing options
- Robyn
- GeoLift
- LinkedIn Conversions API
- TikTok standard events
- TikTok Events API
- Pinterest event codes
- Pinterest Conversions API
- Snap Conversions API
- Microsoft UET
- Reddit Pixel
- Reddit Conversions API
- X conversion tracking
- Adobe tags
- Adobe event forwarding
- Adobe Target
- Firebase Analytics
- SKAdNetwork
- AdAttributionKit
- WebKit tracking prevention
Recommended next
Why these picksReading path transitions…