Executive summary · Section 12
12. Tracking: iOS-first
Exists today (site README):
dpd_utmcookie captures UTMs and click ids./api/checkoutstampsattr_*on Stripe.- GA4 fires
checkout_startandcws_click;/thankssends Purchase to the pixels. - GTM only for tags not in code.
- The Neon
runstable recordsplatform;upsertAppleCustomerstores no channel.
12a. App Store campaign links
-
Format:
https://apps.apple.com/app/apple-store/id6808699477?pt=<PROVIDER_TOKEN>&ct=<campaign>&mt=8ptis your provider token (App Store Connect → App Analytics → Sources → Campaigns → “Generate campaign link”).- Keep
ctshort; Apple limits its length.
-
Naming:
ct=<source>_<campaign>, mirroring UTMs.- Paid and owned:
google_mobile,pinterest_promoted,fb_retarget - Organic and bios:
ig_bio,tiktok_bio,x_launch,reddit_answer - Offline and partners:
flyer_parents,director_email,creator_<name>
- Paid and owned:
-
On the site: rewrite every App Store href client-side so
ct= the visitor’sdpd_utmsource and campaign (fallbacksite_direct).- Do this in
components/Tracking.tsx, next to the existingcws_clickhandler. - Then Google, Pinterest and Meta visitors who tap the badge arrive in App Analytics under their channel.
- Do this in
-
Caveats:
- App Analytics only reports users who opted in to share analytics with developers, so treat the numbers as directional and undercounted.
- The Smart App Banner doesn’t carry a campaign token, so banner installs won’t map to a channel. Keep the banner (conversion matters more than attribution).
- What you get by
ct: impressions, product page views, installs and, I believe, proceeds/sales. Confirm in your App Analytics UI which of these show per campaign.
12b. App Store click events
| Platform | Event | Fire on | Notes |
|---|---|---|---|
| GA4 | appstore_click (key event) | Any link to apps.apple.com | Params: placement (hero / pricing / guide_step / footer / banner_cta), page_path, utm_source from cookie. Mirror the cws_click code path. |
| Google Ads | Import appstore_click | — | Primary conversion for the mobile campaign until in-app purchases can be attributed. purchase stays primary for desktop. |
| Meta Pixel | trackCustom('AppStoreClick') (or the standard Lead if you prefer optimization stability) | Same click | Use it for retargeting optimization. Aggregated Event Measurement priority: Purchase > AppStoreClick > InitiateCheckout. |
| Pinterest tag | lead or a custom event | Same click | Lets the promoted campaign optimize. |
| Reddit pixel | Lead | Same click | For the future, if paid Reddit ever restarts. |
| TikTok pixel | Only if Spark Ads start (via GTM) | — | — |
12c. iOS purchase attribution (closing the CAC gap)
-
Per channel, weekly:
- CAC_ios ≈ channel spend × (share of that channel’s App Store clicks) ÷ App Store licenses attributed to that
ctin App Analytics. - CAC_web = spend ÷ Stripe
attr_utm_source. - Report both, plus blended.
- CAC_ios ≈ channel spend × (share of that channel’s App Store clicks) ÷ App Store licenses attributed to that
- Apple Ads reports its own installs natively. Count licenses from App Store Connect subscription reports for the Apple Ads campaign window.
- Optional, privacy-preserving: when
upsertAppleCustomerruns, store the App Store campaign (not the user) if your server-side receipt or notification data exposes it. Unverified whether it does; check App Store Server API / notification fields. Don’t add any SDK. - Neon/admin: split
runsbyplatform(iosvs.chrome). Preview → paid by platform is the core iOS-first funnel metric.
12d. UTM convention (unchanged, plus ct)
| Param | Values |
|---|---|
utm_source | google · bing · apple_ads · facebook · instagram · pinterest · reddit · tiktok · x · email · flyer · director |
utm_medium | cpc · social · pin · group · email · print · referral |
utm_campaign | procare_mobile, procare_desktop, leaving, retarget, promoted, launch, holiday, bio, <post-slug> |
utm_content | angle and creative, e.g. a_loss_phone, c_ritual_calendar, b_share_airdrop, d_tonight_laptop |
App Store ct | <source>_<campaign>, as above |
Legacy links (utm_source=social&utm_medium=bio, utm_medium=pin) stay live; map them in the CAC script.
12e. Setup checklist
- GA4: add
appstore_click; confirmcheckout_start,cws_clickandpurchaseare key events; fix the Data APIanalytics.readonlyscope. - App Store Connect: generate campaign links for every
ctabove; rewrite site App Store hrefs with a dynamicct. - Google Ads: import
appstore_click; device-split campaigns; auto-tagging on. - Meta:
AppStoreClickcustom event and custom conversion; AEM priority; Conversions API only if spend exceeds ~$5/day. - Pinterest and Reddit: lead event on App Store click; test with one tap each.
- One end-to-end test on an iPhone: ad URL → landing page → badge tap (check GA4 and pixels fire, and that the App Store URL carries
ct) → in-app purchase (sandbox/TestFlight) →apple_activatedrow in Neon. - One desktop test purchase (then refund) to confirm Stripe
attr_utm_sourceand the Purchase pixels. - Weekly (Mondays, 20 min):
npm run cac -- --live --spend …(web); App Analytics by campaign plus subscription report (iOS); Apple Ads dashboard; Log the results under a dated “Results” heading inpaid-channels-strategy.md