Google AdsGA4FoundationalGuide

Google Ads Attribution Setup 2026: The Stack That Survives Cookies, iOS, and the API Deadlines

Google measurement is a stack now: the Google tag, conversion linker, enhanced conversions, consent mode v2, offline uploads through Data Manager, Customer Match, server-side tagging or the tag gateway, and data-driven attribution. What each layer covers, the 2026 API cutoffs that broke old integrations, the code for each path, and the weekly checks.

Updated September 25, 202617 min read

"drop the conversion pixel and you're done" stopped being true years ago, and in 2026 the old integrations stopped working on a schedule: customer match uploads through the ads api closed to new adopters on april 1, offline click conversion uploads closed to new developer tokens on june 15, and everything moved to data manager. this guide is the full google measurement stack as it stands now, with the code for each layer and the checks that keep it honest.


what the google measurement stack actually is

no single tag does the job. each layer covers a different way the click-to-conversion link breaks.

layerwhat it doeswhat it covers
google tag (gtag.js or tag manager)one tag for google ads and ga4the base: page views, conversion events, consent state
conversion linkerwrites gclid, gbraid, wbraid from the url into the first-party _gcl_aw cookiemulti-page funnels where the click id lands on page one and the conversion happens on page four
enhanced conversionshashed first-party identifiers sent with each conversioncookie loss, cross-device, safari, signed-in matching. see enhanced conversions
consent mode v2consent state sent with every hiteea, uk, and swiss traffic; modeled conversions for users who decline
offline and lead uploads through data managerconversions that happen off the site, matched by click id or hashed identifiersphone sales, crm closes, anything after a 90-day window
ga4 conversion importga4 key events as google ads conversionscross-channel view; observation, not bidding. see ga4 and google ads
customer matchhashed audience listssuppression, retention, high-value new customer seeds
server-side tagging or google tag gatewaytags served from your own domainad blockers, safari cookie caps, payload control
data-driven attributioncredit split across touchesthe only model left besides last click

here is what most guides get wrong: they present these as options. the first four are the baseline. an account missing any of them is missing conversions, and smart bidding is training on the gap.


the fundamental shift: from one click id to a portfolio of signals

before: auto-tagging appended gclid, the pixel read it, the conversion counted. one identifier did everything.

after: three click ids, none of them reliable on apple traffic. gclid fires on web clicks; gbraid on ios web-to-app; wbraid on ios app-to-web, both aggregated across users to survive app tracking transparency (google ads help, updates to ios 14+ measurement). safari 26, released september 15, 2025, strips known click identifiers from links opened in mail, messages, and private browsing. google's own answer is the rest of the stack: hashed identifiers through enhanced conversions, consent signals so declined users can be modeled, first-party tag serving through the tag gateway, and a single upload path through data manager.

the 2026 deadlines made it mandatory. from april 1, 2026, the google ads api no longer accepts new adopters of customer match; OfflineUserDataJobService and UserDataService requests fail for cloud projects with no prior history, and the data manager api is the path (google ads developer blog, changes to customer match support, march 2026). from june 15, 2026, UploadClickConversion requests fail for developer tokens that never sent offline or lead conversions before (google ads developer blog, changes to offline click conversion import, may 2026).

in plain english: the click id is one signal among several, and the upload path is data manager. build for that.


what this broke in existing accounts

1. integrations that only captured gclid

code from 2020 reads one parameter. ios traffic arrives with gbraid or wbraid and the click is lost before the landing page finishes loading.

2. new offline pipelines built on the ads api after the cutoff

a crm integration written in july 2026 against UploadClickConversion with a fresh developer token fails every call. the error is real; the fix is the data manager api, not a retry.

3. eea traffic with no consent signal

google requires consent signals to keep "ad personalization capabilities, remarketing features, measurement functionality" for eea users (google ads help, updates to consent mode for eea traffic). accounts without consent mode v2 saw remarketing and customer match disabled and european conversions modeled at zero.

4. conversions counted twice

the same purchase sent from the browser tag and from the api without a shared transaction_id and order_id. google deduplicates on that key and only that key.

5. offline conversions uploaded after day 90

google retains the click id for 90 days. uploads after that do not error; they vanish. sales cycles longer than 90 days need enhanced conversions for leads, matched on hashed identifiers rather than the click id.

6. the wrong conversion action type

enhanced conversions for leads requires an UPLOAD_CLICKS action. uploads to a WEBPAGE action return INVALID_CONVERSION_ACTION_TYPE and are dropped under partial failure.

7. accounts still planning for a cookieless chrome

google retired the attribution reporting api, protected audience, and topics on october 17, 2025, citing "low levels of adoption" (privacy sandbox, update on plans, october 2025), and chrome kept third-party cookies under a user-choice model. teams that built for the sandbox built for nothing; teams that assumed cookies would vanish underinvested in the first-party stack that actually matters.


the data

  • google, may 2025: "advertisers who configured google tag gateway for advertisers saw an 11% uplift in signals" (google ads help, google tag gateway for advertisers); september 2026: 14% conversion uplift on average for advertisers using the gateway, 11% more search conversions from enhanced conversions versus standard imports, and 26% higher incremental roas for advertisers connecting offline and app data through data manager (google ads and commerce blog, september 10, 2026). self-reported.
  • google ads api: customer match closed to new adopters april 1, 2026; offline click conversion uploads closed to new developer tokens june 15, 2026; the data manager api replaces both. (google ads developer blog)
  • customer match lists need at least 100 records for lists uploaded or refreshed after february 1, 2024, down from 1,000 (google ads help, fix customer match issues).
  • consent mode v2 adds ad_user_data and ad_personalization; consent signals are required to keep personalization, remarketing, and measurement for eea users. (google ads help)
  • google retired the privacy sandbox advertising apis on october 17, 2025. (privacy sandbox blog)
  • wordstream, more than 250,000 grader reports: 29% of accounts recorded zero conversions over 90 days while averaging 12,667 impressions a month (wordstream, google ads performance study, 2025). a share of those accounts are not failing to convert; they are failing to measure.

what changed in 2026 specifically

april 1, 2026: customer match through the ads api closed to new adopters. new audience integrations use the data manager api.

april and june 2026: enhanced conversions became one toggle fed by multiple simultaneous paths (tag, data manager, api). see the enhanced conversions guide.

june 15, 2026: offline click conversion uploads through the ads api closed to new developer tokens. data manager api for new lead and offline pipelines.

september 2026 to february 2027: dynamic search ads, automatically created assets, and campaign-level broad match move to ai max. ai max's final url expansion changes which landing pages receive clicks; tracking templates and url rules need to cover the expanded urls (google ads and commerce blog, dynamic search ads are upgrading to ai max).

unchanged: the 90-day click id window, the transaction_id deduplication rule, data-driven and last click as the only models, and the requirement that parallel tracking templates respond within 200 milliseconds.


what actually works now

1. turn on auto-tagging and capture all three click ids

auto-tagging lives under admin, account settings. with it off, google falls back to utms and enhanced conversions and click-id uploads stop working. on the landing page, capture every id and persist it for 90 days; the conversion linker does this into _gcl_aw when the google tag runs, but capture it yourself if you will send click ids from your backend.

// run on every landing page before any conversion logic
function captureGoogleClickIds() {
  const url = new URLSearchParams(window.location.search);
  const ids = { gclid: url.get('gclid'), gbraid: url.get('gbraid'), wbraid: url.get('wbraid') };
  for (const [key, value] of Object.entries(ids)) {
    if (!value) continue;
    const expires = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toUTCString();
    document.cookie = `_orbit_${key}=${encodeURIComponent(value)}; expires=${expires}; path=/; SameSite=Lax; Secure`;
    try { localStorage.setItem(`_orbit_${key}`, value); } catch {}
  }
}
captureGoogleClickIds();
if the final url already has a query string, google appends &gclid=.... server-side redirects that strip query parameters lose the click id before the page loads. test with a real ad click, not a pasted url. the _gcl_aw cookie format is GCL.{timestamp}.{click_id} if you parse it server-side.

2. install one tag, not two

pick the direct google tag or tag manager. running both loads gtag/js twice and inflates everything.

direct google tag, in the head of every page, with consent defaults before config:

<script async src="https://www.googletagmanager.com/gtag/js?id=AW-XXXXXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('consent', 'default', {
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
    analytics_storage: 'denied',
    wait_for_update: 500
  });
  gtag('config', 'AW-XXXXXXXXXX', {
    conversion_linker: true,       // default; writes _gcl_aw from gclid, gbraid, wbraid
    user_id: window.__currentUserId // optional stable id for cross-device matching
  });
</script>

fire a conversion with the dedup key:

gtag('event', 'conversion', {
  send_to: 'AW-XXXXXXXXXX/AbC-D_efG-h12_34-XYZ',
  value: 49.99,
  currency: 'USD',
  transaction_id: 'order_5193' // must equal order_id on any api upload of the same order
});

tag manager: the container snippet on every page, a google tag (the unified tag, not the legacy conversion tag) on all pages, a conversion linker tag on all pages, and one google ads conversion tracking tag per action on its trigger. push the purchase with raw user data and let the tag hash it:

window.dataLayer.push({
  event: 'purchase',
  ecommerce: { transaction_id: 'order_5193', value: 49.99, currency: 'USD' },
  user_data: { email: 'customer@example.com', phone: '+12125551234' }
});

3. set each conversion action on purpose

settingpick
primary or secondaryone primary per outcome; soft actions (newsletter, scroll) secondary
valuereal values for purchases; an estimated lead value for lead gen, or value bidding has nothing to bid on
countevery for purchases, one for leads and signups
click-through window30 days default; match the ga4 key event window; longer only for long cycles
view-through window1 day; do not extend without a reason
attribution modeldata-driven; last click only for baselining

first click, linear, time decay, and position-based were removed in late 2023. if a guide references them, it is old.

4. enhanced conversions on every primary action

email at minimum, plus phone and address where you hold them, normalized before hashing, sent through the google tag, tag manager, or the api, all of which can now run at once. full setup, diagnostics, and the 30-day uplift window are in the enhanced conversions guide. the manual path, for single-page apps where the page does not render the identifiers:

async function sha256(v) {
  const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(v));
  return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
}
async function setUserData({ email, phone, firstName, lastName, postalCode, country }) {
  const userData = {};
  if (email) userData.sha256_email_address = await sha256(email.trim().toLowerCase());
  if (phone) userData.sha256_phone_number = await sha256(phone.replace(/[^\d+]/g, ''));
  if (firstName || lastName || postalCode || country) {
    userData.address = {};
    if (firstName) userData.address.sha256_first_name = await sha256(firstName.trim().toLowerCase());
    if (lastName) userData.address.sha256_last_name = await sha256(lastName.trim().toLowerCase());
    if (postalCode) userData.address.postal_code = postalCode;  // raw
    if (country) userData.address.country = country;           // raw, two-letter iso
  }
  gtag('set', 'user_data', userData);
}
// call before the conversion event on the confirmation state

defaults denied before the user chooses, regional scoping for the eea, uk, and switzerland, an update on consent, and a google-certified consent platform. basic mode blocks tags until consent and gives google nothing to model from; advanced mode sends cookieless pings on denial so declined users are modeled.

gtag('consent', 'default', {
  ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied',
  wait_for_update: 500,
  region: ['BE','BG','CZ','DK','DE','EE','IE','GR','ES','FR','HR','IT','CY','LV','LT','LU','HU','MT','NL','AT','PL','PT','RO','SI','SK','FI','SE','IS','LI','NO','GB','CH']
});
// cmp accept handler
function onConsentAccepted() {
  gtag('consent', 'update', {
    ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted', analytics_storage: 'granted'
  });
}

server uploads carry the same state: consent: { ad_user_data: 'GRANTED' | 'DENIED', ad_personalization: 'GRANTED' | 'DENIED' }. missing consent on eea users is treated as not consented and the data is not used for personalization.

6. offline and lead conversions through data manager

three paths, one destination.

pathaction typewhenlimit
click id uploadUPLOAD_CLICKSyou stored gclid at lead creation90 days from click
enhanced conversions for leadsUPLOAD_CLICKShashed email or phone, with the click id when you have it90 days; survives missing click ids
data manager connectors (sheets, cloud storage, s3, sftp, crm)eitherno-code or scheduledimports the last 90 days on every run

the flow for leads: the google tag captures hashed identifiers at form submit; the crm stores them with the lead and the click id; when the deal closes, upload the conversion with the same hashed identifiers, the click id, the value, and the consent state, through a data manager connector or the data manager api. new integrations do not use UploadClickConversion on the ads api; since june 15, 2026 it fails for tokens with no history.

after creating a new offline conversion action, wait 4 to 6 hours before the first upload. earlier uploads can take up to two days to appear and look like failures.

hashed email, phone, and address, or raw mobile advertising ids, at least 100 records per list, refreshed on a schedule, with the consent field populated. suppression lists for existing customers on prospecting campaigns, buyer lists as seeds for high value new customer mode, and lapsed lists for the retention goal (see lifecycle goals in the subscription guide). new integrations use the data manager api; the ads api path closed on april 1, 2026.

8. serve tags from your own domain

two options. google tag gateway routes the existing tag through your cdn or load balancer with no code changes; cloudflare offers a one-click integration and google reported an 11% signal uplift at launch. server-side tag manager on cloud run gives a full endpoint on metrics.yoursite.com where you can redact, enrich, and fan out to google, meta, and tiktok:

gtag('config', 'AW-XXXXXXXXXX', {
  transport_url: 'https://metrics.yoursite.com',
  first_party_collection: true
});

gateway for most accounts; server-side tag manager when you need payload control or multi-vendor fan-out.

9. keep parallel tracking under 200 milliseconds

parallel tracking is mandatory on search, shopping, display, video, and performance max. a tracking template on a slow third-party redirector silently loses the click. check it with google's parallel tracking tool before launch, and put utms in the final url suffix, never the template (see the utm standard).

10. verify weekly

tag assistant for live tag firing, tag manager preview for firing order, the tag coverage report for pages where the tag did not load, tag diagnostics for malformed user data, the enhanced conversions diagnostics report per action, and the conversion action status column.

symptomcausefix
match rate under 30%un-normalized fields before hashinglowercase, trim, e.164 phone; test one record by hand
lead conversions never appearWEBPAGE action instead of UPLOAD_CLICKScreate the right action type
click id missing server-sidelinker off or template strips itre-enable the linker; read _gcl_aw
modeled conversions above 60% in the eeaconsent mode not implemented, everyone effectively deniedcertified cmp, denied defaults, update on accept
double-counted conversionsbrowser and api without shared transaction_id / order_idsame key on both
"unverified" for a daynew actionwait; do not trust day one
offline uploads failing since june 2026ads api path on a token with no historydata manager api

what to stop doing immediately

  • stop capturing only gclid.
  • stop building new offline or audience pipelines on the ads api upload services.
  • stop uploading conversions after day 90 and expecting them to appear.
  • stop sending the same conversion from browser and server without a shared transaction id.
  • stop running eea traffic without consent mode v2 in advanced mode with a certified cmp.
  • stop planning for a cookieless chrome or a privacy sandbox integration.
  • stop putting utms in tracking templates.
  • stop treating the google tag as the whole setup.

the orbit agent instructions

{
  "guide": "google-ads-attribution-setup",
  "agent_tasks": {
    "audit_click_id_capture": {
      "check": "confirm auto-tagging is on and that gclid, gbraid, and wbraid survive from the ad click to the landing page and are stored with the lead or order",
      "flag": "auto-tagging off, any click id missing on the landing page after a sampled click, or orders and leads stored without a click id",
      "recommend": "enable auto-tagging, fix redirects and consent reloads that drop query strings, and persist all three click ids into a first-party cookie and the crm"
    },
    "audit_conversion_action_settings": {
      "check": "for every conversion action read status, category, primary or secondary, counting (every or one), click-through window, and attribution model",
      "flag": "any primary action in unverified, tag inactive, or no recent conversions status, more than one primary per outcome, counting set to every on a lead action, or a window that differs from the ga4 key event",
      "recommend": "one primary per outcome, every for purchases, one for leads, data-driven attribution, and one window applied in both google ads and ga4"
    },
    "audit_enhanced_conversions": {
      "check": "read enhanced conversions status, coverage, and match rate for each primary action and confirm the customer data terms are accepted on the client account",
      "flag": "status other than excellent on a primary action, coverage below 80 percent, or terms not accepted",
      "recommend": "accept the terms, send email plus phone plus name plus address normalized before hashing, and re-check diagnostics after 24 hours"
    },
    "audit_consent_mode": {
      "check": "detect consent mode v2 signals on eea and uk traffic and read the modeled conversions share in google ads diagnostics",
      "flag": "eea or uk campaigns with no consent signals or zero modeled conversions",
      "recommend": "implement consent mode v2 in advanced mode and re-check modeled conversions after 7 days"
    },
    "audit_offline_import_freshness": {
      "check": "for offline and lead conversion imports, read the last successful upload time, the share of rows carrying a click id, and the upload path in use",
      "flag": "no upload in 7 days, fewer than 80 percent of rows with a click id, uploads older than the 90-day window, or a legacy sheet or api path instead of data manager",
      "recommend": "move uploads to a data manager connector on at least a daily schedule and capture the click id at lead creation"
    },
    "audit_customer_match_lists": {
      "check": "list customer match audiences with size, match rate, and last refresh date",
      "flag": "any list below the size minimum for its network, a match rate below 30 percent, or a list not refreshed in 30 days",
      "recommend": "refresh lists through data manager on a schedule and send email plus phone plus address to lift match rate"
    }
  }
}

the bottom line

google measurement is a stack: three click ids captured, one tag, consent mode v2, enhanced conversions on every primary action, offline and audience data through data manager, tags served from your own domain, and data-driven attribution. the 2026 api cutoffs closed the old shortcuts; the stack is what is left, and it is better than what it replaced.

build every layer once, then run the weekly checks. that is the whole job.


built by orbit - the performance marketing intelligence layer. questions? jose@orbitllm.com