Resolves the "do we need a Google Ads developer token?" question and lays out the concrete steps to make domain-broker value-based bidding actually work. Supersedes Leg 2 of google-ads-conversion-value-strategy.md (the classic-API restatement plan). Companion to google-ads-offline-conversion.md.
TL;DR — the dev token is NOT needed, and the path that needs it is now a dead end. The qualified-lead value can be fed to Google entirely through the Data Manager API (the service-account path we already run for $1 leads — no developer token). The classic Google Ads API restatement we built (Leg 2) is unnecessary AND, since 2026‑06‑15, blocked to new developer tokens anyway. So whoever said "no token needed" is right; the code's assumption is wrong.
Make Google Smart Bidding optimize toward expected commission, not lead volume — by getting a real value (5% of budget) onto each qualified broker lead in Google Ads. Do it on the API we already use, with no new auth dependency.
The loop has two independent gaps, both open. Neither is the −46% bowtie scare (that's unrelated affiliate-gateway measurement).
Gap 1 — GHL → D1 outcome feed is built but not running. The inbound endpoint POST /api/ads-adjust works (it flipped 3 test leads to ads_stage=qualified on 2026‑06‑16). But in the 11 days since, zero real stage updates have arrived. With 219 leads/30d, a live GHL Workflow would have produced many more than 3 qualifications spread over time — the signature is a one-time setup test, not a running integration. D1 today: 216/219 leads stuck at lead, $0 commission ever recorded. So we have leads + cost, never outcome.
Gap 2 — D1 → Google Ads qualified value is built on the wrong (token-gated) API.
drainAdsConversions uploads every lead at $1 via the Data Manager API (datamanager.googleapis.com/v1/events:ingest) with a service account (GOOGLE_SA_KEY). 94 leads ads_status=sent. ✅drainAdsAdjustments → google-ads-adjust.js uses the classic Google Ads API ConversionAdjustmentUploadService (RESTATEMENT) — which needs an OAuth refresh token + developer token + login-customer-id. This is the dev-token dependency, and it has never sent a real adjustment.conversionValue per event and authenticates via OAuth/service account only — no developer token (Data Manager: Send events). We already do exactly this for the $1 lead.UploadClickConversions (and the offline/ECL family) reject any developer token not on Google's allowlist of tokens active Dec 2025–May 2026, with CUSTOMER_NOT_ALLOWLISTED_FOR_THIS_FEATURE. The Data Manager API is the designated replacement and is not subject to the block (ppc.land, Google: Manage offline conversions). Our 3 test pings were 2026‑06‑16 — after the cutoff — so if our token isn't allowlisted, Leg 2 cannot work even if finished. This likely explains why it never validated.transactionId to Data Manager may update the value (docs now say conversionValue "overrides the original value," after a 14‑day per‑action trial) — but this is tag‑centric and unproven for API‑ingested ECL conversions; the strategy doc's author tested re‑send and saw dedup‑only. Treat as unverified.TODO (owner, when back at a desk): open the GHL Workflow, confirm it's enabled and its webhook points at the live
…/api/ads-adjust, then advance one real opportunity to "qualified" and watch the D1 row flip toads_stage=qualified. This is a GHL-console task (no code), which is why it can't be done remotely right now. Everything downstream (Phase 2 value upload, value-based bidding) is blocked on this — until qualified signals arrive, D1 stays at 216/219 leads stuck atlead, $0 commission. Flagged high-priority 2026‑06‑28.
Nothing downstream matters until qualified signals actually arrive.
…/api/ads-adjust on opportunity stage change (qualified / closed‑won / closed‑lost), with the HMAC X-Ads-Adjust-Token and the vpn_lead_id (→ our order_id).4321874619), Secondary to start.ads_stage=qualified uploads a conversion to that qualified action with value = 5% of budget (same service account, same events:ingest, transactionId=vpn-broker-{id}-q). Reuse conversionValueFor() — it already computes 5%.google-ads-adjust.js + drainAdsAdjustments + the ADS_* Ads‑API secrets — the dev-token path is unnecessary and blocked. Delete, don't maintain.transactionId value‑override works on our ECL action with validateOnly — if it reliably restates, it's even simpler than a second action. Do not rely on it without proof.ads_stage.ADS_CONSENT_AD_USER_DATA / _AD_PERSONALIZATION) before Primary (EEA).ads_commission (D1‑only, past window) for true ROAS reporting.landing_page column to domain_intake so the next paid‑traffic flip is measurable by destination instead of inferred.validateOnly adjust; CUSTOMER_NOT_ALLOWLISTED_FOR_THIS_FEATURE confirms the block.)