The app funnel — iOS and Android, separately

11 August 2026. Commissioned by Aga after the App Store delete rate looked alarming. Run by the growth fleet, then blocked twice — once by the growth Skeptic and once by the Data Verification Officer — and rewritten against both. Ten of the first draft's claims did not survive, including four of my own — the last being my own "Android build defect" theory, killed by Google's own crash data. Those corrections are kept visible below rather than quietly fixed.

The short version

1. The 52.7% "delete rate" is not a delete rate. It is 29 delete events ÷ 55 install events over the same six days. A delete logged on 28 Jul can belong to an install from May. It does not mean half our installs get deleted, and it is roughly the expected shape for a paywalled fitness app.

2. The alarming number was the wrong platform. Blended, the funnel says 74% of people who finish onboarding start a workout. Split: iOS 99.7%, Android 41.1%. And the paywall converts 18.0% on iOS against 7.5% on Android. iOS is already performing at the level the first draft proposed as a target. The problem is Android.

3. Apple Search Ads is SOLVED — cost is no longer unknowable. AGA-153 closed the same day; the OAuth chain was proven live. Apple's own numbers, 90d: $407.32 spend (~$136/mo), 163 tap-installs, $2.50 each. Only three campaigns run and all are Branded, at ~$1.19/install. The five paused ones were the non-branded experiment: $230.45 for 15 installs (~$15 each) — already off. 🔴 CAC/ROAS still unpublished: the old "$18.98/install" was revenue, not cost, and its denominator does not match Apple's.

4. And we now know where Android breaks — it is sign-in, and it is not a crash. Play vitals say 0.00% crash rate; holding geography constant to buyer countries says it is not the cheap-geo wave either. What is left: iOS logs in 98.8% of onboarders, Android 68.0%, and social_login_attempted fires 103× on iOS and 0× on Android.

5. None of this should jump the queue. The whole app funnel produces ~50 trial starts per 28 days. A best-case Android fix is worth roughly $150–250/mo, arriving in weeks through App Store review. The web quiz sold 15 subscriptions for $1,298 in the first eleven days of August. Against a $6,136/mo gap with 79 days left, the app rail is a second-priority lane.

1 · What the first draft got wrong

Recorded in full because each one nearly shipped to Aga as fact.

ClaimVerdictThe truth
"No Apple Search Ads credential exists anywhere in the repo"REFUTED APPLE_ADS_PRIVATE_KEY_B64 is in .env.local. The blocker is the un-uploaded public key.
"ASA revenue was measured once"REFUTED Measured at least four times, four different answers, none reconciled.
"$30.56 revenue per install — the highest of any paid source"CORRECTED That was the best of ten ASA campaigns, seven of which returned $0.00. Channel-level: $18.98/install.
"ASA 27 installs → $200 = $7.41/install"REFUTED A units error. 7.4% is a conversion rate (install→purchase), not dollars. Struck entirely.
"333 impressions/day — a thin pipeline"CORRECTED 665/day. The discovery report covers 3 days, not the installs block's 6. Exactly double.
"1.75% install conversion"NOT COMPUTABLE It divided a 2-day download count by a 3-day impression count. A rate across two windows is not a rate.
"Per-platform split is unavailable"REFUTED It was one extra GA4 dimension. Pulled live; it is the most important table on this page.
"Android paid installs never converted"CORRECTED 0.05% — 2 purchases from 4,236 installs, $480.06 over 90 days. Terrible, but not zero.
"An Android BUILD defect — crash, failed asset load, broken transition"REFUTED Play vitals: 0.00% crash rate, 0.10% ANR, 25 Jul–9 Aug. Not a crash. Re-diagnosed to sign-in (§2b).
"Play revenue rose while installs collapsed"REFUTED Window mismatch. Over Apr→Jul installs rose 473→5,221. They fell only in August.

The pattern in all nine: a number was read without its window, its denominator, or its selection. That is what the verification laws exist to catch, and they caught it here.

2 · The table that changes the answer

Same GA4 pull, same 28 days, one extra dimension. CHECKED — property 177471782, pulled 11 Aug 2026.

Step (unique users)🍎 iOS🤖 AndroidBlended (what we used to see)
Onboarding started361271640
Onboarding completed318 (88.1%)241 (88.9%)564 (88%)
First workout started317 (99.7%)99 (41.1%)416 (74%)
Workout completed256 (80.8%)64 (64.6%)320
Pricing page seen228134362
Trial started41 (18.0%)10 (7.5%)50 (13.8%)
Read the blended column and you would fix the wrong thing. "26% of people who finish onboarding never start a workout" sounds like an onboarding or content problem across the product. It is not. On iOS that number is 0.3%. Android loses 59% of people between finishing setup and starting their first workout — the same screens, performing nearly perfectly on one platform and badly on the other.

2b · Where Android actually breaks — two hypotheses killed, one survivor

The first draft of this page called it "an Android build defect — a crash, a failed asset load, a broken transition." That was a guess, and the data killed it. Both obvious explanations were tested before anything was blamed.

❌ Hypothesis 1 — it's crashing. REFUTED. CHECKED live 11 Aug
Google Play Developer Reporting API, com.themovementathlete.app, 25 Jul–9 Aug: crash rate 0.00% on every one of 16 days; ANR 0.00% on fifteen days and 1.61% on one (6 Aug), mean 0.10%. The app is not falling over.
❌ Hypothesis 2 — it's the cheap-geo paid wave, not the app. REFUTED. CHECKED live 11 Aug
This was the strong counter-argument: July's Android install wave was 67% never-bought countries, so of course the funnel looks bad. So I held geography constant and compared like with like — buyer countries only (US · UK · CA · DE · AU · FR · NL · IE · SE · NO · DK · CH · AT · BE · NZ · ES · IT · FI):
Buyer countries onlyonboarding donesigned infirst workoutworkout ÷ onboarding
🍎 iOS242239 (98.8%)269111.2%
🤖 Android12585 (68.0%)5745.6%
Same countries, same 28 days, same screens. iOS exceeds 100% because a workout does not require completing onboarding inside the window. The audience is not the explanation.
✅ What survives — the break is at SIGN-IN.
In buyer countries Android converts onboarding→login at 68.0% against iOS's 98.8%. That is a 31-point gap before anyone reaches a workout, and it happens without a single crash.

And the smoking gun: social_login_attempted fires 103 times on iOS and ZERO times on Android across 28 days — while Android alone emits signed_in (18) and signed_up (6), which iOS never fires. The two platforms are not running the same auth.
Event, 28d unique users🍎 iOS🤖 Android
AppOnboarding_completed318241 (0.76× — normal, tracks the population)
login / signed_in_app296149 (0.50×)
social_login_attempted1030
workout_started31799 (0.31×)
The two-minute question this reduces to: does the Android build offer Google / social sign-in on its auth screen, or is it email-and-password only? If social sign-in is missing on Android, that alone explains a 31-point login gap — Google Sign-In is the default expectation on the platform. If it is present, it is firing no analytics and the drop has another cause. Either answer decides the fix. That is NIC-100 — and it is a question, not a bug hunt.
✅ One branch already closed — the Android workout event IS wired. verified 11 Aug, before the devs replied
The obvious challenge to all of this was "maybe workout_started just isn't wired on Android, and the 41.1% is a measurement artefact." It is wired. Aga said so, and GA4 — which is Firebase for the app streams — confirms it: workout_started fires on 28 of 28 days on Android (260 event-instances from 99 unique users). A dead event does not fire every single day for a month.

So the 41.1% is real, and the finding gets stronger rather than weaker. It also sharpens the contrast: a consistently-firing Android event sits right beside social_login_attempted, which returns no Android row at all — a structural zero, not a sampling gap.

🪤 One trap worth recording, because it nearly produced a wrong number here: adding date to a GA4 request makes totalUsers per-row, so summing the rows counts a person once per active day. It inflated this very event from the true iOS 317 / Android 99 to a nonsense 1,256 / 260. Ask without date for unique users; add it only to answer "on how many days did this fire?".

One honesty note on this table. These are event-scoped user counts over a 28-day window, not a sequenced cohort — a person who started a workout may have onboarded before the window opened. They are flow ratios, the same class of statistic as the 52.7%. The proof they are not nested: GA4 reports 102 purchase-users against 50 trial-users, which is impossible in a real funnel (the app's purchase event fires on initiation). Read the split as a strong directional signal about platform difference — which it unambiguously is — not as a cohort conversion rate.

3 · iOS — the store

CHECKED App Store Connect. 🔴 Three different windows live in this data and conflating them is how the first draft went wrong:

BlockWindowFigures
Installs & deletes26–31 Jul (6d)55 installs (9.2/day) · 29 deletes (4.8/day) · net +26
Store discovery29–31 Jul (3d)1,996 impressions · 92 page views (4.61%) · 665/day
First-time downloads31 Jul–1 Aug (2d)35 downloads · 94.3% from search · 74.3% no page view

87.3% of installs come from App Store search — but Apple's "App Store search" bucket contains both organic search and Apple Search Ads placements. It cannot separate paid from organic, so it must never be quoted as an organic figure.

74.3% install without ever opening the product page. That makes the search-result mini-listing — icon, title, subtitle, rating, first screenshot — the actual conversion surface. The product page, the description and the preview video only ever touch the other quarter.

3b · The free lever — and the fix is already written but never shipped

87.3% of iOS installs come from App Store search and 74.3% install without ever opening the product page. That makes the keyword field and subtitle the conversion surface. So I read what is actually live App Store Connect API, 11 Aug:

Version100-character keyword field
1.70.71
LIVE NOW
ab,butt,beachbody,tracker,gym,exercise,diet,planner,trainer,buttock,calisthenics,ladder,routine,diet
1.70.72
WRITTEN, NOT SUBMITTED
bodyweight,workout,plan,progression,home,beginner,strength,mobility,pull,push,handstand,planche,core
The live field wastes roughly 86 of its 100 characters. diet appears twice and Apple ignores the duplicate. calisthenics is already indexed free from the subtitle "Calisthenics & Gymnastics" — 13 characters spent on a word we already own. butt and buttock are both there and neither is what we sell. beachbody is a competitor's brand. Exactly one term describes the product.
And the good version is already written. v1.70.72 carries a clean, relevant set with no duplicates and nothing already-indexed. It has simply never been submitted. So this is not "write new ASO copy" — it is "confirm that release is going out, and when" (NIC-102). Cost: £0.

If we want to go one better, a skill-intent set drops the generic terms for the ones buyers actually search: bodyweight,pullup,pushup,handstand,muscleup,planche,lever,dips,mobility,strength,progression — 92/100 characters, no duplicates, nothing already free from the name or subtitle. Optional; shipping 1.70.72 is the win.

4 · Apple Search Ads — SOLVED. Cost is no longer unknowable.

AGA-153 is CLOSED and the chain was proven live, not claimed 11 Aug 2026
Aga uploaded the public key; Apple issued the OAuth client. The whole path was then exercised end to end — ES256 client-secret JWT → appleid.apple.com/auth/oauth2/token (200) → /api/v5/acls/api/v5/reports/campaigns. org 3090590, The Movement Athlete LTD, USD. Shipped: tools/finance/asa_pull.py (--days / --write / --selfcheck) → data_asa_spend.json, registered in estate_selfcheck as asa-spend. Every "NOT KNOWABLE — ASA spend" statement elsewhere in this estate is now superseded.

Apple's own numbers — 90 days to 11 Aug 2026

This is the platform's own truth and it outranks AppsFlyer, RevenueCat and every doc.

MetricValueNote
Spend$407.32$136/mo — a small line, not a channel bet
Impressions24,737
Taps5452.2% tap-through
Tap-installs163$2.50 each — the honest cost per install
View-installs300🔴 weak attribution — Apple crediting an install to an ad someone saw. Never blend it in silently.

Only three campaigns are running — and all three are Branded

CampaignStatusSpendTap-installsCost each
Branded – US – SRRUNNING$89.1470$1.27
Branded – EURUNNING$73.1266$1.11
Branded – UKRUNNING$14.6112$1.22
Total running$176.87148~$1.19
— the five paused ones, already off —
Competitive – Branded – US – SRPAUSED$78.167$11.17
Non-Branded – US – SRPAUSED$65.573$21.86
Competitive – Non-Branded – US – SRPAUSED$43.080
Non-Direct Competitive – Demand Gen – USPAUSED$27.833$9.28
Competitors_MC_CA-EUPAUSED$15.812$7.91
Total paused$230.4515~$15
The non-branded experiment already ran, and it is already off. Those five paused campaigns are exactly the "can ASA buy NEW demand?" test — $230.45 for 15 installs, about $15 each, one of them returning literally zero — against branded at $1.19. That is a 12× difference between buying people who already know us and trying to buy people who don't. Nothing to pause; it is done.

🔴 The correction: $18.98 was never a cost

The estate has been carrying "$18.98/install" as if it were comparable to a CPI. It is not — that figure is revenue per install (AppsFlyer: $2,277.17 ÷ 120 installs), and the "seven campaigns returned $0.00" in the same line meant $0 revenue, not $0 spend.

So do NOT declare ASA profitable by putting $18.98 next to $2.50. Different sources, different windows, different attribution — Apple counts 163 tap-installs where AppsFlyer counted 120, and neither denominator matches the other. Cost is now CHECKED. CAC and ROAS still require a denominator-matched revenue join and remain unpublished — an honest gap rather than a flattering ratio.
And the standing caveat survives all of this. Every dollar that still runs is Branded — people typing "the movement athlete" who already know us, and for whom our own listing is the top organic result. Cheap CPI is not the same as incremental. $1.19 is a good price for a click we might have had for free; it is not proof ASA created a customer. That question needs a holdout, not a lower CPI.
⚠️ One fragility worth knowing. The whole OAuth client hangs off one person's Apple ID. Remove that user from the Apple Ads account and ASA goes dark again — which is precisely what the asa-spend selfcheck exists to catch. It fails loudly on a revoked grant rather than quietly reporting zeros.

5 · Android — the store

CHECKED Google Play Console, package com.themovementathlete.app.

PeriodDevice installsFrom countries that never bought
Jan–May 2026 (organic baseline)417–522/mo33.9–48.6%
Jun 20264,10969.0%
Jul 20265,22167.2%
Aug 2026 (partial, 11 days)4829.2% — healthiest in the whole series

The Jun–Jul spike was the paid install wave into low-ARPU geographies — July was 33.2% India, 17.5% South Africa, 10.9% Pakistan, 10.3% Peru. It converted at 0.05%: 2 purchases from 4,236 installs, $480.06 over 90 days.

Android organic installs have probably fallen hard — and it does not matter this quarter. 48 installs in 11 days against a ~470/mo baseline is too large a drop for reporting lag alone to explain. But there is no lever: Google retired install-by-channel reports in 2021, so organic and paid cannot even be separated from the store, and Play revenue is renewals from the existing subscriber base, not this month's installs. Pull the closed month on 1 Sep and settle it then. Do not spend founder time on it now.

6 · What to actually do

Now — costs nothing, no queue jumped

A. ✅ DONE — Apple Search Ads is instrumented. Key uploaded, OAuth proven, puller shipped and selfchecked. ASA now reports its own spend daily. What remains is not more spend: it is a denominator-matched revenue join before anyone quotes a CAC. closed 11 Aug

B. iOS title, subtitle and keyword field. 87.3% of installs come from search and 74.3% never open the product page, so these three fields are the conversion surface. £0, no app release. FLEET drafts

C. Pull closed-month Play installs on 1 Sep and settle the Android organic question with a complete month instead of a partial one. FLEET — scheduled

Next — only after the web quiz and retention have what they need

D. Answer the Android sign-in question. Not a bug hunt any more — crash and audience are both ruled out, and the break is localised to sign-in (68.0% vs 98.8% in the same countries, with social_login_attempted at zero on Android). One question: does the Android build offer Google / social sign-in? Nic — 2 minutes

E. Show the product before the paywall on Android. A free app signup gets setup questions, an example workout, then a price — it never sees the assessment, the placement or the skill tree it is being asked to pay for. Worth ~$150–250/mo at best MODELLED, weakly testable (blended metric, no A/B split, App Store review lag, promo confounds). Do not promise a measurable result.

What we are deliberately NOT recommending: raising the paid envelope (£896 of ~$3,000–3,175 is unspent — budget is not the constraint), launching new ASA campaigns before spend is readable, or acting on the Android August number before the month closes.

7 · Provenance

Every figure on this page traces to a platform's own report, pulled 11 Aug 2026: iOS store → App Store Connect via tools/aso/aso_daily.py · Android store → Play Console via tools/finance/play_install_reports.py · in-app funnel and the platform split → GA4 property 177471782 via tools/app-analytics/app_funnel_by_platform.py · app revenue → data_app_proceeds.json (NET, both stores) · ASA spend → Apple Search Ads Campaign Management API v5 via tools/finance/asa_pull.py (platform own truth) · ASA attribution → AppsFlyer partners_report/v5 and RevenueCat · MRR → canonical_facts.json.

Known-unknowns, stated rather than filled in: ASA CAC and ROAS (cost is now CHECKED; the revenue join is not denominator-matched yet) · the Android organic-vs-paid split (impossible from Play) · any cohort delete rate on iOS · a true sequenced funnel per platform · the April 2026 Play figure of £100.00, which the source file itself contradicts and which is marked UNVERIFIED.

Gates run: growth Skeptic (BLOCK → corrections applied) · Data Verification Officer (BLOCK → corrections applied) · company timeline (DECOMPOSE FIRST — the 2–12 Jul $297 lifetime sale sits inside the 90-day ASA window and the Jun–Jul Android months).

← back to the cockpit