Investigation date: 2026-08-24 Trigger: Littledata launched a second Google Ads purchase connection as a parallel option. It is a real web conversion action rather than an offline click upload, and it is expected to have more attributed revenue than the v1 upload. This document records what the v2 action looks like in the data, what it actually reports against v1 in the accounts running both, and how the value model reads it.
Background on why v1 was capped in the first place: UPLOAD_CLICKS conversion actions do not receive view-through attribution.
Update 2026-08-28 — the view-through mechanism is a minority contributor, not the main one.
Littledata Google Ads purchase connection: v1 vs v2, measured
measures where the uplift actually comes from across all five accounts running both connections.
Impression-side credit (ENGAGED_VIEW + IMPRESSION) accounts for 0% to 31% of the v1→v2
difference — 31% on becausemarket-prod, 0% on soda-sense — with click-side match-rate and value
gains the larger half wherever there is a gain at all. Read the split from
segments.conversion_attribution_event_type, never from metrics.view_through_conversions, which
omits engaged views and understated becausemarket-prod’s impression-side uplift 11-fold.
Both connections use the same action names, so type is the only reliable discriminator — never the name.
| v1 | v2 | |
|---|---|---|
conversion_action.type |
UPLOAD_CLICKS |
WEBPAGE |
| Google Ads UI “Conversion source” | Website (Import from clicks) | Website |
| Action name | Purchase - Littledata, or Purchase - Littledata (<customerId>) |
Purchase - Littledata (Web) |
| Click-through window | 90 days | 90 days |
| View-through window | 1 day (and structurally inert — see linked doc) | 30 days |
| Attribution owner | Littledata, keyed on the original click ID |
Naming is consistent across every account with v2: only the three
Purchase / New Customer Purchase / Returning Customer Purchase families, each suffixed (Web).
ldPurchaseConnectionVersion() in src/services/googleAds/constants.ts is the single place that
decides which connection an action belongs to.
The two connections attribute the same orders. Nothing may ever sum them.
Six accounts had a v2 web purchase action in the last 40 days of snapshots:
soda-sense, tentbox-6623, becausemarket-prod, jimmy-joy, gardeners-dream-online,
sparklingjewels-com. All three named in the launch brief are confirmed present, with the v2 action
captured in the daily conversion snapshots (byAction.actionType = "WEBPAGE", category PURCHASE).
Every rollout follows the same shape: v2 appears at a trickle, climbs over roughly two to three weeks, then plateaus near v1. Crossover happened around 2026-08-10 to 2026-08-13 for the three earliest accounts.
Steady-state comparison — settled days only, ramp-up excluded (see
computeGoogleAdsV2UpliftBenchmark):
| Account | Steady days | v2 ÷ v1 attributed revenue |
|---|---|---|
becausemarket-prod |
11 | 1.23× |
soda-sense |
10 | 1.10× |
tentbox-6623 |
7 | 0.95× |
jimmy-joy |
8 | 0.83× |
Cohort median 1.03×, P25 0.92×, P75 1.13×.
This measurement is not yet usable, and it does not set the customer-facing figure. tentbox-6623
and jimmy-joy had only just started when this was measured, so their sub-parity readings describe an
unfinished rollout rather than the connection — which is most of what drags the median to 1.03×. The
single-week comparisons in the launch screenshots (Soda Sense 1.15×, Because Market 1.17×) come from
the two accounts that had finished.
Two more accounts are excluded as not yet measurable at all: gardeners-dream-online (v2 switched on
2026-08-23, one partial day) and sparklingjewels-com (v2 only, no v1 to compare against).
Revisit from 2026-09-07, by which point the four current donors will have a fortnight of settled
steady-state history and gardeners-dream-online should qualify. Until then the value model quotes a
flat assumption (below).
googleAdsRoasFromSnapshots)Each candidate ROAS window sums the two connections separately and reads whichever reported more
over that window, recording the choice as ldPurchaseVersion alongside both raw totals.
Preferring the larger rather than always preferring v2 is deliberate: a v2 connection switched on
mid-window is still filling in, and reading the partial side would compare a fortnight of Littledata
against a full quarter of baseline. The consequence while v2 is young is that short windows
(7/14-day) already read v2 in soda-sense, becausemarket-prod and sparklingjewels-com, while
long windows still read v1 — two weeks of v2 cannot out-total 90 days of v1. It resolves itself as
v2 accumulates history.
An account running only v2 (v1 switched off) now registers as Littledata-active at all; before this change it produced no Google Ads value tier.
googleAdsAttributionWindowValueUpliftFactor exists to credit v1 for what widening its lookback
windows would add. v2 already carries both the account’s normal 30-day view-through window and a
90-day click window — the window the experiment widens v1 to — so the factor is applied only
when the comparison reads v1, per aggregation and per window score. (Since the 2026-05-23 resolution
the experiment holds view-through fixed and widens click windows only, so the click window is the
part of that sentence doing the work.)
For an account with no v2 activity at all, the model estimates what v2 would add using
GOOGLE_ADS_V2_ASSUMED_UPLIFT — a flat 10% launch assumption, not the measured median:
predicted additional revenue = own v1 revenue × 1.10 − Littledata value already reported
Subtracting the reported value (not raw v1) is what stops an account whose v1 lookback windows were already widened by a conversion-window experiment from being credited twice for attribution it already won — v2’s own wider windows are the same mechanism.
Gates, all of which must pass before anything is stored or shown:
MIN_REPORTABLE_GOOGLE_ADS_V2_MONTHLY_USD ($100) — below that, 10% of this
account’s Google Ads revenue isn’t worth its attentionIt surfaces as one line on the Google Ads hero card:
Test out Littledata’s Google Ads v2 connection to capture another 10% more attributed revenue
The percentage is interpolated from the constant, so changing the assumption changes the copy. The line
quotes no dollar figure on purpose: putting a precise monthly amount on an assumed rate would dress
the assumption up as a measurement. The dollar estimate is still stored on the tier
(googleAdsV2PredictedAdditionalRevenueUsd) for internal sorting, and is never part of value,
any ROI, or any value total — it is a counterfactual about a connection the account does not run.
checkGoogleAdsGoalConfig counts a Littledata-named WEBPAGE purchase action as Littledata being
configured and primary, so an account that finishes migrating to v2 is not told Littledata is
missing. A v1 action losing primary while a v2 action holds the goal is the migration completing, not
a demoted goal, and no longer raises a warning — narrowed to v1 actions and only when a v2 action is
actually primary, so v1-only accounts behave exactly as before.
Baseline selection was already safe: every Littledata-named action is excluded from baseline candidates by name, so the v2 action could never have been picked as a competing pixel.
npx tsx scripts/report-google-ads-v2-uplift.ts
Prints the assumed uplift, the measured cohort median against it, and the per-donor breakdown. Pass
--refresh to bypass the one-day cache. At the 2026-09-07 revisit, if the donor set has reached
V2_UPLIFT_MIN_SAMPLE_SIZE with settled history, replace GOOGLE_ADS_V2_ASSUMED_UPLIFT with the
measured ratioP50 - 1 — the script prints the exact value to use.
src/services/googleAds/constants.ts — ldPurchaseConnectionVersion, the v1/v2 type constantssrc/services/googleAds/googleAdsRoasFromSnapshots.ts — per-window version selectionsrc/services/googleAds/googleAdsV2UpliftBenchmark.ts — the assumed uplift, plus the cohort
measurement, steady-state detection and cache that exist to check itsrc/services/value/computeCustomerValue.ts — computeGoogleAdsV2PredictedUpliftExtrassrc/services/value/valueHelpers.ts — formatGoogleAdsV2PredictedUpliftLinesrc/services/audit/checkGoogleAdsGoalConfig.ts — v2 as a primary Littledata goal