mcpbeat

Win Back Offers

rshankras/win-back-offers

Generates the complete win-back offer flow for churned subscribers — StoreKit Message API handling, eligibility verification, offer sheet presentation, and analytics. Use when implementing win-back campaigns or re-engagement for lapsed subscribers.

6k tokens
context cost
the whole folder, loaded on every use
2
files
instructions only
0
copies elsewhere
how many repositories repackaged it
585
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/rshankras/claude-code-apple-skills --skill win-back-offers

The instruction itself

20 sections, as written by the author

Win-Back Offers Generator

Generate a complete win-back flow for recovering churned subscribers using StoreKit 2's win-back offer APIs (iOS 18+), Message API handling, and fallback promotional offers for older OS versions.

When This Skill Activates

Use this skill when the user:

  • Asks to "win back" or "re-engage" churned subscribers
  • Mentions "lapsed subscribers" or "subscriber recovery"
  • Wants to implement "win-back offers" (iOS 18+)
  • Asks about "subscription churn" reduction
  • Mentions "Message API" for subscription offers
  • Wants to reduce subscriber attrition

Minimum Eligibility

Win-back offers involve meaningful engineering and ASC configuration. Check these thresholds before proceeding:

| Churned Subscribers | Recommendation |

|----|---|

| Under 50 | Not worth the setup effort. Use personal email outreach instead — write individual messages to churned users. Focus energy on preventing churn (improve onboarding, engagement). |

| 50-500 | Implement basic win-back via promotional offers (simpler than full win-back flow). Use offer codes distributed via email. |

| 500+ | Full win-back implementation justified. Native iOS 18+ win-back offers, Message API handling, automated pipeline. |

If you don't know your churn numbers: Check App Store Connect > Subscriptions > Retention metrics. If your total subscriber base is under 200, you likely don't have enough churn volume to justify this skill yet.

Pre-Generation Checks

1. Project Context Detection

  • [ ] Check for existing StoreKit implementation
  • [ ] Check deployment target (iOS 18+ for native win-back, iOS 16+ for promotional offer fallback)
  • [ ] Look for existing subscription management code
  • [ ] Check if subscription-offers skill was already used
  • [ ] Check churn volume — is the number of churned subscribers worth automating?

2. Conflict Detection

Glob: **/*WinBack*.swift, **/*Reengag*.swift
Grep: "winBackOffers" or "Message.messages" or "presentWinBackOffer"

Configuration Questions

Ask user via AskUserQuestion:

  • Win-back approach
  • Native win-back offers only (iOS 18+ required)
  • Promotional offer fallback (supports iOS 16+)
  • Both (recommended)
  • Message handling
  • Handle App Store messages in-app (recommended)
  • Let system handle messages
  • Custom message presentation
  • Analytics integration
  • Basic (print logging)
  • Custom analytics protocol
  • Specific SDK (specify which)

Generation Process

Step 1: Read Templates

Read templates.md for all win-back implementation code.

Step 2: Create Core Files

  • WinBackOfferManager.swift — Win-back offer detection, eligibility, and presentation
  • WinBackMessageHandler.swift — StoreKit Message API integration for in-app offer display
  • WinBackEligibility.swift — Churn detection and eligibility logic
  • WinBackOfferView.swift — Custom win-back offer presentation UI

Step 3: Create Optional Files

  • WinBackAnalytics.swift — Track win-back funnel metrics
  • WinBackFallback.swift — Promotional offer fallback for iOS < 18

Step 4: Determine File Location

- If Sources/Store/ exists → Sources/Store/WinBack/
- If Store/ exists → Store/WinBack/
- Otherwise → WinBack/

Output Format

Files Created

Store/WinBack/
├── WinBackOfferManager.swift      # Core win-back logic
├── WinBackMessageHandler.swift    # Message API handling
├── WinBackEligibility.swift       # Churn detection
├── WinBackOfferView.swift         # Custom offer UI
├── WinBackAnalytics.swift         # Funnel tracking (optional)
└── WinBackFallback.swift          # iOS < 18 fallback (optional)

Integration Steps

App Entry Point — Message Handling:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .storeMessagesTask { message in
                    // Handle win-back messages from App Store
                    await WinBackMessageHandler.shared.handle(message)
                }
        }
    }
}

Check Win-Back Eligibility:

let manager = WinBackOfferManager()
let winBackOffer = await manager.checkWinBackAvailability(
    productID: "com.app.subscription.monthly"
)

if let offer = winBackOffer {
    // Show win-back UI
    showWinBackSheet(offer: offer)
}

Present Win-Back Offer:

// Native (iOS 18+) — raw StoreKit path
if case .verified(let renewalInfo) = status.renewalInfo,
   let bestID = renewalInfo.eligibleWinBackOfferIDs.first,   // first ID = best offer
   let offer = product.subscription?.winBackOffers.first(where: { $0.id == bestID }) {
    let result = try await product.purchase(options: [.winBackOffer(offer)])
}

// With StoreKit views, choose the merchandised offer instead
SubscriptionStoreView(groupID: groupID)
    .preferredSubscriptionOffer { product, subscription, offers in
        offers.first { $0.type == .winBack }
    }

Hide win-back merchandising from anyone still entitled with auto-renew on — the offer is for lapsed subscribers only.

Where Offers Surface (WWDC24)

The App Store does the distribution — eligible lapsed subscribers see your win-back offer on:

  • Your product page (offer card)
  • Today / Games / Apps editorial placements
  • The App Store's Subscriptions management page
  • In-app, via the APIs above

Streamlined purchasing is ON by default: users can redeem on the App Store before ever reopening your app, so the transaction arrives via Transaction.updates — listen from launch. If you disable streamlined purchasing, implement PurchaseIntent to complete the purchase in-app.

Testing Instructions

  • Sandbox Account: Create a subscriber, cancel, wait for expiry — and enable "Display Win-back Offers" in the sandbox account settings
  • StoreKit Testing: Use Transaction Manager to simulate churn; the Xcode StoreKit configuration file has per-offer eligibility toggles to simulate any eligibility state
  • Message Testing: Test App Store message presentation
  • Verify flow: Churned → Offer shown → Purchase → Active subscriber

App Store Connect Setup

  • Go to Subscriptions > Your Group > Win-Back Offers
  • Create offer with pricing and duration
  • Set the three declarative eligibility knobs (WWDC24):
  • Minimum paid subscription months — most recent *consecutive* paid months; any subscription in the group counts, and upgrades/downgrades don't reset the count
  • Time since last subscribed — both a minimum AND a maximum, in months
  • Redemption cooldown (optional) — wait between redemptions, starting after the offer period ends
  • Worked example: paid ≥ 3 months, lapsed 2–24 months, 6-month cooldown, offer = 1 month free
  • Configure offer priority if multiple offers exist — it decides which offer is "best" when several are eligible

Config gotchas:

  • Offer identifier and pricing are immutable after creation
  • Eligibility criteria lock at the offer's start date
  • Stopping App Store promotion requires deleting the offer

Retention Messaging (At-Cancel Save Offers)

> The App Store Connect tier is open to all developers (WWDC26); the real-time server API is gated behind an access-request form + a mandatory sandbox performance test. (Per the session "Explore Retention Messaging in App Store Connect".)

The App Store presents your message on the cancel confirmation page — the last screen before a subscriber churns. It complements win-back rather than replacing it:

| Mechanism | Fires |

|-----------|-------|

| Retention messaging | At cancel — before the user churns |

| Win-back offer | After lapse — once the subscription has expired |

Configure both — they cover opposite ends of the churn window.

Mechanics (from Apple's session):

  • Three formats: message only · message + image (from the Asset Library) · message + retention offer.
  • Attach multiple offers to one subscription with confidence — "the App Store will automatically choose the best offer for eligible customers."
  • Setup (no server needed): ASC → Subscriptions → Retention Messaging → name it, write the message, optionally attach image + offers, pick subscriptions, test in sandbox first. Also configurable via the ASC API.
  • Real-time variant: the App Store calls your endpoint for a per-customer message, falling back automatically (your ASC messages → API defaults → system fallback) if it times out — so always configure the ASC tier even when using real-time.
  • Apple's observed impact: average save-rate lift +1.4 points (~82%); messages carrying a promotional offer reached +5.5 points (~223%) — with "varying results across developers." (Save rate = % of subscribers who keep the subscription after reaching the cancel confirmation page.)

References

  • templates.md — All production Swift templates
  • Related: generators/subscription-offers — All offer types including win-back
  • Related: generators/paywall-generator — Full paywall implementation
  • Related: app-store/marketing-strategy — Win-back campaign planning

How to use it

Copy the folder

Take rshankras/win-back-offers from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.