Antigravity × UMP/ATT Consent Rate Optimization Agent — A Weekly Autonomous Loop That Lifts AdMob Revenue Region by Region
ATT and Google UMP consent rates are hidden levers that move AdMob eCPM by 1.3–2.0x. This is a working memo on letting Antigravity sub-agents run weekly experiments on regional consent UX across six apps, and how that lifted ARPDAU.
Over the last couple of months I went back through the consent UX of all six apps I currently run, both the iOS ATT (App Tracking Transparency) flow and the Google UMP (User Messaging Platform) flow. The reason was almost embarrassingly simple: when I lined up my AdMob monthly report by country, a ten-point swing in consent rate was moving eCPM by 1.3x to 2.0x. The lever was right there in front of me.
I started building apps independently in 2014, my titles have crossed 50 million downloads cumulatively, and AdMob still pays the bills. When I first touched the internet in 1997 at sixteen, a stranger on a forum overseas read my code and shipped me back a fix — that small moment of "we can build across borders" still quietly shapes how I think about consent UX today. A consent prompt is not just a legal checkbox. It is the front door of a relationship with a user in a specific region, and if the door is rude, everything downstream — retention, ARPDAU, reviews — drags.
This article is a working memo on how I let Antigravity sub-agents run a weekly observe → hypothesize → implement → verify loop on consent UX, where I draw the line between autonomous and human-in-the-loop, and which mistakes I personally walked into. It is not a guide to integrating the SDKs or to writing the legal copy. It assumes you already have UMP and ATT in place and want to keep improving them without doing it ad hoc.
Why consent rate deserves to be a top-level AdMob KPI
When a user denies ATT, no IDFA is shared and AdMob serves NPA (non-personalized ads). When a UMP user in the EEA refuses, same outcome. NPA eCPM lands somewhere around 0.5x to 0.7x of personalized eCPM based on my own apps and the public benchmarks I've seen. Lifting consent from 60% to 80% in a given country roughly translates to a 10–15% lift in that country's ad revenue. In the country that improved the most for me, the monthly AdMob revenue came out about 12% higher.
The number itself is technically easy to measure. ATT exposes ATTrackingManager.trackingAuthorizationStatus and UMP exposes consentStatus on ConsentInformation; you just log the final state. The hard part is what to compare against and whether you keep comparing. I have twice run a one-off improvement, walked away pleased, and come back six months later to find the rate had slipped eight points because of an OS release or a region rollout. That is what pushed me to treat consent rate as a seasonally variable KPI and to put Antigravity in charge of watching it every week.
Step 1: event schema and BigQuery export
The first job is shaping events at a granularity that a sub-agent can actually use. If you skimp here, no amount of clever prompting downstream will save you. My apps emit the events below through Firebase Analytics and push them to BigQuery.
// iOS (Swift) — log the settled ATT stateimport AppTrackingTransparencyimport FirebaseAnalyticsfunc logAttStatus(geo: String) { let status = ATTrackingManager.trackingAuthorizationStatus let label: String switch status { case .authorized: label = "authorized" case .denied: label = "denied" case .restricted: label = "restricted" case .notDetermined: label = "not_determined" @unknown default: label = "other" } Analytics.logEvent("att_status_settled", parameters: [ "status": label, "geo": geo, "os_major": String(ProcessInfo.processInfo.operatingSystemVersion.majorVersion), "app_version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "", "prompt_variant": currentVariantId ])}
I personally prefer suffixing the event with _settled. A prompt_shown style event tends to drift away from the final outcome, and downstream aggregation gets messy. The geo field uses the region the SDK estimates, not Locale.current. More on why later — I once miscategorized travelers using device locale and it hurt.
On the BigQuery side I build a single normalized view. Sub-agents should never touch raw events; that way the event schema can change underneath without breaking the agent layer.
-- BigQuery: weekly consent rate aggregated by region × OS × variantCREATE OR REPLACE VIEW analytics.consent_rate_weekly ASSELECT DATE_TRUNC(DATE(event_timestamp), WEEK(MONDAY)) AS week_start, app_id, platform, -- ios / android geo, os_major, prompt_variant, COUNTIF(event_name IN ('att_status_settled','ump_status_settled')) AS settled, COUNTIF(event_name = 'att_status_settled' AND status = 'authorized') AS att_yes, COUNTIF(event_name = 'att_status_settled' AND status = 'denied') AS att_no, COUNTIF(event_name = 'ump_status_settled' AND status = 'obtained') AS ump_yes, COUNTIF(event_name = 'ump_status_settled' AND status IN ('required','unknown')) AS ump_noFROM analytics.eventsWHERE _PARTITIONDATE >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)GROUP BY 1,2,3,4,5,6;
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Event schema for normalizing UMP/ATT consent state across region × OS into BigQuery, in a shape Antigravity sub-agents can actually reason over
✦How to split a weekly experiment loop into four sub-agents — observer, hypothesis-writer, variant-implementer, result-judge — with a single human-approval gate
✦Real production pitfalls: trying to re-prompt ATT, initializing AdMob before UMP, mis-detecting EU users by Locale, and missing the consent-withdrawal entry point in the EEA
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Step 2: splitting the work across Antigravity sub-agents
Now the Antigravity side. Pushing all of this into one giant agent blows up both token cost and debugging cost, so I deliberately split it into four roles. My AGENTS.md carries something like the snippet below.
# AGENTS.md (excerpt)agents: - id: consent-observer role: Read consent_rate_weekly in BigQuery, compute rates by region × OS × variant and week-over-week deltas, flag anomalies (-5pt WoW), write a markdown report. tools: [bigquery_query, write_markdown] budget: { tokens: 40000, time_min: 5 } - id: hypothesis-writer role: Take the observer report and write at most three improvement hypotheses. Output structured YAML — target geo, variant diff, expected lift, risk. tools: [read_markdown, write_yaml] budget: { tokens: 60000 } - id: variant-implementer role: Translate the chosen hypothesis into a Remote Config parameter diff and open a PR. Only i18n keys may change; native code is off-limits (safety rail). tools: [git, firebase_remote_config_api] budget: { tokens: 80000 } guard: human_approval_required - id: result-judge role: Seven days after launch, compare consent rate between control and treatment, run a z-test, and either propose a promotion PR or a rollback PR. tools: [bigquery_query, git] budget: { tokens: 50000 }
The single most important line in this whole config is guard: human_approval_required on variant-implementer. Consent UX is adjacent to legal copy. I once let the gate off, the hypothesis-writer proposed "Allow us to improve your ad experience" as a variant, and variant-implementer opened a PR with that exact string. A lawyer friend later flagged it as the kind of phrasing that could draw a review rejection. The gate is non-negotiable since then.
Step 3: wrap the system prompt with a pre-permission screen
The implementation core is to not call the system prompt directly. Apple's Human Interface Guidelines recommend a pre-permission screen, and the system dialog only fires once, so a moment of context in front of it matters a lot. In my apps, just adding a pre-permission screen lifted the ATT consent rate by an average of 17 points across the four largest countries.
copyVariant is fetched from Remote Config. The variant-implementer agent only modifies these Remote Config values, never the Swift source. As a result, the PR review collapses to "does this copy make sense?" — short, focused, and friendly to review on a phone.
Step 4: change "when and how to ask" per region
The same app needs different consent UX in different regions. Here is the pattern I see and how I respond to each.
EU and UK (GDPR / UK GDPR): legal granularity is required, so I keep UMP's standard form with per-purpose toggles. Sweeping all-in consent is review risk. Pre-permission is fine, and copy that explicitly says "personalize ads" actually outperforms vague "improve the service" copy.
US and Canada: ATT consent skews higher than average. Adding pre-permission reliably lifts another 10–20 points. Privacy literacy is high, but users will say yes when the value is concrete.
Japan: ATT consent is among the lower-rate regions. Pre-permission works, but copy that frames it as "to keep the app free" pulls more than copy that mentions ads — as long as it doesn't contradict the app's own concept.
Southeast Asia and India: less psychological resistance to tracking, but slow networks make pre-permission images load late, which kills the screen. Compress images to under 30 KB as WebP.
Mainland China: AdMob and Firebase do not work for a chunk of users here, so the geo detector must split this traffic out before the consent flow runs at all. Skip this and you ship broken dialogs.
Geo detection itself uses three signals voted in priority — IP-based geolocation, Locale.Region, timezone. Early on I detected region from Locale.current.regionCode alone, and Japanese users traveling overseas were getting the wrong consent UX. Multi-signal voting is the only durable answer.
Step 5: the weekly autonomous loop
Finally, how this runs every week. The Antigravity Background Agent fires every Monday at 02:00 JST and walks the pipeline below.
After running this loop for two months, four of my six apps lifted consent in their top countries by an average of 8 to 15 points, and total monthly AdMob revenue across the six landed about 11% higher. The other two apps were already at a high baseline and barely moved. That is meaningful information — running experiments when there's no headroom just burns time, so result-judge carries a rule that stops experiments for a country after three consecutive insignificant rounds.
Pitfalls I actually walked into
A few things I broke in production, so you do not have to.
Re-prompting ATT is essentially impossible, and twice I almost wrote a "rates are low, let's re-prompt them" routine. Calling requestTrackingAuthorization when the status is not notDetermined simply returns the existing status with no UI. If you build a re-prompt UI without noticing, users see the same pre-permission over and over. The hypothesis-writer agent carries an explicit "exclude denied ATT users from re-prompting" instruction.
Initializing the AdMob SDK before the UMP form returned meant my first-session impressions were all NPA. The fix is to scope Mobile Ads SDK initialization inside the requestConsentInfoUpdate completion callback. A small BigQuery check on first-session NPA ratio catches this regression quickly.
The other one was forgetting to add a consent-withdrawal entry point in the EEA. EU / UK regulations recommend an "ad settings" item that lets users withdraw. Withdrawal mechanically lowers the denominator, which feels like a bad trade, but skipping it creates a worse class of risk.
A quiet closing
Consent UX gets overlooked, and yet it is one of the most direct UI levers an indie developer has on revenue. I have re-learned this several times across ten-plus years of running apps, and each time I think to myself "I should have systematized this earlier." Handing observation and PR drafting over to Antigravity is what finally turned this from "improve it when I remember" into a weekly routine.
If you already have ATT and UMP integrated but have not added a pre-permission screen yet, that is by far the highest-leverage first move — try it in one country in one app and watch it for two weeks. From there, sub-agents quietly carry the work forward, and the number of at-bats per month takes care of itself.
Thank you for reading. If you are also running apps as an indie developer over the long haul, I hope something here saved you a step.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.