From 91ab778e83b67b03fae8b89a2e9b5092e7b21a18 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 9 Jul 2026 16:31:48 +0600 Subject: [PATCH] [2026-07-09] eagle: work/projects/ios-subscription-day7-promo-pixels.md --- .../ios-subscription-day7-promo-pixels.md | 70 +++++++++++++------ 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/work/projects/ios-subscription-day7-promo-pixels.md b/work/projects/ios-subscription-day7-promo-pixels.md index 92549394..0b7ebb7d 100644 --- a/work/projects/ios-subscription-day7-promo-pixels.md +++ b/work/projects/ios-subscription-day7-promo-pixels.md @@ -14,12 +14,48 @@ ## Objective -- Define three new pixels (impression, tap, dismiss) for the day-7 existing-user promo half-sheet. -- Mirror the parameter shape of the existing `m_privacy-pro_skipped_onboarding_promotion_*` pixels to allow cross-audience comparison. +- Instrument the day-7 existing-user promo half-sheet with impression, tap, and dismiss pixels. - These are permanent pixels (no expiry) — the feature is not an experiment. - The origin value `funnel_modal_ios__subscriptionnudge` flows downstream automatically to `m_privacy-pro_offer_screen_impression` and `m_subscribe` without additional work. -## Testing the feature +## Android reference + +Android uses a **single shared pixel** (`AppPixelName.ONBOARDING_DAX_CTA_SHOWN`, ctaId `DAX_INTRO_PRIVACY_PRO`) for both the reinstaller promo and the day-7 promo, with **no `origin` pixel parameter** — origin is only embedded in the subscription purchase URL (`https://duckduckgo.com/pro?origin=…`) for downstream funnel attribution: + +| Android audience | Pixel name | `origin` in URL | +|---|---|---| +| Reinstaller / skipped onboarding | `ONBOARDING_DAX_CTA_SHOWN` | `funnel_skippedonboarding_android` | +| Day-7 existing users (new) | `ONBOARDING_DAX_CTA_SHOWN` | `funnel_newusermodal_android` | + +The two audiences are **indistinguishable at the pixel level** on Android — analysis relies on the origin flowing to downstream purchase pixels. Source: [Android PR #8369](https://github.com/duckduckgo/Android/pull/8369), `BrowserTabViewModel.kt` + `CtaViewModel.kt`. + +Android pixel definition / privacy triage tasks cover Phase 1 paywall instrumentation only (`m_subscription_paywall_not_seen_*`) — the promo modal itself got no separate pixel definition or privacy triage: +- [Define pixels to track users who haven't seen the subscription paywall](https://app.asana.com/1/137249556945/task/1213904100962479) +- [Privacy Triage: Android - Added new subscription_promo_shown pixel param](https://app.asana.com/1/137249556945/task/1214025242626360) + +--- + +## Option A — Recommended: Reuse existing pixels (mirrors Android approach) + +Reuse `m_privacy-pro_skipped_onboarding_promotion_impression/tap/dismiss` for the day-7 audience. Audience attribution relies on the origin URL parameter (`funnel_modal_ios__subscriptionnudge`) flowing to downstream purchase pixels — identical to Android's approach. + +**No new pixels required. No privacy triage required for new pixels.** + +| Pixel | Trigger | +|---|---| +| `m_privacy-pro_skipped_onboarding_promotion_impression` | Day-7 half-sheet shown | +| `m_privacy-pro_skipped_onboarding_promotion_tap` | User taps CTA | +| `m_privacy-pro_skipped_onboarding_promotion_dismiss` | User dismisses without tapping CTA | + +**Trade-off:** Cross-audience analysis (reinstallers vs day-7 users) is not directly available from pixels alone; it requires joining on origin via downstream `m_privacy-pro_offer_screen_impression` / `m_subscribe` pixels. + +--- + +## Option B — Alternative: Separate pixels per audience + +Define three new pixels exclusively for the day-7 audience. Provides direct per-audience split in pixel dashboards without filtering by origin. Requires a new privacy triage. + +### Testing - Feature is implemented in: `iOS/DuckDuckGo/SubscriptionPromo/SubscriptionPromoExistingUserCoordinator.swift` - Test paths: @@ -30,11 +66,9 @@ - Tap CTA → verify `m_privacy-pro_existing_user_promotion_tap` fires and subscription flow opens with origin `funnel_modal_ios__subscriptionnudge` - Re-install and dismiss instead → verify `m_privacy-pro_existing_user_promotion_dismiss` fires ---- +### Pixel definitions -## Final pixels (for triage) - -### `m_privacy-pro_existing_user_promotion_impression` +#### `m_privacy-pro_existing_user_promotion_impression` - **Trigger:** The day-7 promo half-sheet is shown to an existing user (7+ days since install) who has never seen any subscription offer. - **Frequency:** Once per install (guarded by `daxDialogsSettings.subscriptionPromotionDialogShown`). @@ -45,7 +79,7 @@ - `free_trial` — `true` / `false` — whether the user is eligible for a free trial at time of display - **Suffixes:** `_ios`, `_phone` / `_tablet` -### `m_privacy-pro_existing_user_promotion_tap` +#### `m_privacy-pro_existing_user_promotion_tap` - **Trigger:** User taps the CTA button ("Try It Free" or "Learn More") on the promo modal. - **Frequency:** Standard (can fire once per impression, since the modal is shown at most once). @@ -53,7 +87,7 @@ - **Parameters:** same as impression — `appVersion`, `ru`, `free_trial` - **Suffixes:** `_ios`, `_phone` / `_tablet` -### `m_privacy-pro_existing_user_promotion_dismiss` +#### `m_privacy-pro_existing_user_promotion_dismiss` - **Trigger:** User dismisses the promo modal (taps X or swipes down) without tapping the CTA. - **Frequency:** Standard. @@ -61,24 +95,20 @@ - **Parameters:** same as impression — `appVersion`, `ru`, `free_trial` - **Suffixes:** `_ios`, `_phone` / `_tablet` ---- - -## Behavior rules +### Behavior rules - Impression and tap/dismiss are mutually exclusive per install — the modal is shown at most once. - `ru` and `free_trial` are evaluated at time of display, same as in the skipped-onboarding promo. - The dismiss pixel fires on both explicit close (X button) and swipe-to-dismiss; neither path fires the tap pixel. - If the user taps CTA, tap fires but dismiss does not. +**Trade-off:** Three new pixels to triage. Structurally identical to `m_privacy-pro_skipped_onboarding_promotion_*` — no new parameter types are introduced. + --- ## Notes and status -- Pixel definitions are committed to `iOS/PixelDefinitions/pixels/definitions/subscription.json5`. -- These pixels are structurally identical to `m_privacy-pro_skipped_onboarding_promotion_*` (same parameters, same suffixes) — no new parameter types are introduced. -- Android equivalent pixels: Android uses a **single shared pixel** (`AppPixelName.ONBOARDING_DAX_CTA_SHOWN`, ctaId `DAX_INTRO_PRIVACY_PRO`) for both the reinstaller promo and the day-7 promo — the two audiences are distinguished solely by the `origin` field: - - Reinstaller/skipped onboarding: `origin = "funnel_skippedonboarding_android"` - - Day-7 existing users: `origin = "funnel_newusermodal_android"` - iOS takes a more granular approach with separate pixel names per audience (`m_privacy-pro_skipped_onboarding_promotion_*` vs `m_privacy-pro_existing_user_promotion_*`), which allows direct per-audience funnel analysis without filtering by origin. Source: [Android PR #8369](https://github.com/duckduckgo/Android/pull/8369), `CtaViewModel.kt`. -- Origin `funnel_modal_ios__subscriptionnudge` needs formal approval via the [Origin Proposal form](https://form.asana.com/?k=Wy3aY17vglr2bihpUHoyBA&d=137249556945) — tracked in the origin request subtask above. -- Privacy triage is required for the new pixels — see `ios-subscription-day7-promo-privacy-triage.md`. +- Option A pixels are already committed to `iOS/PixelDefinitions/pixels/definitions/subscription.json5` as `m_privacy-pro_skipped_onboarding_promotion_*`; reusing them for this audience requires no code changes to pixel definitions. +- If Option B is chosen, new definitions must be added to `subscription.json5` and a privacy triage opened. +- Origin `funnel_modal_ios__subscriptionnudge` needs formal approval via the [Origin Proposal form](https://form.asana.com/?k=Wy3aY17vglr2bihpUHoyBA&d=137249556945) — tracked in the origin request subtask above — regardless of which option is selected. +- Privacy triage draft (for Option B): see `ios-subscription-day7-promo-privacy-triage.md`.