<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>RevenueCat Blog</title>
    <link>https://www.revenuecat.com/blog</link>
    <description>Insights about subscription apps, monetization and growth — by RevenueCat.</description>
    <language>en-US</language>
    <lastBuildDate>Fri, 13 Nov 2026 16:44:42 GMT</lastBuildDate>
    <atom:link href="https://www.revenuecat.com/blog/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title><![CDATA[Apple’s metadata API changes: no action needed with RevenueCat]]></title>
      <link>https://www.revenuecat.com/blog/engineering/apple-in-app-purchase-metadata-api-changes</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/apple-in-app-purchase-metadata-api-changes</guid>
      <pubDate>Fri, 13 Nov 2026 16:44:42 GMT</pubDate>
      <dc:creator><![CDATA[Perttu Lähteenlahti]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[We’re migrating our product editing tools to Apple’s new App Store Connect APIs. You don’t need to update your SDK or change your RevenueCat configuration.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/45d3d34cc390a6ce1f1d3bfc1c7ba3317d2eef9e-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Apple recently emailed developers about changes to the App Store Connect API, warning that deprecated In-App Purchase and subscription endpoints will begin to be removed in early 2027. If you received that email and use RevenueCat, here’s what it means for your app.</p>
<p>First of all, <strong>you don’t need to update your RevenueCat SDK or change your configuration because of Apple’s In-App Purchase metadata API changes</strong>. Your purchases, renewals, restores, entitlements, webhooks, and product sync are unaffected.</p>
<p>RevenueCat uses the affected App Store Connect endpoints in our product editing tools, but  we’re migrating those tools to Apple’s replacements and will complete the migration before Apple removes the deprecated endpoints.</p>
<h2>What Apple is changing</h2>
<p><a href="https://developer.apple.com/documentation/appstoreconnectapi/app-store-connect-api-4-4-1-release-notes"><u>App Store Connect API 4.4.1</u></a> introduced a version-based workflow for In-App Purchase, subscription, and subscription group metadata. Previously, localizations and images were attached directly to an In-App Purchase, subscription, or subscription group. To change them, you updated the metadata on that product.</p>
<p>With the new workflow, localizations and images belong to a version: a container for metadata that goes through App Review. You create a new version when making changes, keeping the proposed updates separate from the metadata already live on the App Store.</p>
<p>Existing integrations can still use the deprecated endpoints during the transition. <a href="https://developer.apple.com/documentation/appstoreconnectapi/migrating-in-app-purchase-metadata-to-v2"><u>Apple’s migration guide</u></a> explains the replacement workflow.</p>
<h2>What this means for RevenueCat customers</h2>
<p>These changes affect how RevenueCat creates or edits product metadata in App Store Connect through <a href="https://www.revenuecat.com/docs/offerings/product-editor"><u>Product Editor</u></a> and the product tools available through our MCP server.</p>
<p>They don’t affect the services your app uses to sell subscriptions and deliver access:</p>
<ul>
<li>Purchase processing and receipt validation</li>
<li>Renewals and restores</li>
<li>Entitlements</li>
<li>Webhooks and integrations</li>
<li>Product import and sync from App Store Connect</li>
</ul>
<p>There’s no minimum RevenueCat SDK version for this migration, and you don’t need to change your RevenueCat configuration.</p>
<h2>Migration status</h2>
<p>We're migrating Product Editor and the MCP product tools to the version-based endpoints. We'll complete the migration before Apple's removal date, and we're targeting December 2026. Until then, these features keep working on the current endpoints, which Apple still supports.</p>
<p>The new flow has more steps on Apple's side. Products need a draft version, localizations are attached to that version, and the version goes through a review submission. RevenueCat will handle all of that for you. You'll keep using Product Editor the same way.</p>
<h2>FAQ</h2>
<h3>Has RevenueCat migrated off the affected endpoints? </h3>
<p>Not yet. We use them only in Product Editor and the MCP product tools, and the migration is in progress.</p>
<h3>When will the migration be done? </h3>
<p>Before Apple's removal date. We're targeting December 2026</p>
<h3>Do I need to update the SDK or change any configuration? </h3>
<p>No. This change only affects how RevenueCat talks to App Store Connect, and there's no minimum SDK version tied to it.</p>
<h3>Are purchases, renewals, restores, entitlements, webhooks or product sync affected?</h3>
<p> No. None of these use the affected endpoints.</p>
<h3>Where can I find updates? </h3>
<p>We'll update this page when the migration is complete.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Apple’s Bundles and Suites: which is right for your app?]]></title>
      <link>https://www.revenuecat.com/blog/engineering/apple-subscription-bundles-and-suites</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/apple-subscription-bundles-and-suites</guid>
      <pubDate>Thu, 12 Nov 2026 14:52:00 GMT</pubDate>
      <dc:creator><![CDATA[Perttu Lähteenlahti]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[Get ready to offer combined subscriptions with your apps]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/10f1bce1f014ea53f4bf3a588a875d8f3d46e413-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p><a href="https://developer.apple.com/app-store/subscriptions/bundles-and-suites/">Apple’s Subscription Bundles and Suites</a> let you sell access to several auto-renewable subscriptions in one in-app purchase. Announced at <a href="https://developer.apple.com/videos/play/wwdc2026/210/">WWDC26</a>, these configurations can cover your own apps and, with bundles, apps from other developers as well. This opens new options for developers to cross-promote their subscriptions and offer better pricing for customers.</p>
<p>At first glance, Bundles and Suites sound very similar. A subscription bundle is one in-app purchase that combines up to five subscriptions customers can also buy separately, at a lower combined price. It can include subscriptions within one app, across your apps, or across apps from multiple developers.</p>
<p>A subscription suite is a single subscription that provides access across a set of up to 15 apps from one developer. Its included subscriptions exist only within the suite, and apps’ subscriptions aren’t sold separately. Customers subscribe to the collection, rather than choosing individual subscriptions.</p>
<p>In this guide, we’ll explain how bundles and suites differ, <a href="https://www.revenuecat.com/blog/engineering/wwdc26-whats-new-for-apps/">how to get your apps ready for them</a>, and how you should plan your new subscription offerings. Since Bundles and Suites are currently in early access, you’ll need to request access from Apple, something we cover at the end of this guide.</p>
<h2>Bundles and suites at a glance</h2>
<table>
<thead><tr>
<th><p><strong>Question</strong></p></th>
<th><p><strong>Bundle</strong></p></th>
<th><p><strong>Suite</strong></p></th>
</tr></thead>
<tbody>
<tr>
<td><p>What does the customer buy?</p></td>
<td><p>Several subscriptions in one combined purchase</p></td>
<td><p>A subscription covering a set of apps</p></td>
</tr>
<tr>
<td><p>Can they buy the included subscriptions separately?</p></td>
<td><p>Yes</p></td>
<td><p>No</p></td>
</tr>
<tr>
<td><p>Who can participate?</p></td>
<td><p>One developer, or up to five developers</p></td>
<td><p>One developer</p></td>
</tr>
<tr>
<td><p>What is the size limit?</p></td>
<td><p>Up to five subscriptions</p></td>
<td><p>Up to 15 apps</p></td>
</tr>
<tr>
<td><p>When does it fit?</p></td>
<td><p>Services sold individually with a combined offer</p></td>
<td><p>Related apps sold as a subscription collection</p></td>
</tr>
</tbody>
</table>
<h2>When should you use App Store bundles (+ types of Bundle)?</h2>
<p>Use a bundle when you want to keep selling subscriptions individually while <a href="https://www.revenuecat.com/blog/growth/subscription-app-expand-value/">offering customers a combined deal</a>. There are three configurations to consider:</p>
<ul>
<li><strong>Single-app bundle:</strong> <em>CatNavigator</em> sells subscriptions for Norway, Sweden, and Finland individually, and combines all three in its Nordic bundle</li>
<li><strong>Multi-app bundle from one developer:</strong> <em>TodoCat</em> and <em>CalendarCat</em> each have a subscription, and their developer offers both together in a bundle</li>
<li><strong>Multi-developer bundle:</strong> <em>TodoCat</em> and <em>CalendarDog</em> come from different developers, who partner to sell their subscriptions together</li>
</ul>
<p>A multi-developer bundle also creates a cross-promotion opportunity: customers who already value another developer’s app may discover yours through the combined offer. Bundling subscriptions from two different apps can also give customers a better deal than buying each app individually.</p>
<p>A bundle can contain up to five subscriptions, and a multi-developer bundle can involve up to five developers. Each included subscription must have the same duration, so you can’t combine monthly and annual products in one bundle. Customers must also be able to buy the included subscriptions individually.</p>
<h3>Single-app bundle</h3>
<p>The following configurations are illustrative. <em>CatNavigator</em> is a navigation app with separate subscriptions for Norway, Sweden, and Finland. A customer who only needs Norway can subscribe to that country. A customer traveling around the region can choose the Nordic bundle, which includes all three subscriptions at a lower combined price.</p>
<p>Everything is unlocked inside <em>CatNavigator</em>. You’re combining subscriptions within one app, so the bundle doesn’t require a second app or another developer. The individual country subscriptions remain available, and their durations must match within the bundle.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/8cb2ad32aef3e0bbef87214c441424cf75801f71-2400x1500.png" alt="CatNavigator’s Nordic bundle combines Norway, Sweden, and Finland subscriptions."/><figcaption>Figure 1. CatNavigator’s Nordic bundle combines Norway, Sweden, and Finland subscriptions.</figcaption></figure>
<h3>Multi-app bundle from one developer</h3>
<p>Take a developer with two apps: <em>TodoCat</em> for tasks and <em>CalendarCat</em> for scheduling. Each app has its own subscription, but the developer now has the option to offer both in a bundle at a lower combined price. The bundle gives customers access to both services through one purchase while keeping the standalone subscriptions available. When canceling, customers can also decide to keep parts of the bundle.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f80ef1cf88851617f52595ea7aca0fdf98d050e2-2400x1500.png" alt="One developer bundles the TodoCat and CalendarCat subscriptions."/><figcaption>Figure 2. One developer bundles the TodoCat and CalendarCat subscriptions.</figcaption></figure>
<h3>Multi-developer bundle</h3>
<p>Multi-app bundles can also combine apps from different developers, for example, a calendar app from one developer and a to-do app from another. The customer proposition stays similar: <em>TodoCat</em> and <em>CalendarDog</em> offer their monthly subscriptions together for less than the combined standalone price.</p>
<p>If the customer is already subscribed to <em>TodoCat</em>, joining the bundle upgrades their access and triggers a prorated refund for the unused standalone period. If they later decide they only want <em>TodoCat</em>, they can keep that subscription and <a href="https://www.revenuecat.com/feature/retention-messaging">leave the rest of the bundle</a> at renewal.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3318b7dadb274c644b5db4d0b668a15a891c8128-2400x1500.png" alt="Different developers bundle the TodoCat and CalendarDog subscriptions."/><figcaption>Figure 3. Different developers bundle the TodoCat and CalendarDog subscriptions.</figcaption></figure>
<h2>When should you use App Store Suites?</h2>
<p>Use a suite when you want to sell one subscription across <a href="https://www.revenuecat.com/blog/growth/app-portfolio-vs-single-app/">a related set of your apps</a>. <a href="https://www.apple.com/apple-creator-studio/">Apple Creator Studio</a> illustrates that use case by combining creative tools such as Final Cut Pro, Logic Pro, and Pixelmator Pro in one subscription. It’s a useful reference for the purchase experience:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/88dcafcd6ef867ccc4764342efb8bbbd27f63063-2048x1291.png" alt="Apple Creator Studio apps"/></figure>
<p>A suite can cover up to 15 apps from the same developer. Apple’s explanation says the subscriptions included in a suite can’t be purchased individually. This makes suites more limited when it comes to <a href="https://www.revenuecat.com/blog/growth/what-you-need-to-know-apples-advanced-commerce-api">offering both bundled and individual subscriptions</a>. For most developers, bundles might therefore be the better option due to their flexibility with existing business models.</p>
<h3>A suite example: PhotoCat, VideoCat, and SoundCat</h3>
<p>Imagine one developer offering PhotoCat for photo editing, VideoCat for video editing, and SoundCat for audio editing. Customers buy the suite subscription and receive access across all three apps. The subscriptions included in that suite aren’t sold individually.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a7d0664234fa11b7c3ee6f73d182dad7a802ee13-2400x1500.png" alt="One suite subscription covers PhotoCat, VideoCat, and SoundCat."/><figcaption>Figure 4. One suite subscription covers PhotoCat, VideoCat, and SoundCat.</figcaption></figure>
<h2>Get access to Bundles and Suites from Apple</h2>
<p>Start with <a href="https://appstoreconnect.apple.com/forms/755b71a0-aba1-4c81-bbd9-391369e485ea">Apple’s request form</a> in App Store Connect. Have the participating apps’ identifiers ready, along with the configuration you want to offer.</p>
<h3>Check the technical requirements</h3>
<p>Apple lists support for Bundle and Suite purchases on iOS 27, iPadOS 27, macOS 27, and tvOS 27 or later. Check that all participating apps meet the requirements before submitting the request.</p>
<h3>Choose your configuration</h3>
<p>After entering your contact details, confirm that you’ve implemented StoreKit 2 across the apps you plan to include. Select a single-developer bundle, a single-developer suite, or a multi-developer bundle.</p>
<p>For a single-developer bundle or suite, provide the App Apple IDs for the participating apps. These are the numeric app identifiers in App Store Connect you can find on each app’s submission page.</p>
<p>For a multi-developer bundle, the form also asks whether you’re the primary or secondary developer. The primary developer acts as the seller of record and Apple’s main contact. The secondary developers contribute subscriptions to the bundle. Include the App Apple IDs for your apps and your partners’ apps, so agree on the participating apps before anyone submits.</p>
<p>Apple’s <a href="https://developer.apple.com/app-store/subscriptions/bundles-and-suites/">public overview</a> and <a href="https://developer.apple.com/videos/play/wwdc2026/210/">WWDC26 session</a> don’t explain how revenue is split between developers or how secondary developers receive payment. Confirm the revenue split and payment arrangements with Apple during onboarding before agreeing to a partnership.</p>
<h3>Submit the request and complete onboarding</h3>
<p>Each developer in a multi-developer bundle must submit their own request and sign Apple’s program addendum. One partner’s application isn’t enough. After approval, Apple gathers subscription details and works with the participants on pricing and the go-live date.</p>
<h2>Get started with Bundles and Suites</h2>
<p>Decide which services belong together, whether customers should be able to buy them separately, and which apps and developers will participate. For bundles, choose matching subscription durations and a combined price that gives customers a reason to buy the package.</p>
<p><a href="https://developer.apple.com/app-store/subscriptions/bundles-and-suites/">Apple supports introductory and promotional offers</a> on Bundles and Suites, so you can offer a discounted starting period or a promotion alongside your standard price. Our guides to <a href="https://www.revenuecat.com/docs/subscription-guidance/subscription-offers">introductory offers</a> and <a href="https://www.revenuecat.com/docs/subscription-guidance/subscription-offers/ios-subscription-offers">promotional offers</a> explain how these offer types work.</p>
<p>Already have access from Apple? If you’re using RevenueCat on a self-serve plan, <a href="https://app.revenuecat.com/settings/support">contact RevenueCat support</a> from your dashboard to discuss your configuration and next steps. You can also <a href="https://www.revenuecat.com/talk-to-sales">contact the RevenueCat team</a> to discuss your requirements.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Flo gets 40% of its subscriptions more than six months after install, and wants to keep it that way]]></title>
      <link>https://www.revenuecat.com/blog/growth/mike-charniatsou-flo-sub-club-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/mike-charniatsou-flo-sub-club-podcast-2026</guid>
      <pubDate>Wed, 11 Nov 2026 14:04:51 GMT</pubDate>
      <dc:creator><![CDATA[David Barnard]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Mike Charniatsou says you run out of ways to squeeze the lemon, so Flo waits for needs to change.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/7c022fd10ff1abcf5ac5f971dd03085f7bfbe223-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>&quot;40% of all subscriptions take place after six months,&quot; says Mike Charniatsou, who works on growth and web onboarding at Flo, the women's health app. For most subscription businesses that number would be a problem to fix. At Flo it is the model. &quot;We aim to be a lifelong partner to the user,&quot; Mike says, and that means accepting that a user's needs, and her willingness to pay, move with the stages of her life. &quot;They may easily fall in and out of different needs and, you know, the level of willingness to pay, maybe different. And that's okay. So we accept those, you know, rules of the game.&quot;</p>
<p><a href="https://www.youtube.com/watch?v=fpo8OfzjTKU">Watch on YouTube</a></p>
<p>Cycle tracking, the core use case, stays free. What Flo does instead is watch how each user's needs evolve and step in when it has something new to offer, whether that is trying to get pregnant, following a pregnancy, or understanding perimenopause. &quot;When we see that there is something of value that we can bring to the user besides our core product offering, we do. And this is, you know, what lets us convert users from freemium&quot; into paying, three, six, and 12 months later.</p>
<p>Mike is clear that this is a luxury, not universal advice. A younger app under pressure to pay back acquisition from day zero can't wait six months for its subscribers. But he also thinks the alternative has a ceiling. There are only so many ways to squeeze the lemon, he says, &quot;and suddenly, you know, like there are not that many ways to keep squeezing that lemon.&quot; Flo would rather protect the relationship: &quot;We don't feel the need to try and monetize the user like on day one and day seven, you know, because we know that, you know, one day something may change.&quot;</p>
<h2><strong>&quot;Your paywall conversion is not your monetization strategy&quot;</strong></h2>
<p>That line is Mike's, and David Barnard read it back to him on the show. The argument behind it: growth teams overestimate two things. &quot;One of them is that short term conversion wins smoothly translates to business wins, which is not true.&quot; The other is that subscription conversion is a single event. &quot;There are lots of things like starting a trial, activating a trial, you know, retaining the user and so forth. So it's a life cycle.&quot; An onboarding paywall is the beginning of that journey, and a test that lifts it can still lose money further down.</p>
<p>David's counterexample came from an earlier Sub Club guest, Anada Lakra of BoldVoice, whose team removed the X from its paywall, watched conversion jump, and then watched refunds spike until the win became a drain. Mike's answer is to keep analyzing experiments long after rollout, and to run one exercise at the end of every year. &quot;Over the course of a year, you kind of ship tons of successful experiments, you know, with five, seven, 10% uplift, you know, and at the end of the year, you just, you know, do this exercise and check. Did your business grow by the same percentage that you've reported over the course of the year?&quot; If it didn't, the difference is the share of wins that were never business wins.</p>
<h2><strong>The onboarding step that lost 10% twice and then won 23%</strong></h2>
<p>Mike's biggest win of the year started as a failure. Flo's web onboarding team added a step to work out which perimenopause phase a user was in. &quot;It made so much sense to us at the very beginning. The hypothesis was strong,&quot; Mike says. &quot;And the first test came back with almost 10% drop in conversion rate.&quot;</p>
<p>What happened next is the part he wants other teams to hear. Instead of looking for evidence, the team reached for an explanation. &quot;Maybe something was, you know, something was wrong with the design. Let's just do it. One more run and nothing happened. You know, we got the same job and conversion rate.&quot; Only after the second loss did they dissect the audience, and the finding was blunt: the question was written in a way users couldn't answer, so &quot;almost 75% of the users were not assigned any perimenopause phase.&quot; Those users, who had just clicked an ad promising to tell them their phase, got a screen that said sorry, we couldn't pinpoint it, carry on.</p>
<p>Flo rewrote the question with its medical team and changed the logic so that a user who still couldn't be placed moved quietly to the next section instead of hitting a dead end. &quot;It was the third iteration based on the evidence, and it came back with 23% win.&quot; The team didn't trust it. A fourth, blackout test removed the new section entirely, and lost, which confirmed the uplift was real. Mike's summary: &quot;All the iterations that were based on explanation loss, the one that was built on evidence One.&quot;</p>
<p>His rule for any failed test follows from that. &quot;Explanation usually tells you what you want to be true,&quot; he says, whereas &quot;the evidence tells you where to look.&quot; You iterate when you have new evidence, not a new explanation, because explanations are unlimited and chasing them is &quot;this vicious cycle of, you know, experimenting.&quot;</p>
<h2><strong>Flo's marketing team found the need before product did</strong></h2>
<p>One of Flo's best onboarding branches began as an ad the product team hadn't planned for. The user acquisition team noticed that &quot;a specific kind of narratives around enhancing your sex life, kind of started to gain traction at meta. And the numbers were pretty good, even with the existing funnel.&quot; Product took the signal apart and concluded there was a user need the onboarding had never explored. So they built for it: a dedicated branch that acknowledges which ad the user arrived from and asks a tailored set of questions so the narrative carries straight through. &quot;It turned out to be a great revenue driver for us.&quot;</p>
<p>The mechanism matters more than the example. &quot;Marketing team has a bad insight into things that resonate with your audience, and they can validate those insights much faster than you ever can do with your product funnel,&quot; Mike says (the transcript's &quot;bad&quot; is almost certainly &quot;better&quot;; check the audio). At Flo's scale, the marketing team can scan the market and test hypotheses in volumes a product funnel never could, so the practice is to share every change, weekly, whether or not it seems to matter: &quot;you never know what insights will be valued by different teams.&quot; He doesn't claim the relationship is comfortable. &quot;It's not like a happy marriage.&quot; It is, in his view, the only way to make the growth engine stronger than either team alone.</p>
<h2><strong>The Blinkist paywall didn't work at Flo, or anywhere Mike has tried it</strong></h2>
<p>Since joining Flo, Mike has been pitched silver bullets by product leaders at other companies. &quot;We did this. We tracked 10% uplift in conversion rate. This is like your silver bullet. You got to do this. And we did exactly the same thing. Pixel to pixel, you know. And it just did not work.&quot; He has no tidy explanation. He does have a rule, which David restated as &quot;borrow the idea, not the solution,&quot; and he thinks most product teams get it backwards: they &quot;borrow the solution before understanding the problem.&quot;</p>
<p>The clearest case is the Blinkist-style paywall, the one that promises to remind you before your trial ends. It has worked for plenty of apps. &quot;We did tested it at Luna, we did test it at the flower, and it did not work for both of the teams,&quot; Mike says, referring to Loóna, the sleep app where he was CPO, and to Flo. The trial toggle went the same way. His conclusion is not that the pattern is bad, but that a result from another product is not evidence it will work in yours: &quot;You have to understand the underlying problem first.&quot;</p>
<p><a href="https://www.youtube.com/watch?v=fpo8OfzjTKU" target="_blank" rel="noopener noreferrer">In the full episode</a>, Mike also explains why he thinks streaks have become a cargo cult, how Flo uses proxy events to make early calls on experiments when the real conversion is a delayed trial activation, and why putting partner mode, a feature with more than 2 million pairs using it, into the onboarding funnel was his biggest fail of the year.</p>
<h2>Guest links</h2>
<ul>
<li><a href="https://www.linkedin.com/in/mikechernetsov/" target="_blank" rel="noopener noreferrer">Mike Charniatsou on LinkedIn</a></li>
<li><a href="https://flo.health" target="_blank" rel="noopener noreferrer">Flo Health website</a></li>
<li><a href="https://flo.health/careers" target="_blank" rel="noopener noreferrer">Flo Health Careers</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Sometimes the numbers won't match, and that's okay: fixing measurement paralysis in app growth]]></title>
      <link>https://www.revenuecat.com/blog/growth/measurement-paralysis</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/measurement-paralysis</guid>
      <pubDate>Wed, 11 Nov 2026 14:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Hannah Parvaz]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Why more data isn’t always the answer]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/786add80f24f40dd7b4f3ebafec3cdf60e884461-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>There’s a meeting I’ve sat in (some version of) too many times.</p>
<p>Paid acquisition says customer acquisition cost (CAC) is $42 when finance says it’s $67. Meta says one campaign is outperforming everything else, when the mobile measurement partner (MMP) is much less enthusiastic. Product is celebrating an 18% improvement in trial starts, but the revenue data suggests trial-to-paid is falling…</p>
<p>The meeting inevitably ends with someone opening a dashboard to clarify the numbers, only to find it sparks three new debates.</p>
<p>Everyone has data, and everyone can defend their number. Yet, <strong>no one can figure out what to do next. </strong>And this isn’t usually happening because a company has no data. It frequently happens when a company has <em>lots of data</em>, but no shared understanding of it.</p>
<p>This measurement dysfunction is one of the most underestimated barriers to app growth.</p>
<h2><strong>ATT broke the illusion of perfect attribution</strong></h2>
<p>If you were running mobile growth in 2021 and you’re still here, you’re already battle-tested. Apple’s App Tracking Transparency (ATT) framework became a reality with iOS 14.5 in April 2021, pushing the industry into a much more aggregated and privacy-preserving measurement world.</p>
<p>Lucas Moscon’s <a href="https://www.revenuecat.com/blog/growth/skadnetwork-guide-subscription-apps">guide to SKAdNetwork (SKAN) </a>describes the pre-ATT period as a time when user-level <a href="https://www.revenuecat.com/blog/growth/activation-metrics">metrics</a> made deterministic attribution much more accessible. Then came ATT, SKAN postbacks, conversion values, anonymity thresholds, shorter windows, and far less visibility at a user and creative level.</p>
<p>There was a fog around data: everyone was trying to work out what numbers they could still see (not many), what they could send back to the platforms (not much), and which parts of the old playbook still worked (again, not many).</p>
<p>But the industry adapted. We learned SKAN. We added more tracking. We got better at first-party data. Platforms built modeled attribution (hello, Meta’s Aggregated Event Measurement, or AEM). Teams got big into <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels/">web funnels</a>, incrementality tests, cohort analysis, marketing mix modeling (MMM) and more sophisticated internal business intelligence.</p>
<p>Five years later, the problem is almost the inverse of where we started: most subscription <strong>businesses now have</strong> <strong>more measurement</strong> than they did before ATT, but <strong>less alignment on what the numbers actually mean</strong>.</p>
<h2><strong>The numbers are going to disagree</strong></h2>
<p>Today, each team working on a subscription app has gotten better at measuring their own part of the journey, but the connections between those views have definitely not improved at the same speed.</p>
<p>UA teams look at platform models and MMPs; product has richer behavioral analytics; finance keep their own revenue view; and subscription teams get better cohort and renewal data. But this segregated measurement<strong> </strong>is how you end up with five sophisticated systems and a basic argument about whether CAC is $42 or $67.</p>
<p>That does not mean the post-ATT measurement stack failed. It means we have to <strong>stop expecting each layer to produce the same answer.</strong></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9ce8c49c657e173de7996807c5ccf157aad43835-1280x720.png" alt=""/><figcaption>One customer journey can create several valid views because each system is answering a different question.</figcaption></figure>
<p>One example is Meta: for one of our recent clients, Meta is reporting less than a third of the conversions we see in App Store Connect across the same period. That’s a huge gap if you look at the two numbers and <strong>assume they are supposed to match</strong>.</p>
<p>We don’t panic about it. We don’t believe the campaigns suddenly became 3.5x worse. Most importantly, we don’t spend the week trying to make Meta and App Store Connect display the same conversion total. What we <em>do </em>do is understand the gap:</p>
<ul>
<li>Is it broadly stable?</li>
<li>Does it widen or narrow when we change how spend is deployed?</li>
<li>Does Meta still correctly identify the campaigns and creatives that are relatively stronger or weaker?</li>
<li>What happens to the business-level numbers when we scale?</li>
</ul>
<p>Once you know those answers, an under-reporting platform can still be very useful. It just <strong>cannot be treated as a complete representation of reality.</strong></p>
<p>This is something <a href="https://www.linkedin.com/in/davidvargasmontiel/">David Vargas</a> has talked about - his LinkedIn is basically a running catalog of what iOS measurement looks like in the real world. <a href="https://www.linkedin.com/posts/davidvargasmontiel_last-week-we-had-a-good-case-with-aem-in-share-7254400761793576960-O66u/">In one example</a>, RevenueCat showed 90 free trials while Meta AEM reported 33. In <a href="https://www.linkedin.com/posts/davidvargasmontiel_skan-can-work-better-than-aem-for-some-activity-7313468014610280448-Kb2N">another</a>, the App Store produced a materially lower cost per trial than AEM.</p>
<p>The point is not that <em>Meta</em> is uniquely bad. It’s that <strong>discrepancy in your data is normal.</strong></p>
<h3><strong>Identify your normal disagreement</strong></h3>
<p>The way we solve this at Aperture is by defining what ‘normal disagreement’ looks like. E.g. if Meta usually reports 35% fewer trials than your baseline, that’s your normal disagreement. Seeing 100 trials on your baseline tool and 65 on Meta isn’t concerning. But if that gap suddenly becomes 70% disagreement, then that <em>is </em>worth investigating. <strong>A stable mismatch is very different from a new mismatch.</strong></p>
<p>This is the part a lot of measurement frameworks miss. Reliable data doesn’t mean identical numbers. It requires the team to understand why the numbers differ, roughly by how much, and what would count as an anomaly.</p>
<p>In short, the useful question is not “which dashboard is right?” It is: <strong>what decision am I trying to make with this number?</strong></p>
<h2><strong>Build the measurement system around decisions</strong></h2>
<p>A lot of companies experiencing this kind of measurement pain respond by building another dashboard to bring all the numbers into one place. And sometimes — <em>on rare occasions </em>— that is useful.</p>
<p>But if the numbers are already inconsistent, then a new dashboard is only a means to display those inconsistent numbers together.</p>
<p>I would start somewhere less exciting: <strong>write down the decisions your team repeatedly needs to make, like:</strong></p>
<ul>
<li>Should we increase channel spend next week?</li>
<li>Which ad set gets the next $1,000?</li>
<li>Did the paywall test improve the business?</li>
<li>Is the US cohort genuinely better than the UK cohort?</li>
<li>Can we afford to acquire a customer at $60?</li>
<li>Is this channel adding customers or mostly taking credit for demand we already had?</li>
</ul>
<p>Then, for each decision, decide four things:</p>
<ol>
<li><strong>The fastest useful signal:</strong> what can move quickly enough to help you act?</li>
<li><strong>The downstream guardrail:</strong> what stops you from declaring a false win?</li>
<li><strong>The business anchor:</strong> what blended outcome should ultimately move if the strategy is working?</li>
<li><strong>The evidence threshold:</strong> how certain do you need to be given the size and reversibility of the decision?</li>
</ol>
<p>Here’s what that looks like for four common decisions:</p>
<table>
<thead><tr>
<th><p>Decision</p></th>
<th><p>Fast signal</p></th>
<th><p>Downstream guardrail</p></th>
<th><p>Business anchor</p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Allocate creative spend</p></td>
<td><p>Platform CPT / CPA</p></td>
<td><p>Trial-to-paid, early value</p></td>
<td><p>Blended CAC / revenue</p></td>
</tr>
<tr>
<td><p>Roll out onboarding change</p></td>
<td><p>Activation / trial start</p></td>
<td><p>Paid conversion, refunds, retention</p></td>
<td><p>Cohort revenue</p></td>
</tr>
<tr>
<td><p>Change price or paywall</p></td>
<td><p>Purchase conversion</p></td>
<td><p>Plan mix, renewal, refund rate</p></td>
<td><p>Realized revenue / contribution</p></td>
</tr>
<tr>
<td><p>Reallocate major channel budget</p></td>
<td><p>Attributed CPA + cohort quality</p></td>
<td><p>Incrementality / longer-term value</p></td>
<td><p>Blended CAC, ROAS, total revenue</p></td>
</tr>
</tbody>
</table>
<p>The same measurement stack should use different signals for different kinds of decisions. Only after sorting that would I worry about whether the company needs another reporting tool.</p>
<h3><strong>Match the evidence to the decision</strong></h3>
<p>Once you accept that no single metric can do every job, the next question becomes much easier: <strong>how much evidence does this decision actually require?</strong></p>
<p>I’ve had versions of this debate for years. In a <a href="https://www.singular.net/blog/first-million-users/">Singular Growth Masterminds conversation</a>, John Koetsier and I talked about teams wanting to judge every product experiment on lifetime value. The intention is good, and LTV is important, but you cannot necessarily use lifetime value to judge a week-long experiment. You would never learn anything quickly enough.</p>
<p>We must also consider the stakes of each decision. Looking at our four common decisions above — each differs drastically. A $500 <strong>creative-allocation</strong> decision tomorrow does not require the same evidence as moving $3 million of annual spend between channels next week. For the first decision, platform data can be perfectly adequate, if you understand the usual reporting gap and you are watching the blended business outcome.</p>
<p>For a <strong>paywall or pricing change</strong>, I want a <a href="https://www.revenuecat.com/feature/experiments">controlled experiment</a> and downstream subscription guardrails. For a <strong>large channel reallocation</strong>, I want cohort quality, blended performance and, where possible, incrementality evidence. For <strong>financial planning</strong>, I care far more about realized financial data and explicit assumptions.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9ecac87133c5c1823126d22a2dd155086a0148e2-1280x920.png" alt=""/><figcaption>The bigger and less reversible the decision, the stronger the evidence should be.</figcaption></figure>
<p>This is also why directional data is not dirty data. The obsession with perfect measurement can become another form of paralysis.</p>
<p>If Meta consistently finds one creative at half the reported cost per trial of everything else, I do not need to wait a year to put more budget behind it. But I <em>do</em> want to know whether the creative is bringing in a cohort that converts and retains sensibly, and whether blended economics remain healthy as spend increases.</p>
<p>The standard of evidence rises with the cost of being wrong.</p>
<h3><strong>How Opal kept it simple</strong></h3>
<p>A good example is Opal. On <a href="https://www.revenuecat.com/blog/growth/kenneth-schlenker-opal-sub-club-podcast">Sub Club, Opal’s CEO, Kenneth Schlenker</a>, shared that the seven-person team had reached $5 million annual recurring revenue (ARR) (<a href="https://www.speedinvest.com/knowledge/scaling-smart-how-opal-built-a-10m-arr-business-in-just-2-years">and they’ve since doubled that</a>).</p>
<p>Opal invested in paid acquisition early, but rather than trying to build an enormous measurement framework, they focused on Day 8 return on ad spend (ROAS) as a practical growth signal. Kenneth described the approach as <strong>“just focusing on this one metric: Day 8 return on ad spend.”</strong></p>
<p>That number wasn’t the final answer to every question about the business. But it was useful because it was available quickly enough to make decisions, and close enough to the economics they cared about.</p>
<aside class="tip"><strong>The lesson</strong><p>There’s an important <strong>difference between an imperfect metric and a useless metric</strong>. Growth teams get into trouble when they forget that.</p></aside>
<h2><strong>Use blended metrics as your business reality</strong></h2>
<p>When the attribution gets noisy, I like to retrace my steps back to the boring numbers.</p>
<p>How much did you spend? How many customers did you acquire? How much revenue did those customers generate?</p>
<p>That blended view is your anchor.</p>
<p>If the business spent $200,000 during a period, and acquired 4,000 new paying customers, the <a href="https://www.revenuecat.com/blog/growth/measure-hybrid-monetization">blended acquisition</a> cost is $50. There will still undoubtedly be debate about which channels caused those customers, which captured demand, or how organic growth contributed. But before you have that debate, you at least know the economic boundary the models have to fit inside.</p>
<ul>
<li>Platform data helps us make decisions inside the platform</li>
<li>Attribution helps us understand where growth appears to be coming from</li>
<li>Blended performance tells us <strong>whether the business as a whole is actually becoming more efficient</strong></li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/765b8e91bb9e3f44927e209834db241d03aa7a97-1200x664.png" alt=""/><figcaption>Attribution models can disagree with one another. The blended business totals give those models an economic boundary.</figcaption></figure>
<p>Blended reporting does not solve attribution. It doesn’t tell you whether Meta caused customer #4,382 to subscribe, but it does tell you what you put into the machine and what came out.</p>
<p>Perhaps even more importantly, it also gives you somewhere to sanity-check the stories your channels are telling you. E.g. if every ad platform claims more conversions than the business acquired in total, you know the channel reports cannot all be interpreted literally.</p>
<p>I often think of this as working from the outside in. Start with the size and economics of the whole pie, then use attribution and segmentation to work out how it’s being divided. If you start with the individual slices, you risk scaling a channel that looks brilliant in isolation, while the overall business barely moves.</p>
<h3><strong>Beware: blended truth still hides averages</strong></h3>
<p>There is a catch to everything I have just said about blended metrics: <strong>averages can hide important differences too.</strong></p>
<p>Blended CAC is a fantastic anchor. But it is not a segmentation strategy.</p>
<p><a href="https://www.revenuecat.com/blog/growth/best-metrics-for-understanding-subscription-app-growth">Eric Crowley made this point years ago</a> when discussing LTV: “The problem with this metric, and lots of other metrics, is that they are derived from an average.”</p>
<p>You’ve probably heard about ‘tourists’ and ‘locals’ — tourists may subscribe quickly, but also churn quickly. On the other hand, locals can become deeply engaged, long-lived customers. Put both groups into one average and you can end up with a number that represents neither.</p>
<p>The same problem appears everywhere in subscription growth. A blended trial-to-paid rate can hide one country converting brilliantly and another badly; overall LTV can hide radically different monthly and annual subscribers; and average CAC can hide an audience that is cheap to trial but expensive to pay.</p>
<p>Time creates another complication. RevenueCat Charts distinguishes <a href="https://www.revenuecat.com/blog/growth/what-is-lifetime-value-ltv-apps">realized LTV</a> from predicted LTV: one reflects value already generated; the other estimates future value. A Day 30 realized cohort is observed but immature. A 12-month predicted LTV is more complete, but it’s an estimate rather than observed data. Both are useful: finance may care about contribution after fees and refunds, while growth may need an earlier gross-revenue signal to decide whether to scale tomorrow.</p>
<p>Across all metrics, it’s never as simple as “this is always the best one to use”. The hierarchy is not ‘blended good, segmented bad’. It’s more:</p>
<p>Use blended metrics to understand <strong>whether the business economics work</strong>. Then <strong>segment to understand </strong><em><strong>why</strong></em><strong> they work</strong>, where they are breaking, and which levers you should pull next.</p>
<h2><strong>What good measurement actually looks like</strong></h2>
<p>After all of this, you can see that good measurement is actually pretty unglamorous. And it’s never as clear as it seems.</p>
<p>A trial-conversion lift can look great until those users cancel more often. A channel can look efficient until you zoom out and realize the blended economics are moving in the wrong direction. None of that makes the original number useless. It just means no single metric gets to answer every question.</p>
<p>The goal is not to make every system agree. The goal is to know:</p>
<ul>
<li>What each number is useful for</li>
<li>When to sense-check the number against the blended picture</li>
<li>Which numbers help you operate day to day</li>
<li>Which ones tell you whether the business is healthy</li>
<li>Which decisions are important enough to require extra evidence</li>
</ul>
<p>The measurement stack will keep changing. We made it through ATT, SKAN, disappearing user-level signals, and several years of being told attribution was dead. There will be another version of this problem.</p>
<p>But if everyone can look at the data and know what to do next, the measurement is doing its job.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Recalculated, not recomposed: what does one tap cost in a Compose paywall?]]></title>
      <link>https://www.revenuecat.com/blog/engineering/android-paywall-improve</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/android-paywall-improve</guid>
      <pubDate>Wed, 11 Nov 2026 02:50:36 GMT</pubDate>
      <dc:creator><![CDATA[Jaewoong Eum]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[A real case from RevenueCat's Android SDK: how we traced a short pause after a package tap in our Compose paywalls, and what each fix actually bought, measured one at a time.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/5823fe635a2cbccdfe3d38050860ec35f81d998b-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>We received an issue report about an Android paywall for <a href="https://github.com/RevenueCat/purchases-android">RevenueCat Android SDK</a>, with a screen recording attached: a bottom sheet with two subscription plans over a blurred background. The user taps the second plan, the ripple starts, and the screen pauses for about 0.4 seconds before the new plan is selected. The paywall was built with RevenueCat Paywalls, a server driven UI that the Android SDK renders with Jetpack Compose. So what does one tap ask a Compose tree to do, and how can that take longer than twenty frames?</p>
<p>In this article, you'll start from that recording and trace the cost of one package tap inward: the override model behind a server driven paywall, an instrumented test and a set of kill switches for measuring a tap, what <code>derivedStateOf</code> skips and what it doesn't, lazy condition inputs, a regex cache, fonts without <code>equals</code>, work moved into the draw phase, an invisible spinner, and the limits of reordering code inside one main thread task.</p>
<h2><strong>The fundamental problem: One selection, read by every component</strong></h2>
<p>A RevenueCat paywall is designed in the dashboard and arrives in the app as a tree of components: stacks, texts, images, packages, buttons. Any component can carry overrides, which are partial property sets applied when a condition holds. If you've used a <code>&lt;selector&gt;</code> drawable with <code>state_selected</code>, it's the same idea, applied to any property of any component. The condition can be &quot;the package this component belongs to is selected&quot;, &quot;the user is eligible for an intro offer&quot;, &quot;the selected package, wherever it is, is the annual one&quot;, or a window size. A package card that gets a thicker, colored border when it's selected looks like this in the SDK's model:</p>
<pre><code>PackageComponent(
    packageId = &quot;\$rc_annual&quot;,
    isSelectedByDefault = true,
    stack = StackComponent(
        components = listOf(planName, planPrice),
        border = Border(color = gray, width = 1.0),
        overrides = listOf(
            ComponentOverride(
                conditions = listOf(ComponentOverride.Condition.Selected),
                properties = PartialStackComponent(border = Border(color = accent, width = 2.5)),
            ),
        ),
    ),
)</code></pre>
<p>Resolving overrides means checking each override's conditions against the current state and merging the matching ones over the base style. The result is the set of properties the component draws with. Every component has a state holder that does this inside a derivedStateOf, so the resolution reruns only when something it read changes. The inputs it reads, such as whether this component's package is selected or which package is selected overall, are backed by Compose state, and a derived state records every state object it reads as a dependency. Before October 2026, StackComponentState resolved its overrides like this, and every other component state did the same:</p>
<pre><code>private val presentedPartial by derivedStateOf {
    val windowCondition = ScreenCondition.from(windowSize)
    val componentState =
        if (packageAwareDelegate.isSelected) ComponentViewState.SELECTED else ComponentViewState.DEFAULT

    style.overrides.buildPresentedPartial(
        windowCondition,
        packageAwareDelegate.offerEligibility,
        componentState,
        conditionContext = ConditionContext(
            selectedPackageId = selectedPackageInfoProvider()?.rcPackage?.identifier,
            customVariables = customVariablesProvider(),
            stateReader = stateStoreProvider()::currentValueOrDefault,
            windowDpSize = windowDpSize,
        ),
    )
}</code></pre>
<p>Notice the order: <code>isSelected</code>, <code>offerEligibility</code>, and the selected package's identifier are all read before <code>buildPresentedPartial</code> looks at a single condition. A component with no overrides at all still read the selection. There's a difference between &quot;the selection changed&quot; and &quot;this component's inputs changed&quot;. Every tap is the first, and it should be the second only for components whose overrides mention the selection. The old code made it the second for every component.</p>
<p>If you've written a selectable list in Compose, you've probably written the same shape yourself:</p>
<pre><code>val badgeStyle by remember {
    derivedStateOf {
        val isSelected = selection.selectedId == plan.id
        resolveBadgeStyle(plan.badgeRules, isSelected)
    }
}</code></pre>
<p>For a plan whose badge rules never look at the selection, the read is wasted, and the derived state now depends on every tap anyway. In a list of five items that's harmless. The rest of this article is about what it costs when the list is a whole screen.</p>
<h2><strong>Measuring before guessing: The recording and an instrumented test</strong></h2>
<p>The first theory was the background: maybe every tap re-rendered and re-blurred the paywall behind the sheet. The recording made that unlikely. Sampled at 30 frames per second, each tap showed the ripple starting, then about a dozen identical frames, then the new selection in one frame. Across this and a second recording, the pauses lasted 0.33 to 0.50 seconds. The background didn't change on the frame where the selection flipped, and in the second recording, where it animated, the animation paused during every delay. When unrelated animations pause at the same moment, the main thread is the usual suspect.</p>
<p>A blocked main thread right after a state change points at composition, layout, or something they call. To find out which, we wrote an instrumented test that renders a paywall with the real SDK on an Android runtime, changes the selected package on the UI thread, and waits for Compose to go idle:</p>
<pre><code>repeat(ITERATIONS) { i -&gt;
    val target = if (i % 2 == 0) idA else idB
    val started = System.nanoTime()
    composeTestRule.runOnUiThread { state.update(target) }
    composeTestRule.waitForIdle()
    samples[i] = (System.nanoTime() - started) / 1_000_000.0
}</code></pre>
<p>Each run does 6 warmup selections and 20 measured ones and reports the median, and every timing from this test below is the median of at least three such runs. The paywall is synthetic: N plain texts with no overrides, plus two packages, and no blur at all. The cost reproduced anyway, so it doesn't need the blur. Changing N answers the first question to ask about any slowdown: does the cost grow with the number of components, or is it a constant paid once per tap?</p>
<p>Two caveats apply to every timing in this article. The runs use an Android 15 emulator and a debug build, so the values are only meaningful as before and after comparisons on the same bench, and phones aren't necessarily faster: during the investigation, an earlier build on a different paywall took 190 ms per tap on a Galaxy S23 against 112 ms on an Android 14 emulator. And <code>waitForIdle</code> advances in whole frames, so the resolution is one frame (16.7 ms) and the floor is two frames, about 33 ms.</p>
<p>To see what each fix bought on its own, the fixes that touch the tap got kill switches: flags in a measurement build that restore the old behavior at one choke point. With every flag off, the build is the current SDK, so every &quot;before&quot; number in this article is the current SDK with a fix switched off, not an old release. Next to the switches sit counters for the work a tap causes: override resolutions, text recompositions, font file loads, and variable passes, which count how many times a text template was processed. Here's one tap on 300 texts, with <a href="https://github.com/RevenueCat/purchases-android/pull/4383">#4383</a>, the main fix this article explains, switched off and on:</p>
<ul>
<li><strong>#4383 switched off</strong>: 700 ms per tap, with 308 override resolutions, 0 text recompositions, and 600 variable passes.</li>
<li><strong>Current SDK</strong>: 184 ms per tap, with 0 override resolutions, 0 text recompositions, and 600 variable passes.</li>
</ul>
<p>The chart below repeats the measurement at 20, 100, and 300 texts:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a369d883df9b62f5148d459b80a3c83ddb8eb659-1600x1089.png" alt=""/></figure>
<p>The orange and blue lines climb with the number of texts, so this is work every component does on every tap, not a one off like a file read. And the counters show where it went: 308 override resolutions and 600 variable passes per tap, against zero text recompositions. Compose correctly skipped every text on the screen, because none of them looked any different. Almost all of the 700 ms went into recalculating values only to find out they hadn't changed.</p>
<h2><strong>Skipping in </strong><code><strong>derivedStateOf</strong></code><strong>: Recomposition, not recalculation</strong></h2>
<p>To see why skipping can be expensive, look at what the Compose runtime does when a state object read by a derived state changes. Composition puts every scope that read the derived state on a &quot;maybe changed&quot; list, and right before it would recompose one of them, it checks whether the derived value really changed. In the runtime that the SDK ships with (1.11.4) and the latest stable release (1.12.1), that check is <code>checkDerivedStateChanged</code> in <code>RecomposeScopeImpl</code>:</p>
<pre><code>private fun DerivedState&lt;*&gt;.checkDerivedStateChanged(
    dependencies: MutableScatterMap&lt;DerivedState&lt;*&gt;, Any?&gt;
): Boolean {
    @Suppress(&quot;UNCHECKED_CAST&quot;)
    this as DerivedState&lt;Any?&gt;
    val policy = policy ?: structuralEqualityPolicy()
    return !policy.equivalent(currentRecord.currentValue, dependencies[this])
}</code></pre>
<p><code>currentRecord</code> is where the work happens: if any dependency changed since the last calculation, reading it runs the calculation again. So a single tap goes through these steps:</p>
<ol>
<li>The selected package changes.</li>
<li>Every derived state that read it is now out of date.</li>
<li>Before recomposing each reader, Compose recalculates the derived state and compares the result with the value the reader last saw, using <code>==</code>.</li>
<li>If the two are equal, the reader is skipped.</li>
</ol>
<p>Step 4 is what <code>derivedStateOf</code> is known for, and it worked: nothing recomposed. Step 3 is the part that's easy to forget, and there's no shortcut around it, because the only way to learn that the new value equals the old one is to compute the new value.</p>
<p>One more detail multiplies the cost. A <code>derivedStateOf</code> without an explicit policy stores a new record every time it recalculates, even when the result is equal, so any derived state built on top of it sees a changed dependency and recalculates too. In <code>TextComponentState</code>, <code>presentedPartial</code> is private, and composition never reads it directly. It reads about a dozen properties derived from it, such as <code>visible</code>, <code>text</code>, <code>color</code>, <code>fontSize</code>, <code>padding</code>, and <code>fontFamily</code>, and each of those recalculated and was compared on every tap, for every text.</p>
<h2><strong>Reading less: Condition inputs as lambdas</strong></h2>
<p>The first half of <a href="https://github.com/RevenueCat/purchases-android/pull/4383">#4383</a> doesn't make that chain cheaper. It removes the dependency for every component that can't be affected. The condition inputs became lambdas, and each condition calls only the one it needs. Looking at the evaluation after the change:</p>
<pre><code>private fun ComponentOverride.Condition.evaluate(
    windowSize: ScreenCondition,
    offerEligibility: () -&gt; OfferEligibility,
    state: () -&gt; ComponentViewState,
    conditionContext: ConditionContext,
): Boolean = when (this) {
    ComponentOverride.Condition.Compact,
    ComponentOverride.Condition.Medium,
    ComponentOverride.Condition.Expanded,
    -&gt; windowSize.applicableConditions.contains(this)
    ComponentOverride.Condition.IntroOffer -&gt; offerEligibility().isIntroOffer
    ComponentOverride.Condition.Selected -&gt; state() == ComponentViewState.SELECTED
    is ComponentOverride.Condition.SelectedPackage -&gt; evaluate(conditionContext.selectedPackageId())
    // ...the other conditions follow the same pattern
}</code></pre>
<p>The real <code>when</code> covers more condition types, and each branch follows the same rule: a lambda runs only inside the branch that needs its value. The call sites now pass <code>state = { if (packageAwareDelegate.isSelected) SELECTED else DEFAULT }</code> instead of the computed value.</p>
<p>Why does a lambda change the dependencies? Because a derived state learns its dependencies by watching what its calculation reads, not by looking at what it could read. Inside <code>DerivedSnapshotState</code>, the calculation runs under a read observer, a callback Compose invokes every time a state object is read inside the block. The real code also tracks how deeply derived states are nested, but the core of it is this:</p>
<pre><code>Snapshot.observe(
    {
        if (it === this) error(&quot;A derived state calculation cannot read itself&quot;)
        if (it is StateObject) {
            newDependencies[it] = /* nesting level of this read */
        }
    },
    null,
    calculation,
)</code></pre>
<p>Every state object read during <code>calculation</code> lands in <code>newDependencies</code>, and nothing else does. A lambda that is never invoked never reads the selection, so the selection never becomes a dependency. A component whose overrides don't mention the selection no longer resolves them on a tap, because nothing it read has changed.</p>
<p>This is the habit to take into your own code. Inside <code>derivedStateOf</code>, or any scope that tracks reads, such as a composable body, <code>snapshotFlow</code>, or a layout or draw block, read a state only on the path that uses it. Here <code>resolveBadgeStyle</code> takes a <code>() -&gt; Boolean</code> and calls it only when a rule checks the selection:</p>
<pre><code>val badgeStyle by remember {
    derivedStateOf {
        resolveBadgeStyle(plan.badgeRules, isSelected = { selection.selectedId == plan.id })
    }
}</code></pre>
<p>The kill switch for this half sits at the top of buildPresentedPartial. Calling every lambda up front recreates the dependencies the old code had:</p>
<pre><code>if (PerfKnobs.eagerConditionReads) {
    offerEligibility()
    state()
    conditionContext.selectedPackageId()
}</code></pre>
<p>With only this half switched off, the 300 text paywall took 450 ms per tap against 184 ms. On one of RevenueCat's own paywall templates, an 89 component design with selected state overrides and price variables, override resolutions per tap dropped from 99 to 22. The ones that remain belong to components whose overrides really do depend on the selection, such as the plan cards.</p>
<h2><strong>The other half of #4383: 3,000 regex compilations per tap</strong></h2>
<p>The counters exposed a second cost. Text in a paywall is a template, where <code>{{ product.price_per_period }}</code> is replaced with the real price, and a variable outside a package refers to the selected package. So every text outside a package reads the selection and reprocesses its template on every tap, twice: once for the screen, and once for a spoken version that screen readers read, so that &quot;$9.99/mo&quot; becomes &quot;$9.99 monthly&quot;. That's where the 600 variable passes for 300 texts come from.</p>
<p>The spoken pass expands period abbreviations with regular expressions. Compiling a <code>Regex</code> costs far more than running one, which is why patterns are normally built once. Before the fix, the spoken pass compiled them on every call:</p>
<pre><code>val replacements = spokenPeriodKeys
    .mapNotNull { (writtenKey, spokenKey) -&gt;
        val written = localizedVariableKeys[writtenKey]?.takeIf { it.isNotEmpty() }
        val spokenWord = localizedVariableKeys[spokenKey]?.takeIf { it.isNotEmpty() }
        if (written != null &amp;&amp; spokenWord != null) written to spokenWord else null
    }
    .sortedByDescending { (written, _) -&gt; written.length }
    .map { (written, spokenWord) -&gt;
        Regex(&quot;/\\s*${Regex.escape(written)}(?!\\p{L})&quot;, RegexOption.IGNORE_CASE) to &quot; $spokenWord&quot;
    }</code></pre>
<p><code>spokenPeriodKeys</code> has ten entries, from &quot;day&quot; to &quot;annual&quot; in their short and long forms, and English defines all ten. So every spoken pass compiled ten patterns, and 300 texts compiled 3,000 <code>Regex</code> instances per tap. The second half of #4383 keeps the compiled list next to the word pairs it was built from and reuses it while the words stay the same, which in a paywall means the patterns are compiled once.</p>
<p>That makes each variable pass cheaper, not rarer, which is why the pass count above doesn't move. With only the cache switched off, a tap took 433 ms against 184 ms. The two halves of #4383 cost about the same, and they add up: with both switched off, the 300 text paywall took 700 ms. On the 89 component template, the same comparison was 100 ms against 50 ms.</p>
<h2><strong>When </strong><code><strong>derivedStateOf</strong></code><strong> can't tell two values apart: Fonts without </strong><code><strong>equals</strong></code></h2>
<p>A recalculation that ends in &quot;equal, skip&quot; is the cheaper case. Now consider one that can never end in &quot;equal&quot;.</p>
<p>Fonts uploaded in the dashboard are downloaded by the SDK and loaded from a file. <code>FontSpec.Downloaded.resolve()</code> builds a <code>FontFamily</code> out of new <code>LocalFileFont</code> instances on every call, and neither <code>LocalFileFont</code> nor its parent, Compose's <code>AndroidFont</code>, overrode <code>equals</code>. A class without <code>equals</code> is compared by identity, so two fonts pointing at the same file are still different objects, and because <code>FontFamily</code> compares its list of fonts, two families resolved from the same file were never equal.</p>
<p>The text's <code>fontFamily</code> is a derived state over the font spec, and the font spec is derived from the resolved overrides. Before #4383, those read the selection, so a tap reached the font too, two derived states downstream. Every recalculation built a new font object that Compose couldn't recognize as the same font, so every tap looked like a font change:</p>
<ol>
<li>The new <code>FontFamily</code> isn't equal to the previous one, so <code>checkDerivedStateChanged</code> reports a change and the text recomposes.</li>
<li>The text is laid out again, and <code>FontFamilyResolver</code>, the part of Compose that turns a <code>FontFamily</code> into an Android <code>Typeface</code>, looks the family up in its cache.</li>
<li>The cache key is a data class that includes the family. A data class compares every field, so the key matches only if the families are equal, and they never were.</li>
<li>The per font cache below it is keyed by <code>Font</code> equality, so it misses too.</li>
<li>The font's loading strategy is <code>Blocking</code>, which means it must be loaded before the text can be drawn, on the thread that asks. The file is parsed again on the main thread.</li>
</ol>
<p>The fix in <a href="https://github.com/RevenueCat/purchases-android/pull/4245">#4245</a> gives <code>LocalFileFont</code> value based equality, with a matching <code>hashCode</code> over the same four fields:</p>
<pre><code>override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other !is LocalFileFont) return false
    return file == other.file &amp;&amp;
        weight == other.weight &amp;&amp;
        style == other.style &amp;&amp;
        fileTypefaceLoader == other.fileTypefaceLoader
}</code></pre>
<p>On a 300 text paywall with a downloaded font, a build with both #4245 and #4383 switched off took 833 ms per tap, with 300 text recompositions and 300 font file loads. With only #4245 restored, it took 667 ms, with zero recompositions and zero loads.</p>
<p>The font fix shipped while we were still investigating the bug report, and the reporter saw no visible difference with that release. The defect was real and so was its cost, but proving that a defect exists is not the same as proving it caused the symptom you're chasing. In our measurements, the two costs that #4383 later removed were larger.</p>
<p>The two fixes also interact. With #4383 in place, a plain text's font no longer recalculates on a tap, so switching the <code>equals</code> off today changes nothing for selection: 193 ms with it and 184 ms without, within one frame. It still matters anywhere the font does recalculate, for example a text inside a plan card whose overrides read the selection. A value that lives in snapshot state needs a real <code>equals</code> whether or not today's code path happens to exercise it.</p>
<p>To see all three costs on screen, the demo below renders a synthetic paywall with 148 texts and a downloaded font through the real SDK, on the same emulator and debug build, and taps the plans with <code>adb shell input tap</code>. The overlay at the top reports each tap's cost, with override resolutions shortened to &quot;override calcs&quot;, and its spinner is driven by Compose's frame clock on the main thread, so it stops whenever the main thread is busy. On the left, #4245 and #4383 are switched off, and after the first tap, which is slower in both runs, a tap takes about 360 ms. On the right is the current SDK, at about 75 ms. The variable passes barely change between the two (296 and 288), because 144 of the 148 texts sit outside a package and still read the selected package. The overlay counts everything a real tap triggers, including checks the click handler runs before the selection changes, so its numbers aren't directly comparable with the instrumented test:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/7cc1e94e253f170b2ebeb866b2970df39e919179-766x528.gif" alt=""/></figure>
<h2><strong>Smaller audit fixes: Work repeated more often than its inputs changed</strong></h2>
<p>A Compose audit of the paywall renderer, done a week before the recording arrived, found three more places where work ran more often than its inputs changed. None of them was measured against tap latency:</p>
<ul>
<li><strong>Markdown parsing (</strong><a href="https://github.com/RevenueCat/purchases-android/pull/4194"><strong>#4194</strong></a><strong>)</strong>: the CommonMark parse in <code>Markdown</code>, the leaf renderer for every text, is now wrapped in <code>remember(text)</code> instead of running on every recomposition. Countdown texts recompose every second, and before #4245 so did every text with a downloaded font on every tap.</li>
<li><strong>Shadows (</strong><a href="https://github.com/RevenueCat/purchases-android/pull/4195"><strong>#4195</strong></a><strong>)</strong>: the outlines, paths, <code>Paint</code>, and <code>BlurMaskFilter</code> that <code>drawBehind</code> built on every draw are now built in <code>drawWithCache</code>, so each draw only clips and paints.</li>
<li><strong>Window size class (</strong><a href="https://github.com/RevenueCat/purchases-android/pull/4200"><strong>#4200</strong></a><strong>)</strong>: each component called <code>currentWindowAdaptiveInfo()</code>, which collects the window layout flow, only to read the width size class. A helper now derives it from the window size and density.</li>
</ul>
<p>For the first two, all 279 Paparazzi screenshots were byte for byte identical before and after, which shows that nothing visible changed, not that anything got faster.</p>
<h2><strong>Animation in the draw phase: Dimming a button</strong></h2>
<p>The package tap isn't the only tap on a paywall, and the buttons had their own per frame cost. When a button's action is in progress, its content fades out and a progress indicator fades in. <code>ButtonComponentView</code> animated the content alpha with <code>animateFloatAsState</code> and passed the <code>Float</code> down to <code>StackComponentView</code>, so the whole button stack recomposed on every frame of the fade. <a href="https://github.com/RevenueCat/purchases-android/pull/4201">#4201</a> hands the stack a provider instead, and the stack reads it inside the lambda form of <code>graphicsLayer</code>:</p>
<pre><code>private fun Modifier.contentAlpha(provider: (() -&gt; Float)?): Modifier =
    applyIfNotNull(provider) { alpha -&gt;
        graphicsLayer {
            this.alpha = alpha()
            clip = true
        }
    }</code></pre>
<p>Compose runs that lambda when it updates the layer's properties, so a new alpha value changes the layer without recomposing the stack or laying it out again. The provider is <code>null</code> while the button is fully opaque, which keeps the common case free of an extra layer, just like <code>Modifier.alpha</code> with a value of 1.</p>
<p>The shipped code still recomposes the button itself. It decides whether to pass a provider by reading the animated value in the composable body:</p>
<pre><code>val contentAlphaProvider: (() -&gt; Float)? = if (animatedContentAlpha.value == 1f) {
    null
} else {
    remember(animatedContentAlpha) { { animatedContentAlpha.value } }
}</code></pre>
<p>Reading .value in the body subscribes the button to every frame of the animation, even though == 1f only flips twice. The stack skips, because it receives the same remembered lambda each time, but the button doesn't. The demo below isolates this read in a small standalone app built in release mode with Compose BOM 2026.09.00, and counts how many times the button's scope and the stack's scope run during one press. The third variant moves the check into derivedStateOf { animatedContentAlpha.value != 1f }:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/782202e84f3c65dafbb2daa62cd1553329925320-540x570.gif" alt=""/></figure>
<p>Each counter includes the first composition, so one press cost the <code>Float</code> version about 60 recompositions of both scopes, the shipped provider about 60 of the button and 2 of the stack, and the <code>derivedStateOf</code> version 4 and 2. The real <code>ButtonComponentView</code> also reads the progress indicator's alpha in the body, so that read would have to move out of composition too.</p>
<h2><strong>Frames nobody asked for: An invisible spinner</strong></h2>
<p>A second report described a paywall that stopped updating after a package tap, this time in a Flutter app on an Android 10 phone (<a href="https://github.com/RevenueCat/purchases-flutter/issues/1954">purchases-flutter#1954</a>). The reporter also noticed that the paywall redrew continuously while nobody touched it: 307 frames in five seconds, according to <code>dumpsys gfxinfo</code>. Testing one component type at a time, the RevenueCat engineer on the issue found that only buttons did this.</p>
<p>The cause was the button's progress indicator. It was always composed, with its alpha animated to zero while idle:</p>
<pre><code>CircularProgressIndicator(
    modifier = Modifier.alpha(animatedProgressAlpha),
    color = progressColorFor(style.stackComponentStyle.background),
)</code></pre>
<p>An indeterminate CircularProgressIndicator runs an infinite animation, and alpha 0 doesn't pause it. Each animation frame invalidates the indicator's drawing, and each invalidation makes the window render a new frame, so an invisible spinner kept the whole paywall rendering at the display's refresh rate. In the Flutter app, the native paywall renders into a small queue of buffers that Flutter reads from. After the tap, the log shows that queue falling behind, then Skipped 36 frames!, and then the surface disconnecting, after which Flutter kept showing its last frame. The reporter suspected the constant stream of idle frames. Whether removing it alone fixed that phone wasn't confirmed, but the stream itself was real. <u><a href="https://github.com/RevenueCat/purchases-android/pull/4442">#4442</a></u> composes the indicator only while it's visible:</p>
<pre><code>// The indeterminate indicator animates forever, so composing it at alpha 0 redraws the paywall
// every frame while idle.
if (animatedProgressAlpha &gt; 0f) {
    CircularProgressIndicator(
        modifier = Modifier.alpha(animatedProgressAlpha),
        color = progressColorFor(style.stackComponentStyle.background),
    )
}</code></pre>
<p>On our emulator, an untouched paywall with the old behavior switched back on rendered 302 to 303 frames in five seconds across three runs. With the fix, it rendered 0. In the demo below, the &quot;Profile HWUI rendering&quot; developer option draws one bar per rendered frame. Ignore the bar heights, because frames on an emulator are slow to swap. What matters is that new bars keep arriving on the left. On the right, the tall strip at the right edge is left over from the first frames after launch, and nothing new arrives:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e62b528662bc7c71aa46d41a406d0c3264a2e055-726x800.gif" alt=""/></figure>
<p>If you hide an animated component with alpha, check whether its animation is infinite. Alpha controls what the user sees, not whether frames are produced.</p>
<h2><strong>What reordering can and cannot do: The interaction listener</strong></h2>
<p>When a package is tapped, the SDK calls the app's <code>PaywallListener.onInteraction</code> synchronously, and it used to do so before applying the selection, so a slow listener delayed the selection. <a href="https://github.com/RevenueCat/purchases-android/pull/4298">#4298</a> applies the selection first, which looks like it should keep the listener from delaying the frame. It doesn't, because the main thread runs one queued task at a time: the click handler, with both the selection write and the listener call inside it, is one task, and the next frame can't start until it returns. Measured from the start of the click handler to the frame that draws the new selection, on the 89 component template, with a listener that blocks for 300 ms, in two runs of ten taps:</p>
<ul>
<li><strong>Listener first (before #4298)</strong>: 350 to 356 ms.</li>
<li><strong>Selection first (#4298)</strong>: 342 to 343 ms.</li>
<li><strong>Listener posted to the main thread</strong>: 348 to 369 ms.</li>
<li><strong>Listener deferred until after the next frame</strong>: 41 to 48 ms.</li>
</ul>
<p>Applying the selection first saved about 10 ms, less than a frame, and posting doesn't help, because the posted task still runs before the next frame. Deferring the listener past the frame takes its cost out of the tap, though the 300 ms block then delays the frames that follow. Deferring also changes when apps receive the callback, so it's an API decision rather than a refactor. For now, keep <code>onInteraction</code> short and move anything slow off the main thread.</p>
<h2><strong>Conclusion</strong></h2>
<p>The habit this investigation leaves you with is a question to ask about any state that many composables observe: who reads it, and do they all need to? <code>derivedStateOf</code> protects you from recomposition, not from recalculation, and a calculation that reads more than it uses subscribes to more than it needs. When you fix something like this, keep a way to turn the fix back off in a measurement build, because &quot;it fixed a defect&quot; and &quot;it was the cause&quot; are different claims, and only an A/B can tell them apart.</p>
<p>The symptom was a short pause after a tap, and the first theories were about expensive drawing. On every paywall we measured, the real cost was invisible to tools that look at what changed on screen, because nothing changed. Composition did what it promised and skipped the components that looked the same. What made the tap slow was hundreds of components each asking, correctly, whether they needed to do anything.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Don’t build your own harness: lessons from building Paywalls AI Editor]]></title>
      <link>https://www.revenuecat.com/blog/engineering/lessons-paywalls-ai-editor</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/lessons-paywalls-ai-editor</guid>
      <pubDate>Tue, 10 Nov 2026 15:31:59 GMT</pubDate>
      <dc:creator><![CDATA[Azat Valiev]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[How one Slack message led us to rebuild our paywall agent]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/149685e32fca9e8765e98aef1411b0fc35e95d3a-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Earlier this year, we shipped an <a href="https://www.revenuecat.com/blog/company/paywalls-ai-editor">AI editor</a> for our remotely configurable paywalls product. Users can work with our agent to come up with a new paywall or edit an existing one.</p>
<p>On paper we fulfilled user requests, and adoption was growing rapidly. But there was a deeper issue that none of our eval or metrics could capture.</p>
<h2><strong>An inflection point: “it’s not even fair”</strong></h2>
<p>A pattern of issues started trickling in via user reports and internal feedback. On paper, the agent would accomplish the task the user asked, like creating the paywall, adding or overhauling a section of it. But the result it produced lacked the level of taste and quality that you’d expect a frontier agent to be capable of.</p>
<p>We worked to address this issue in several ways:</p>
<ul>
<li>We tried different models from various providers, with higher effort and reasoning levels</li>
<li>We tweaked the system prompt and skills</li>
<li>We injected messages with screenshots of the paywall, pushing the agent to check its work and maintain a high bar for quality</li>
</ul>
<p>There were notable improvements, but the gap between what users wanted, and what the AI delivered, remained massive.</p>
<p>We then got some feedback from Ryan, one of our Product Managers:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/de86c32ce4802cec8db16c18b802ae36796186d3-1080x1000.png" alt=""/></figure>
<p>(left) our old paywall editor, (right) using Fable in Claude Code(FYI: <strong>Astra</strong> is our internal term for the paywall agent)</p>
<p>This was a brutal inflection point. The feedback was originally about our agent getting the app’s name wrong. But the bigger issue was blatantly obvious.</p>
<p>How does our agent not understand that the paywall it produced is atrocious? Spacing, alignment, card within a card… there were too many issues to name. How did it think this was acceptable?</p>
<p>And how can Claude Code just one shot something that looks great while our agent flounders? This thought haunted me. No one would use our paywall editor if you could get <em>that</em> much better results with a simple Claude Code prompt.</p>
<p>So where did we go from here?</p>
<h2><strong>The old agent technically worked</strong></h2>
<p>To understand why our old agent was struggling, you first have to see how it worked.</p>
<p>Beginning with the data shape. Some remote paywall products use HTML, CSS and JS to render on all platforms. This provides the most flexibility, at the cost of feeling native, appearance, and performance.</p>
<p>We opted for native (well, <a href="https://www.revenuecat.com/blog/company/beyond-the-building-blocks-custom-components-for-paywalls-and-funnels">a bit of both now</a>). Our paywalls are stored as JSON trees: each node is a component with its own attributes. When your paywall is rendered, the component tree is mapped into the native components.</p>
<p>Here’s what a <a href="https://www.revenuecat.com/blog/growth/introducing-web-paywall-buttons/">purchase button</a> component can look like in our structure:</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;purchase_button&quot;,
  &quot;stack&quot;: {
    &quot;type&quot;: &quot;stack&quot;,
    &quot;background&quot;: { &quot;type&quot;: &quot;color&quot;, &quot;value&quot;: { &quot;light&quot;: { &quot;type&quot;: &quot;hex&quot;, &quot;value&quot;: &quot;#ff8a00ff&quot; } } },
    &quot;shape&quot;: { &quot;type&quot;: &quot;pill&quot;, &quot;corners&quot;: null },
    &quot;padding&quot;: { &quot;top&quot;: 12, &quot;bottom&quot;: 12, &quot;leading&quot;: 8, &quot;trailing&quot;: 8 },
    &quot;components&quot;: [
      {
        &quot;type&quot;: &quot;text&quot;,
        &quot;text_lid&quot;: &quot;PBcCWPIKbT&quot;,
        &quot;color&quot;: { &quot;light&quot;: { &quot;type&quot;: &quot;hex&quot;, &quot;value&quot;: &quot;#ffffffff&quot; } },
        &quot;font_weight&quot;: &quot;semibold&quot;,
        &quot;margin&quot;: { &quot;top&quot;: 0, &quot;bottom&quot;: 0, &quot;leading&quot;: 0, &quot;trailing&quot;: 0 }
      }
    ]
  }
}
// ... one of 20+ components on this screen</code></pre>
<p>Our agent had a very custom harness (set of tools, prompts and loop around the model). It understood the shape through an annotated JSON schema, and would operate on the paywall JSON directly, manipulating it with a set of tools.</p>
<p>This was a reasonable design for the first iteration.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/613e40fb1de41017a918cf82b66fccbf17c01068-1999x1801.png" alt=""/></figure>
<ul>
<li>The targeted JSON operations prevented it from making simple formatting mistakes we’d had in earlier prototypes</li>
<li>The schema showed it what was valid</li>
<li>Skills gave it reusable patterns and guidance</li>
</ul>
<p>But we were asking a lot of the model: it needed to learn this new schema on the fly for representing UI, along with the new tools. It needed to translate the user request into visual intent, then translate that visual intent into a series of tool operations on the JSON. And lastly, it needed to self-evaluate using a screenshot.</p>
<p>It was a bespoke workflow and syntax — a far cry from the Jetpack Compose, JSX and SwiftUI use cases it had been extensively trained on.</p>
<p>But the feature shipped.</p>
<h2><strong>The format is cooked</strong></h2>
<p>Still, I couldn’t shake the feeling that we were not just “one more prompt” away from closing this gap. Something was fundamentally wrong.</p>
<p>I read through Claude Code’s system prompt; there’s very little in there telling Claude how to make tasteful design, and certainly nothing specific to paywalls. Yet it achieved far better results even with models the harness wasn’t built for, like GPT.</p>
<p>Anthropic <em>has</em> a frontend design skill but, in my experience, the newer models still create great UI without it.</p>
<p>This led me to suspect the taste was being lost in translation. In a minimal harness, the models could express taste into code. But not into JSON patching tool calls…</p>
<p>What if our paywall could just be code? The model knows how to build a great UI in JSX. And JSX components are a tree very similar to ours: each node has JSON attributes and an optional set of children.</p>
<p>So we tried it. Here’s that same purchase button from earlier, as JSX:</p>
<pre><code class="language-typescript">&lt;PurchaseButton&gt;
  &lt;Stack background=&quot;#ff8a00ff&quot; padding={{ vertical: 12, horizontal: 8 }}&gt;
    &lt;Text text=&quot;Continue&quot; fontWeight=&quot;semibold&quot; color=&quot;#ffffff&quot; /&gt;
  &lt;/Stack&gt;
&lt;/PurchaseButton&gt;</code></pre>
<p>It turned out to be fairly simple. A transpile step turns the JSON into this intermediate JSX, which the agent can edit as a file. Once it’s done editing, a strict compiler runs, flagging any issues that could prevent the paywall from displaying or functioning correctly. When there are no blocking issues, the compiler turns the JSX back into the same JSON structure our backend &amp; SDKs expect.</p>
<p>Two days after rock bottom, the new prototype set a record on our blank paywalls eval dataset. That’s when we knew we were onto something.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/28f6ac9abb02c44c0cc5e1c26b02bb170674af9a-1758x1018.png" alt=""/></figure>
<h2><strong>Yet another coding agent</strong></h2>
<p>If the theory is that leaning into coding agent RL will outperform learning a new harness on the fly, why not lean in fully. Let’s give the agent a full workspace, simple mechanisms to read, and additional context as needed.</p>
<p>So we did just that. We gave it a folder of snippets to see examples of how to do certain component structures, which it can copy-paste as needed. It doesn’t need the full documentation for <strong>every component</strong> on <strong>every</strong> <strong>request</strong>, so I split it into multiple <code>d.ts</code> files that can be lazily loaded, instead of cramming 175KB of JSON schema at the start of every session. This meant paywall translations just became entries in a <code>strings/</code> folder. The agent can keep context about the app, like what the app does and who it is for, in a <code>context.md</code>.</p>
<p>And most importantly, this gave us room to expand. New capabilities can be represented with files/folders, and since things are lazily loaded in, there is minimal additional cost/overhead to do so.</p>
<pre><code class="language-typescript">/workspace
├── context.md
├── paywall.tsx
├── snippets/  (read-only)
│   ├── ctas/purchase_button.tsx
│   ├── feature_lists/checklist.tsx
│   └── ... (28 snippets)
├── strings/
│   ├── es_ES.json  (14 strings)
│   └── fr_FR.json  (14 strings, 2 stale)
└── types/  (read-only)
    ├── _readme.d.ts
    ├── base.d.ts
    ├── components/layout.d.ts
    ├── components/...d.ts
    └── overrides.d.ts</code></pre>
<p>We didn’t need to reinvent our own tools to work with this format. We used what the model already knows: a free loop with <code>Read</code>, <code>Write</code>, <code>Edit</code> and a compiler. The compiler even <em>feels</em> like <code>tsc</code>: giving line numbers and diagnostics with guidance when there are issues in the paywall code, and handing back a screenshot of the paywall when it succeeds.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/112dc14127987b75e61ffab685f5a6195f3272d7-1898x1336.png" alt=""/></figure>
<p>With a familiar format and harness, We were also able to remove over half of our system prompt and almost all of our skills. Most of those skills and prompts were compensating for a subpar format. As we continue to iterate, we focus on improving format first, then only prompt our way out if we must.</p>
<h2><strong>Works on my machine</strong></h2>
<p>The prototype broke eval records in two days. Getting that to production was a full team effort that took several weeks.</p>
<p>First, we had to build our confidence that this was production ready.</p>
<p>Our existing offline evaluations were a good start. Your users always use the agent in unexpected ways though, so we sampled hundreds of production traces and ran them against the new agent and created a dataset of new vs. old results. Our LLM judge mechanism was able to catch functional regressions: comparing the behavior, responses and results of the new vs. old agent, and ensuring the new agent was equal or superior in all cases. We also recruited many internal humans-as-judge to blind vote which version had better taste.</p>
<p>Only then did we started rolling out to external users — first, those who had given us feedback about taste and capability gaps.</p>
<p>Once we felt confident that this agent would be a definite step up in our production quality across the board, we had some infrastructure gaps to close.</p>
<p>The new agent was working hard. Between the large amount of turns it was using and the compilation work, we had to allocate additional CPU and memory. We also switched to Claude models: both Claude and GPT models perform well in the new harness, but Claude models currently have an edge in taste. Our new inference provider had different failure modes: stalled streams, empty responses, and increased downtime. So we hardened our retry and fallback mechanisms around the LLM calls.</p>
<p>After pushing through, we finished rolling out the new editor to 100% of users on September 2nd.</p>
<h2><strong>Fair enough</strong></h2>
<p>So, did it work?</p>
<p>Survey says: compared to the two weeks prior to rollout, across approximately 46K requests, our rate of positive feedback <strong>grew by 83%</strong> and negative feedback <strong>fell 40%</strong>.</p>
<p>We saw little difference with the new agent in our simpler requests like changing copy, adding a simple button, checking app store compliance, and automatic translations. But the gains show up in large paywall edits, new paywall creation, and anything that requires <em>taste</em>.</p>
<p>Going back to Ryan’s paywall tells the story.</p>
<h2><strong>If you’re building your own agent</strong></h2>
<ol>
<li><strong>Lean into the model’s strengths:</strong> models have been trained extensively with certain tasks, environments and tools. Lean into that. A great example of where this is starting to show up is <a href="https://developers.cloudflare.com/agents/tools/codemode/">code mode</a> with MCP tools; letting the model write code to string together data from several tool calls, then compose and manipulate it.</li>
<li><strong>Needing lots of brittle system prompts and skill changes is a smell: </strong>when we got the format right, we cut the system prompt in half. We stopped fighting with tuning verbiage changes in a prompt to get the desired result. And as we continue to optimize and add features, we’re looking at making the interface intuitive first, so we don't stack more debt in this category.</li>
<li><strong>If applicable, measure taste, not just completion:</strong> the problem flew under the radar as we didn’t have any automated, clearly-correlated measures for this. We still don’t have a great one. What’s helped is blind voting by humans between two different options, in addition to listening to user feedback.</li>
<li><strong>Take big swings</strong>: if your agent has decent evaluation mechanisms, the cost of prototyping and measuring big agent changes is relatively cheap. Try it, test it and see if there’s a better way. The payoff can be massive.</li>
<li><strong>Latency may be accepted for quality:</strong> the new agent takes more turns, reading in context, editing, compiling and iterating. This has significantly increased our latency. At first we anticipated users would be unhappy with this, but from the data it appears the quality of results is a far more important factor. (Of course, this can vary based on your type of application and users.)</li>
</ol>
<section><h3>Test the Paywalls AI editor for yourself</h3><p>The new AI editor for RevenueCat Paywalls is now available for every app. Simply describe what you want, and let AI do the rest.</p><p><a href="https://www.revenuecat.com/feature/paywalls">Explore Paywalls</a> · <a href="https://www.revenuecat.com/blog/company/paywalls-ai-editor">Learn more about AI Paywalls Editor</a></p></section>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[How to set up your first Apple retention message in App Store Connect]]></title>
      <link>https://www.revenuecat.com/blog/growth/apple-retention-messaging</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/apple-retention-messaging</guid>
      <pubDate>Thu, 05 Nov 2026 17:18:13 GMT</pubDate>
      <dc:creator><![CDATA[Margarita Loktionova, Luigi Forte]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Apple Retention Messaging is now available to everyone in App Store Connect. Here’s how to set up your first message and make the moment before cancellation count.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/5deebdce70faf7936bc90b6431ab9dd6ea7cd793-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Apple Retention Messaging is now available to every developer in App Store Connect, with no code or server integration required.</p>
<p>It shows subscribers a short message from your app right before they confirm their cancellation.</p>
<p>When someone taps ‘Cancel Subscription’, they’re one step away from leaving. This is your chance to give them one good reason to stay.</p>
<p>Here’s how to choose what to say, decide whether to add an offer, set up your first message in App Store Connect, and measure the results. We’ll also cover when it’s worth using <a href="https://www.revenuecat.com/feature/retention-messaging">RevenueCat’s real-time Retention Messaging</a> instead.</p>
<h2>How does Apple Retention Messaging work in App Store Connect?</h2>
<p>Retention Messaging appears when a subscriber starts canceling through Apple’s subscription settings. Before confirming, they see a message from your app reminding them of its value. If you set it up in App Store Connect, that’s the message you’ve assigned to their subscription.</p>
<p>A retention message can show text only, text with an image, or text with an offer:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/934a23eb00f97de64034c56f89f85561c9b25fd2-1534x814.png" alt="Apple’s cancellation screen showing three retention message formats: text only, text with an image, and text with an offer"/></figure>
<p>The setup happens on your app’s Subscriptions page in App Store Connect. Write the message, add a translation for each language, and assign it to the subscriptions it should cover.</p>
<p>Each subscription can have only one message, so every subscriber who cancels that plan sees the same text. Any offer you attach goes only to subscribers who meet your eligibility rules, while the rest see the same message without the offer.</p>
<h2>App Store Connect vs the Retention Messaging API</h2>
<p>There are two main ways to set up retention messages: in App Store Connect, or through Apple’s <a href="https://www.revenuecat.com/blog/engineering/apple-retention-messaging-api">Retention Messaging API</a>. </p>
<p>App Store Connect is the quickest way to start. It needs no code or access request, but it comes with limitations, like one message per subscription and offers only for subscribers who’ve paid for at least a month. Using the Retention Messaging API removes those limits. Apple checks with your server every time a subscriber starts to cancel, so you can pick a different message and offer for each subscriber and run</p>
<p>RevenueCat <a href="https://www.revenuecat.com/feature/retention-messaging">handles the server side for you</a>, so you can set up messages, targeting rules, and experiments in the dashboard.</p>
<p>Here’s how the two options compare:</p>
<table>
<thead><tr>
<th></th>
<th><p>App Store Connect</p></th>
<th><p>Retention Messaging API </p>
<p>(with RevenueCat)</p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Show messages with text and images</p></td>
<td><p>✓</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Offer a discount</p></td>
<td><p>✓</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Set offer eligibility (paid duration, wait between offers, countries)</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Target messages by subscriber segment</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Show an offer to free trial users</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Show different messages for subscribers who get the offer and those who don’t</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Offer a switch to another plan (like annual to monthly)</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Auto-translate messages</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>A/B test messages</p></td>
<td><p>–</p></td>
<td><p>✓</p></td>
</tr>
<tr>
<td><p>Get started without requesting access from Apple</p></td>
<td><p>✓</p></td>
<td><p>–</p></td>
</tr>
</tbody>
</table>
<h2>Step 1: Start with one good reason to stay</h2>
<p>Before writing your message, ask yourself: why do subscribers choose your app, and what makes them leave?</p>
<p>You probably have more clues than you think. App Store reviews, support conversations, and <a href="https://www.revenuecat.com/docs/tools/customer-center/customer-center-configuration">Customer Center</a> cancellation surveys show what subscribers value and where they get stuck. You can also use <a href="https://www.revenuecat.com/docs/dashboard-and-metrics/charts">RevenueCat Charts</a> to check when subscribers cancel (e.g., during a free trial or at their first renewal).</p>
<p>In App Store Connect, everyone who cancels a subscription sees the same message text. You can only limit who gets the offer attached to it. So pick a reason to stay that fits most subscribers on that plan, without misleading the rest.</p>
<p>For example, Character.AI highlights the one thing subscribers lose if they cancel: daily premium chats. It also lists what comes back — full ads, slow mode, and basic memory.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e2ee84aab88dc82e420b50c13ac19ebe5e3a9359-1448x1086.png" alt="Character.AI retention message in Apple’s cancellation flow, listing the premium features subscribers lose if they cancel"/></figure>
<p>With the Retention Messaging API, you can show different messages to different groups, like trial users, long-time subscribers, or subscribers who have already redeemed an offer.</p>
<p>RevenueCat handles the API for you, so you can set up these rules in the dashboard without building your own server.</p>
<h2>Step 2: Decide whether to add an offer</h2>
<p>Next, decide whether to include an offer in your message. Discounts can help when price is the reason someone leaves. That said, it’s not always the case.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d1dc245fb70ab73a6f99771255585991b05d3656-1448x1086.png" alt="Cancellation screen with an offer, example"/></figure>
<p>In our <a href="https://www.revenuecat.com/blog/growth/subscription-app-churn-reasons-how-to-fix">data from Google Play cancellation surveys</a>, ‘not enough usage’ was the top reason subscribers canceled (37%), just ahead of cost (35%). As <a href="https://www.revenuecat.com/blog/growth/not-enough-usage-churn">Daphne Tideman puts it</a>: “If someone isn’t using the app, why would they care what it costs?”</p>
<p>Our <a href="https://www.revenuecat.com/blog/growth/apple-retention-messaging-benchmarks">analysis of 22 million Apple retention messages</a> across 309 apps also found that subscribers accepted only about 1.1% of the discount offers and 0.6% of the plan switches they were shown.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ea2b75afb2b8ffd67e7786ee83f447688f4cf1af-3296x1736.png" alt="Chart of offer acceptance rates for Apple retention messages: about 1.1% for discount offers and 0.6% for plan switches"/></figure>
<p>The revenue impact is another factor to consider.</p>
<p>In a recent experience by the audio stories app Dipsea, annual subscribers who tried to cancel saw either a message with no offer or the same message with 50% off.</p>
<p>The discount had a slightly higher save rate (22.1% vs 18.8%) but brought in less than half the revenue per subscriber so far ($1.24 vs $3.16).</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/dda476a7db048fe31cae0022eb97080d1ee804c8-2048x1152.png" alt="Bar charts comparing Dipsea’s no-offer and 50% off messages for annual subscribers: save rate of 18.8% vs 22.1%, and revenue per subscriber of $3.16 vs $1.24"/></figure>
<p>Before deciding whether to offer a discount, try to answer the following:</p>
<ul>
<li><strong>Is price a common complaint?</strong> Check reviews, support tickets, and cancellation surveys.</li>
<li><strong>Who should get it?</strong> In App Store Connect, you can set a minimum paid subscription duration to exclude brand-new subscribers, and choose which countries the offer is available in. Free trial users can’t currently get offers there at all, since the minimum is one month of paid subscription.</li>
<li><strong>How often should the same subscriber get an offer?</strong> You can set a wait time between offers, starting at two months, so nobody gets a discount every time they try to cancel.</li>
<li><strong>Is it working? </strong>Use <a href="https://www.revenuecat.com/docs/tools/experiments-v1/experimenting-with-retention-messaging">RevenueCat Experiments</a> to see what performs best for your audience. For example, you can show a message with no offer to one group and a message with a discount to another, then compare save rate and revenue. It requires Apple’s approval for real-time messages.</li>
</ul>
<aside class="tip"><strong>Tip</strong><p>Don’t mention the discount or price in your header or body (e.g., “Stay for 50% off”). Apple shows the offer and its price separately and rejects messages that mention them.</p></aside>
<h2>Step 3: Write your message</h2>
<p>The retention message subscribers see in Apple’s cancellation flow has a header and a short body. Use them to remind subscribers what their subscription includes, such as specific features, content, or perks.</p>
<p>If you’re approved for real-time messages, RevenueCat also lets you show different messages to different groups. Think of creating personalized messages for trial users and long-time subscribers, highlighting what matters most to each segment.</p>
<p>Three angles for a hypothetical meditation app:</p>
<ul>
<li><strong>Highlight what they’d lose</strong>: ‘Keep your bedtime routine. If you cancel, you’ll move to the free plan and lose sleep stories and most of the session library.’</li>
<li><strong>Address why they’re canceling</strong>: ‘Short on time? Premium includes five-minute sessions for your busiest days, so you can keep your practice going.’</li>
<li><strong>Tease what’s coming</strong>: ‘A new sleep series drops every Monday. Stay Premium to hear each one as it’s released.’</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/bd7951f90ed595e8992b217cde096c118ae390c5-1448x1086.png" alt="Example retention message for a meditation app with a sleep series image, reading ‘A new sleep series drops every Monday. Stay Premium to hear each one as it’s released.’"/></figure>
<p>Whichever angle you choose, the strongest messages pair a functional point with an emotional one.</p>
<p>The functional part names what subscribers would give up: sleep stories, five-minute sessions, offline downloads. The emotional part says why it matters: a calmer bedtime, a few quiet minutes in a busy day.</p>
<h2>Step 4: Add an image (optional)</h2>
<p>You can also add an image of the premium content or features subscribers would lose, like sleep story artwork or a guided session screen for our meditation app.</p>
<p>This helps the message stand out on the cancellation screen and shows subscribers what they’d give up at a glance.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/bd7951f90ed595e8992b217cde096c118ae390c5-1448x1086.png" alt="Retention message on Apple’s cancellation screen with an image above the header and body text"/></figure>
<p>It’s optional, and it only appears when there’s no offer, or the subscriber isn’t eligible for it. When they are, Apple shows the offer in its place.</p>
<p>A few tips for getting it right:</p>
<ul>
<li><strong>Match the message:</strong> Add a relevant visual that fits your header and body</li>
<li><strong>Use original screens or content:</strong> Subscribers will recognize screenshots or artwork from your app more easily than generic stock photos</li>
<li><strong>Keep it clean:</strong> Avoid long text or a cluttered layout</li>
<li><strong>Use the right dimensions:</strong> Apple accepts 3840 × 2160 or 5244 × 2950 pixels</li>
</ul>
<h2>Step 5: Set up your message in App Store Connect</h2>
<p>On your app’s Subscriptions page in <a href="https://appstoreconnect.apple.com/">App Store Connect</a>, select ‘Get Started’ on the Retention Messaging banner, then write the header and body. Everyone on the subscriptions you assign sees this text.</p>
<p>To add more languages, use the language menu next to Message Text. There’s no copy or auto-translate option, so you enter each one by hand.</p>
<p>In the same step, you can add an image from your asset library or upload a new one at 3840 × 2160 or 5244 × 2950 pixels.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4eb81f4750d93896267e8f43958abfa8afaeb724-2631x1442.jpg" alt="Set up screen + the image field in App Store Connect’s retention message setup, with options to choose from the asset library or upload a new image"/></figure>
<p>To attach a retention offer, create it within the same flow.</p>
<p>First, choose how subscribers pay: a discounted price each billing period (pay as you go), one discounted payment for the whole offer (pay up front), or nothing for a set time (free).</p>
<p>Then set how long the offer lasts and its price. You set the price once, and Apple converts it for all 175 countries or regions. You can still adjust it for specific countries.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e814e00e0c4890c04cdf7246402c4efc285f0337-1298x1000.png" alt="Retention offer settings in App Store Connect, showing offer type, duration, and price"/></figure>
<p>From here, choose the countries or regions where the offer is available. You can select all of them.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/8e4dd634bb27370d2fc19d09ca4dd242b2c9dfde-1334x654.png" alt="Country or Region Availability settings for a retention offer in App Store Connect"/></figure>
<p>Next, set eligibility so the discount applies only to certain subscribers and excludes others, such as free trial users and subscribers who recently redeemed an offer.</p>
<p>There are two settings:</p>
<ul>
<li><strong>Minimum paid subscription duration (required)</strong>: how long someone must have paid before they qualify, starting at one month. This means free trial users currently can’t get the offer.</li>
<li><strong>Wait between offers (optional)</strong>: how long after an offer ends before the same subscriber can redeem it again, starting at two months.</li>
</ul>
<p>Then, assign the message to your subscriptions.</p>
<p>You can use one message for all of them, or create separate ones (up to 10 per app) if you want different copy for each plan, like weekly, monthly, and annual. Each subscription can only have one message.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/221f275f3999fe240a5ae765818515bf89a23eaf-2606x1331.jpg" alt="Subscriptions &amp; Offers section in App Store Connect, where you assign the retention message to a subscription, with a preview of the cancellation screen"/></figure>
<p>Finally, check the preview and submit the message for Apple’s review, and test the cancellation flow in sandbox.</p>
<p>Make sure each subscription shows the right message, the copy and image display properly in every language, and the offer terms and eligibility show up as intended.</p>
<p>For Apple’s own instructions, see <a href="https://developer.apple.com/help/app-store-connect/manage-subscriptions/configure-retention-messages/">Configure retention messages in App Store Connect</a>.</p>
<h2>Step 6: Measure whether it works</h2>
<p>Once your message is live, start tracking how subscribers respond on the cancellation screen and how it impacts revenue over the following weeks.</p>
<p>Apple’s reporting shows what subscribers chose on the cancellation screen. With the real-time integration, RevenueCat’s retention messaging charts and Experiments also track whether saved subscribers keep paying.</p>
<p>Focus on these key metrics:</p>
<ul>
<li><strong>Save rate:</strong> The share of subscribers who didn’t cancel within 24 hours or accepted the offer</li>
<li><strong>Offer acceptance:</strong> The share of subscribers who accepted the offer</li>
<li><strong>Active and set to renew:</strong> The share still subscribed with auto-renew on, which shows whether saves last</li>
<li><strong>Churn:</strong> The share who canceled after all</li>
<li><strong>Realized LTV per customer:</strong> The revenue each subscriber has actually brought in so far</li>
</ul>
<p>The bottom line is: a higher save rate doesn’t always mean a better message.</p>
<p>That’s what happened in the Dipsea experiment. The discount had a slightly higher save rate, but fewer of those subscribers stayed set to renew, and each brought in less revenue than with no offer.</p>
<h2>Go further with RevenueCat’s real-time Retention Messaging</h2>
<p>A retention message works best when it’s relevant to the subscriber seeing it. That means highlighting the right reason to stay, tailoring the message to each group of subscribers, and offering a discount only when it can truly change someone’s mind.</p>
<p><a href="https://www.revenuecat.com/feature/retention-messaging">RevenueCat’s Retention Messaging</a> connects to Apple’s cancellation flow in real time, so you can:</p>
<ul>
<li><strong>Target messages and offers:</strong> Choose which message, plan, or offer each subscriber sees in Apple’s cancellation flow, based on trial status, purchase stage, and offer history</li>
<li><strong>Manage messages across languages</strong>: Auto-translate your copy, edit all locales in one screen, and copy existing messages to make setting up more products and audiences easier</li>
<li><strong>Test and measure:</strong> Compare messages and offers with RevenueCat Experiments, then see what happened after each cancellation, from saves to renewals and revenue</li>
<li><strong>Skip the infrastructure work:</strong> RevenueCat manages the real-time connection with Apple</li>
</ul>
<p>To get started, you’ll need Apple’s approval for real-time messages. If you’ve already set up a message in App Store Connect, you can build on that with RevenueCat. The two work together, so you can add targeting and experiments when you’re ready. Our <a href="https://www.revenuecat.com/docs/platform-resources/apple-platform-resources/apple-retention-messaging-api">setup guide</a> walks through each step.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Touch Grass had 40,000 preorders before Rhys Kentish worked out it would cost $20,000 a week to run]]></title>
      <link>https://www.revenuecat.com/blog/growth/rhys-kentish-touch-grass-launched-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/rhys-kentish-touch-grass-launched-podcast-2026</guid>
      <pubDate>Wed, 04 Nov 2026 14:05:50 GMT</pubDate>
      <dc:creator><![CDATA[Charlie Chapman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Rhys Kentish rewrote it in three sleepless days; keeping the 50,000 people who arrived on launch day was harder.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/0e9082c620866148b0c1b7cd881a94c6f0368ff9-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Touch Grass is a screen time app with one rule: your distracting apps stay locked until you go outside and take a photo of real grass. Rhys Kentish, a Welsh mobile engineer in London with ten years of app work behind him, built it as a side project to fix his own winter scrolling habit, posted a rough prototype to Reddit, and watched it go viral before it existed. On this episode of Launched, Charlie Chapman talks to Rhys about the launch that nearly bankrupted him, the parody app that drew a cease-and-desist, and the retention problem a viral spike leaves behind.</p>
<p><a href="https://www.youtube.com/watch?v=TmrpIoKXN2c">Watch on YouTube</a></p>
<h2><strong>40,000 preorders, and a Google Vision bill of $20,000 a week</strong></h2>
<p>The first version of Touch Grass checked your photo with Google's cloud vision API, because the plan was modest. &quot;I was expecting maybe, you know, a couple hundred people to download it in the first couple of weeks,&quot; Rhys says. He posted the polished version to Hacker News and Reddit one morning, went for lunch, and came back convinced his analytics were broken: &quot;I was just like, what, 6000 people are on this stupid website that I built.&quot;</p>
<p>He put the app up for preorder the same day, and he thinks that's a big part of why it kept spreading. People could press a button without hitting a price, &quot;because as soon as you kind of hit a paywall, no one will ever want to share it again.&quot; Around 40,000 of them ended up with the App Store notification that the app was live.</p>
<p>Then he did the arithmetic on Google Vision at that scale. &quot;It would have cost something like $20,000 a week to run on the Google Vision API. And I was just like, oh my days.&quot; The fix was the kind you can only do once: &quot;I did not sleep for three days while I rewrote the entire, like, vision detection thing to native on device.&quot; It shipped before launch, just about, as Charlie says. Launch day itself was a party with family and friends and, in Rhys's words, &quot;something stupid, like 50,000 unique active people on the app.&quot;</p>
<p>The lesson is the one every viral-app founder learns in the wrong order: a per-call cloud API is a fine prototype decision and a dangerous launch decision, because the bill scales with the hook.</p>
<h2><strong>The website is one screen recording, and the tweet got 1.5 million impressions</strong></h2>
<p>The marketing for Touch Grass was unusually small. The website hasn't changed since launch day: &quot;It's just a screen recording and then some text saying like reduce your screen time something, download Touch Grass and that's all the website is.&quot; The first post was &quot;a very, very, very early prototype of the sort of mechanism no like UI polish or anything,&quot; which hit the top of r/SideProject in November; Rhys spent two or three months polishing it and posted again.</p>
<p>He has one timing tip for European developers: post in the morning. If it does well in Europe, it's the first thing the US East Coast sees when it wakes up, and then the West Coast after that. Charlie admits to setting 3 a.m. alarms for the same reason back when Product Hunt ran on a daily cycle.</p>
<p>Then a friend told him to tweet it. The tweet was a single sentence, &quot;I built an app to stop me doing scrolling by touching grass. That's it.&quot;, over a screen recording, and it ended up with around 1.5 million impressions, reporters in the replies, and interview requests from TechCrunch and Fast Company. All of this while the app was still in preorder.</p>
<p>Charlie's observation is what makes the story useful rather than just lucky. Most viral stories hinge on one famous account picking something up. This one &quot;landed and performed excellent on multiple separate platforms at the same time,&quot; which points at the hook rather than the channel. Rhys agrees, and says it's deliberate: &quot;The way I start marketing is I will distill the app into one sentence,&quot; which he then says to people in real life and watches their faces before he builds. For Touch Grass, they chuckled.</p>
<h2><strong>Tinder's lawyers objected to one word in Tender's App Store listing</strong></h2>
<p>Rhys's next app after Touch Grass was Tender, a Tinder parody in which every profile is his wife and you can only swipe right. She loved it. Tinder didn't. &quot;They sent me a scary letter saying cease and desist. But it wasn't for the name. It wasn't for the gesture. It was for the word swipe in the App Store listing.&quot; Tinder, it turns out, has trademarked &quot;swipe.&quot; Rhys's reaction: &quot;okay, this is scary.&quot; The listing was changed and the app is still in the store.</p>
<p>The twist is what happened next. TikTok didn't care much about the app, but &quot;TikTok actually resonated with the story of Tinder sending me a cease and desist,&quot; and the legal letter became the marketing moment. Rhys shrugs at this as the natural order of things: &quot;I do everything with the story in mind sort of thing.&quot; Charlie's version is the old line about never wasting a crisis, with a sensible caveat about being careful where lawyers are involved.</p>
<h2><strong>Touch Grass was the easiest app in the world to delete</strong></h2>
<p>The 50,000-user launch hid a flaw that took Rhys a while to see. &quot;It is an app that is so easy to uninstall. Like if you want to get on Instagram, the first thing you're going to do is uninstall touch grass like if it's raining outside or something like that.&quot; You couldn't just switch it off, &quot;which might have been a product oversight by me at the time.&quot; Charlie names it for him: a churn problem. He adds a detail that makes it worse. iOS now asks, at the moment you delete an app, whether you'd like to cancel the subscription too, and almost everyone says yes.</p>
<p>So the rebuild is about staying rather than spiking. Rhys is gamifying the whole thing, with eight-bit UI, rewards for hours spent off your phone, and critters that visit a meadow, plus &quot;guides&quot; modeled on the dogs he and his wife dog-sit, with a paid feature planned where your own pet becomes your guide. He's candid about the shape of the growth chart: &quot;Touch grass graph is like this mega mega viral spike and then a very, very long tail.&quot; He's never run a paid ad for it, and he's not trying to recreate the spike. &quot;I'm just trying to rebuild slowly and not necessarily chase the viral moment again and just work on the retention side of things.&quot;</p>
<h2><strong>Charlie's advice on copycats: assume nobody is copying you</strong></h2>
<p>Rhys isn't sure he'd post an unpolished prototype today, because people can copy you faster now. Charlie pushes back, hard. Even if a thousand clones appeared the day after a viral post, the preorder page meant the traffic had somewhere to land; without it, &quot;there would have been 30 apps called Touch Grass&quot; and the clones would have collected the downloads. Copying, he says, &quot;is a tale as old as the App Store,&quot; and the fear of it mostly stops people from marketing at all. His base case: &quot;I think it's good to have your base assumption be that nobody cares about you, and nobody's like sitting there copying you. And if people are copying you, that's a signal that you've you've gotten traction.&quot;</p>
<p>Rhys's answer is that character is the moat, and so is the person. &quot;I think the idea has to have character and those apps or ideas or websites or games or whatever, they're the hardest to copy.&quot; He has clones every day now, and people on Reddit and Twitter point at them and say he did it first, because they follow him, not just the app. &quot;Like your app is not necessarily the brand like you are your brand.&quot;</p>
<p>There's <a href="https://www.youtube.com/watch?v=TmrpIoKXN2c" target="_blank" rel="noopener noreferrer">more in the episode</a>: the Canadians who demanded touch snow mode (it now has touch sand and touch anything modes too), the unreleased app that charged Rhys real money every time he opened TikTok, the cinema he rented to premiere his first app's YouTube vlog, and the half-finished bird-song app that's Strava for wood pigeons.</p>
<h2>Guest links</h2>
<ul>
<li><a href="https://x.com/RhysKentish">Rhys Kentish on X</a></li>
<li><a href="https://touchgrass.now/">Touch Grass</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[App Growth Annual 2026 recap: key insights + 5 experiments to try ]]></title>
      <link>https://www.revenuecat.com/blog/company/app-growth-annual-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/app-growth-annual-2026</guid>
      <pubDate>Mon, 02 Nov 2026 16:16:48 GMT</pubDate>
      <dc:creator><![CDATA[Lorelei Whitman]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Everything that happened at App Growth Annual, plus five experiments inspired by the keynotes ]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/2983b08026215a26a4e336fa6d18caed15170935-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>On October 21, App Growth Annual took over The Glasshouse in New York, hosting a room of 400+ app builders, with 5,000+ joining online from around the world. Whether you joined us on the day or are looking to catch up on what you missed, read on for the definitive debrief on everything that went down, including <a href="https://youtube.com/playlist?list=PLZypamu0Cy0g&amp;si=NCgeru8CfIhTRzDn">all the session recordings</a> — plus five growth experiments inspired by the day’s presentations, so you can keep the momentum going.</p>
<p>Let’s kick off with our main stage sessions.</p>
<h2><strong>1. Julie Hansen (Babbel): breaking the rules is sometimes what’s best for your app</strong></h2>
<p>Growth advice typically favors freemium, short funnels, recurring subscriptions over lifetime, and focusing on US markets first. But <a href="https://www.linkedin.com/in/juliehansen/">Julie Hansen</a>, CRO and US CEO of Babbel, opened the day by walking through exactly what Babbel ignored from the industry’s rulebook.</p>
<blockquote><p>Maybe one day we will start to do things a bit more conventionally — go against our own rule-breaking.</p><footer>Julie Hansen - CRO &amp; US CEO, Babbel</footer></blockquote>
<p>Many of the contrarian bets behind Babbel’s 19 years of growth — and now 30 million subscribers — trace back to a single unconventional decision: going premium in 2009. Here are the key decisions Julie shared, and the trade-offs behind them.</p>
<ul>
<li><strong>Premium filters for motivated users: </strong>Babbel tried ads before going premium in 2009. For a language-learning app, they found asking for money upfront identified users who would retain better, as they were committed to putting in the work long-term. The choice funded the business and sharpened <a href="https://www.revenuecat.com/blog/growth/product-market-fit-subscription-apps/">product-market fit</a>, but it also meant a smaller audience and more pressure on marketing.</li>
<li><strong>Longer onboarding is only valuable if the product delivers:</strong> asking how many minutes a day you'll study, why you're learning, and whether you've studied before can reinforce commitment. But if onboarding promises personalization, the product must deliver it; additional questions are only valuable when they build commitment or inform the experience.</li>
<li><strong>Web checkout creates trade-offs: </strong>Babbel values its <a href="https://www.revenuecat.com/guides/web-to-app-funnels">web funnel</a> for data, speed, and economics, including avoiding app store fees. The flip side: Julie notes Babbel lost focus on app marketing for a time. It’s important to optimize the full <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels">web-to-app</a> experience, not just checkout costs.</li>
<li><strong>Lifetime plans can match the job:</strong> language learning is a journey people dip in and out of, so short plans create performance pressure. <a href="https://www.revenuecat.com/blog/growth/lifetime-subscriptions">Lifetime subscriptions</a> remove that, and “Babbel sells a lot of them”. The downside: lifetime is the end of the monetization road, unless you have new products to offer or hybrid monetization in play.</li>
<li><strong>Win-backs matter more over time:</strong> People drop in and out of learning, so Babbel is increasingly investing in <a href="https://www.revenuecat.com/blog/growth/app-reactivation-strategy-how-to">retention and reactivation</a>, balancing it against new user acquisition.</li>
</ul>
<aside class="tip"><strong>Experiment inspired by Julie’s session</strong><p>Add one or two questions to onboarding about your user’s goals or intended commitment. Compare completion, trial-to-paid conversion, and early activation against your current flow, by acquisition channel. Do those questions build commitment or simply introduce drop-off?</p></aside>
<p><a href="https://www.youtube.com/watch?v=ALhuKCmn_Ig">Watch on YouTube</a></p>
<h2><strong>2. Greg Cohn (Burner): fighting churn is a missed opportunity</strong></h2>
<p>What do you do when your app's <em>aha!</em> moment is the moment users throw it away? <a href="https://www.revenuecat.com/blog/growth/greg-cohn-burner-sub-club-podcast-2025">Greg Cohn</a>, Co-founder and CEO of Burner (Ad Hoc Labs), took to the stage to explain how Burner stopped trying to turn &quot;tourists into locals&quot; and instead designed a growing business that embraced its churn-by-design.</p>
<blockquote><p>Trying to get them to stay long-term when they had a short-term use case was fighting gravity.</p><footer>Greg Cohn - Co-founder &amp; CEO — Burner</footer></blockquote>
<p>The impact is undeniable, with a returning Burner user's subscription now worth 25% more than a new user's, and returning-user LTV up by 35%. Here are the lessons that got Burner here:</p>
<ul>
<li><strong>Strong conversion can hide the wrong retention strategy: </strong>Burner converted installs to paid at almost 6x the median, but M1 retention was 38%, below the RevenueCat median. The challenge was that many customers needed a number temporarily, not indefinitely.</li>
<li><strong>Look for returners in your data:</strong> over a quarter of people who cancelled came back within a year (2.6x <a href="https://www.revenuecat.com/state-of-subscription-apps">the utility app median</a>), with zero effort from Burner. Returners made up about 10% of MRR, and over 30% of new subscription revenue.</li>
<li><strong>Make leaving easy:</strong> instead of fighting cancellations, Burner offers a $1.99 ‘pause’ plan (vs. $9.99 standard) to hold a user's number. It saves about 10% of would-be cancellations. They’re also testing keeping numbers active for a period after a plan expires, so users can see what they're missing.</li>
<li><strong>Don't treat returners like new users:</strong> <a href="https://www.revenuecat.com/blog/growth/premium-win-lifecycle-messaging">returning users</a> already know why they're there. Rather than wasting their time with normal onboarding, Burner shows them what's new, offers a more generous free trial, and leads with annual pricing as the best value. As a result, returning users have 2x the trial conversion of new users, and 5x the annual take rate.</li>
</ul>
<aside class="tip"><strong>Experiment inspired by Greg’s session</strong><p>Calculate how many customers return after cancelling, and how much new subscription revenue they generate. If the opportunity is meaningful, test one tailored returner experience — like a welcome-back paywall or a different offer — against the standard path. Compare conversion, plan mix, and LTV to see the impact, then use our <a href="https://www.revenuecat.com/reactivation-calculator">reactivation calculator</a> to size the opportunity.</p></aside>
<p><a href="https://www.youtube.com/watch?v=EidYLPez7b8">Watch on YouTube</a></p>
<h2><strong>3. Ajay Mehta (Tolan): hybrid monetization gives apps a way to balance compute cost</strong></h2>
<p>Two years ago, <a href="https://www.revenuecat.com/blog/growth/ajay-mehta-sub-club-podcast-2025">Ajay Mehta</a> was sitting in the audience at the first App Growth Annual. This year, he was on the main stage.</p>
<p>In those two years, he co-founded Tolan, a voice-first AI companion app that has since grown to more than one million users and 100k+ subscribers. But with 70+ million AI conversations happening in Tolan, Ajay knew from the first OpenAI bill that its unit economics were unsustainable.</p>
<blockquote><p>Spend your extra margin on growth and on the people who already love you.</p><footer>Ajay Mehta - Co-founder — Tolan</footer></blockquote>
<p>Ajay’s session broke down the monetization models Tolan speedran while racing the compute cost countdown. Here are the key learnings that got Tolan to healthy unit economics:</p>
<ul>
<li><strong>AI changes the math:</strong> for the last generation of consumer apps, a big, engaged user base was pure upside. For an <a href="https://www.revenuecat.com/blog/growth/ai-feature-cost-subscription-app-margins">inference-heavy product, tokens cost real money</a>, and cheaper models don't automatically fix margins. Tolan quickly learned that every user conversation carried a cost, and growing was actually hurting their profit margins.</li>
<li><strong>Product-market fit should factor in conversion:</strong> Tolan first clicked with Gen Z, which drove installs but not payment. Users in their late 20s–40s found deeper, everyday uses for Tolan, and had the means to pay for a subscription. Tolan rebuilt its creator program around this ICP, and added new character designs to appeal to them.</li>
<li><strong>A hard paywall has a ceiling:</strong> a hard paywall protects margins, but it caps your audience at the people who'll pay right away. Rather than lose users who couldn’t pay (like its Gen Z audience), Tolan moved to a <a href="https://www.revenuecat.com/blog/growth/freemium-tier-design">freemium model</a>, with a high-intent onboarding paywall (personalized with what users shared during onboarding) and feature-gated paywalls further down the funnel.</li>
<li><strong>Price for willingness to pay and serving cost: </strong>Tolan now offers Lite, Plus, and a $200/month Unlimited plan, with tiers set by cost to serve. Expensive new AI capabilities start on higher tiers and only move to lower tiers as serving costs fall.</li>
<li><strong>Hybrid is the natural fit for AI apps: </strong><a href="https://www.revenuecat.com/blog/growth/ai-hybrid-monetization">mixed monetization</a> allows AI apps to <a href="https://www.revenuecat.com/blog/growth/hybrid-monetization-techniques">lower the floor and raise the ceiling</a>. Tolan combines subscriptions and one-off top-ups, while experimenting with ads and offering a family plan. Its emphasis on monthly subscriptions challenges the assumption that annual is always best. Instead, evaluate plan mix using conversion, retention, billing success, and revenue per user.</li>
</ul>
<aside class="tip"><strong>Experiment inspired by Ajay’s session</strong><p>Are your most active users also your most profitable? Segment subscribers by usage, revenue, and estimated cost to serve. Where usage and costs vary significantly, test an additional plan tier or one-off top-up, measuring contribution margin, conversion, and retention.</p>
<p>Want to dig into the numbers first? <a href="https://www.revenuecat.com/blog/growth/ai-feature-cost-subscription-app-margins/">Read the hidden cost of AI features</a>.</p></aside>
<p><a href="https://www.youtube.com/watch?v=SDM2wLBf2ks">Watch on YouTube</a></p>
<h2><strong>4. Ravi Mehta: the right onboarding is whatever onboarding builds user confidence</strong></h2>
<p>Our closing keynote came from <a href="https://www.revenuecat.com/blog/growth/ravi-mehta-tinder-sub-club-podcast-2025">Ravi Mehta</a>, former Chief Product Officer at Tinder.</p>
<p>Taking us on a journey from Boeing’s 1935 Model 299 bomber plane all the way through to Tinder’s two-minute onboarding, Ravi explained why one of onboarding’s most well-known ‘best practices’ isn’t always right.</p>
<blockquote><p>Most product teams ask, &quot;Should our product onboarding be shorter?&quot; In reality, the right question to ask is, &quot;What does a user need in order to feel confident at the moment of decision?&quot;</p><footer>Ravi Mehta - Former-CPO — Tinder</footer></blockquote>
<p>This is how Ravi matchmakes the perfect onboarding for each app:</p>
<ul>
<li><strong>Sometimes more steps convert better:</strong> when Ravi's client Sesame launched a weight loss program with Costco, its three-step checkout converted below target, and the users who did convert had so many questions that refunds were unsustainable. Replacing it with a 25-step intake (covering medical history, lifestyle, goals, and motivation) lifted conversion by 40%, and refunds went way down.</li>
<li><strong>Not all friction is created equal:</strong> good friction — like asking medical questions in a health app’s onboarding — builds confidence. Bad friction drains it, like asking for a password before you've given users any value, or asking a question they can't easily answer. (Sesame’s &quot;When was your last physical?&quot; became &quot;Have you had a physical in the last six months?&quot;)</li>
<li><strong>Pick a lane:</strong> the worst onboarding sits in the &quot;messy middle&quot;, usually the result of compromise: too long and too short at the same time. The best onboarding is opinionated, either doing the work to build confidence or cutting everything that isn't essential.</li>
<li><strong>Engineer confidence: </strong>give users the information they need, at the pace they need it, to help them make a confident decision. The right onboarding depends on both the stakes and complexity of your app. For example, a glucose monitoring app is high stakes and high complexity, a meeting reminder app may be high stakes but low complexity, and a crossword game is low stakes and low complexity.</li>
</ul>
<aside class="tip"><strong>Experiment inspired by Ravi’s session</strong><p>Plot your app on the stakes/complexity matrix, then audit every onboarding step (including auth screens) for whether it builds confidence or creates doubt. Rewrite or remove one major doubt-creator and A/B test the change, measuring trial conversion alongside refunds or early cancellations.</p></aside>
<p><a href="https://www.youtube.com/watch?v=WPVINNbHGMU">Watch on YouTube</a></p>
<h2><strong>5. The Big App Roast: paywalls and lifecycle messaging are more connected than you realize</strong></h2>
<p>Lunch for our virtual audience came with a side of paywall critique, as CRO Designer Julie Buzhynskaya and CRM Strategist Luigi Forte put five pairs of popular apps head to head, including Calm vs. Headspace, and FaceApp vs. Facetune. Julie dissected the paywalls, followed by Luigi reviewing everything after that: permission prompts, push notifications, emails, and in-app messages.</p>
<p>Four lessons came up repeatedly:</p>
<ul>
<li><strong>Show value before asking for payment:</strong> both Cal AI and FaceApp use short demonstrations or before-and-after visuals to show what the product actually does. Test whether a concrete demonstration helps users understand the offer better than generic creative.</li>
<li><strong>Make the offer and trial easy to understand:</strong> multiple apps lacked clarity around trial and offer terms. Explain what users receive, when billing starts, and how to cancel.</li>
<li><strong>Use lifecycle messaging to reinforce activation:</strong> ask for notification permissions in context and explain their value. Useful reminders should help users experience the product, with timing and frequency tested against engagement and opt-outs.</li>
<li><strong>Keep communicating with trial cancellers (where appropriate): </strong>cancellation during a trial does not necessarily mean the user has lost interest. Relevant, respectful follow-up can help people discover value before access ends.</li>
</ul>
<aside class="tip"><strong>Experiment inspired by the app paywall roast</strong><p>Review how understandable your trial is, purely from what’s shown on the paywall. Then, test a paywall that explains your free trial in its simplest terms: what users get, when they will be charged, and how to cancel. Measure trial starts, trial-to-paid conversion, and early cancellations vs. your current paywall.</p></aside>
<p><a href="https://www.youtube.com/watch?v=Kcm9SLXVGB0">Watch on YouTube</a></p>
<h2><strong>Other highlights from App Growth Annual</strong></h2>
<p>It wasn’t just the main stage sessions that got people talking at App Growth Annual. With 36 interactive workshops, a game show, and a surprising after-party guest, here are some moments you may have missed.</p>
<h3><strong>Hands-on workshops</strong></h3>
<p>Throughout the day, three blocks of time were reserved for intimate, hands-on workshops. In-person attendees had 30 to choose from, with content exclusive to those in the room. Online, attendees got to select from live workshops with Lisa Kennelly (Klarna), Marcus Burke (Ctrl Shift), Bria Sullivan (Honey B Games), Sven Jürgens, Julien Daver (SplitMetrics), and Yuliya Lennox (Genesis Tech).</p>
<p>Each virtual workshop is available to watch now, complete with the presenter’s full slide deck. <a href="https://www.revenuecat.com/blog/company/app-growth-annual-2026-workshops" target="_blank" rel="noopener noreferrer">Check out our recap on the workshop content here to see what you missed.</a></p>
<h3><strong>RevenueCat product keynote</strong></h3>
<p>Our annual product update saw RevenueCat CEO &amp; Co-founder, <a href="https://www.linkedin.com/in/jeiting/">Jacob Eiting</a>, share news about Early Payouts, ad monetization, and everyone’s favorite in-dashboard AI advisor, Rico. We also got a glimpse into Targeting by Checkpoint, a new way to personalize your funnel to each user by matching the right experience (like a specific offer or paywall variant) to each moment on their journey.</p>
<p><a href="https://www.revenuecat.com/blog/company/everything-we-announced-at-app-growth-annual-2026">Read the full rundown of every product announcement from App Growth Annual 2026.</a></p>
<p>From RevenueCat data land, Jacob also highlighted these stats:</p>
<ul>
<li><strong>More apps are shipping than ever:</strong> 107,000 apps have gone live on RevenueCat so far this year, up from 41,000 in all of 2025, and all-time annual revenue tracked across RevenueCat now sits at $17 billion+</li>
<li><strong>Spending isn’t keeping pace: </strong>per Appfigures, app launches are up 85% year over year, while consumer spending grew just 15%, so more apps are competing for roughly the same growth</li>
<li><strong>Newer apps have begun to make a dent:</strong> as a cohort, apps that launched on RevenueCat in 2023 and after grew 124%, while apps launched before 2023 declined -2%</li>
</ul>
<p>The takeaway? When anyone can build your features in days, software alone isn’t a moat anymore. Your edge has to come from network effects, brand, content, and what you are uniquely placed to understand about your customers.</p>
<blockquote><p>It's never been easier to make software, but it's never been harder to make that software a successful business.</p><footer>Jacob Eiting - CEO &amp; Co-founder — RevenueCat</footer></blockquote>
<p><a href="https://www.youtube.com/watch?v=TxgC5dj3Ijc">Watch on YouTube</a></p>
<h3><strong>Who Wants to Be a MillionARR?</strong></h3>
<p>Last year, we turned building paywalls into an e-sport. This year, we turned the <a href="https://www.revenuecat.com/state-of-subscription-apps">State of Subscription Apps</a> into a game show. Next year, who knows what we’ll do. <em>There is no stopping us.</em></p>
<p><em>Who Wants to Be a MillionARR?</em> kicked off with a qualifying round open to the whole room: 10 timed multiple-choice questions, answered from attendees' phones (and won by whoever answered fastest). The top three — Hamad, Daphne, and Adrian — then took to the stage for a Jeopardy-style final, complete with real buzzers and categories covering the app market, paywalls, retention, and AI apps.</p>
<p>The prize? Not quite a million dollars, but still very cool: $500 and a massive, wrestling-style championship belt, with $300 and $200 for second and third place.</p>
<p>After a rocky start that saw her drop to -800 points, <a href="https://www.revenuecat.com/blog/author/daphne-tideman">Daphne Tideman</a> staged a comeback (or as she called it, a <em>winback</em>), answering question after question to take the crown.</p>
<p>Five consecutive correct answers later, she was the proud owner of the <em>Who Wants to Be a MillionARR </em>belt. Proof that it pays to be persistent (and to read the RevenueCat blog), even in the face of challenging odds.</p>
<p>Think you'd have made the final? Try these questions from the day, then scroll down for the answers.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/8f550867eee99039d82e4e52e59827e0eebb1254-3200x2400.png" alt=""/></figure>
<p><strong>Answers:</strong></p>
<p>1. 55%</p>
<p>2. 4.6%</p>
<p>3. 14,700</p>
<p>4. 17–32 days</p>
<p>5. “Continue”</p>
<p>6. Progress bars</p>
<p>7. Asia-Pacific (APAC)</p>
<p>8. 41%</p>
<aside class="tip"><strong>Hint</strong><p>All the answers come from our <a href="https://www.revenuecat.com/state-of-subscription-apps">State of Subscription Apps report</a>, so you know where to study up for next year.</p></aside>
<p><a href="https://www.youtube.com/watch?v=OQgP0O9c4FQ">Watch on YouTube</a></p>
<h3><strong>After-party with Marc Rebillet</strong></h3>
<p>Every year, the RevenueCat team brainstorms who should perform at the App Growth Annual after-party. And every year, it’s the most unexpected option on the shortlist that makes the cut.</p>
<p>This year, Marc Rebillet took The Glasshouse by storm, delivering his signature improvised electronic tunes, complete with an app monetization-themed track. There was dancing, drinks, dim sum. And (of course), custom RevenueCat silk robes for every attendee. So everyone got to party in true Marc Rebillet style.</p>
<h2><strong>We'll see you next year</strong></h2>
<p>That’s it for your whirlwind tour of App Growth Annual 2026. Five keynotes, 36 workshops, one championship belt, and more cat-themed swag than even we expected — but you can only fit so much into a blog post.</p>
<p>If you want to relive the day for yourself, here’s everything you need:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=QBLy_nX-6Us">Watch the highlight reel</a></li>
<li><a href="https://www.youtube.com/playlist?list=PLZypamu0Cy0g">Replay all the main stage sessions on YouTube</a></li>
<li><a href="https://www.revenuecat.com/blog/company/app-growth-annual-2026-workshops">Catch up on the virtual workshops</a></li>
<li><a href="https://www.revenuecat.com/blog/company/everything-we-announced-at-app-growth-annual-2026">Read all the product news we announced</a></li>
<li><a href="https://www.youtube.com/watch?v=_YnW-H6Xfjw">Hear Thomas Petit and Sylvain Gauchet’s top insights from App Growth Annual</a></li>
<li><a href="https://atepamedia.pic-time.com/client/revenuecat-app-growth-annual/gallery?inviteToken=AAAAAI0AAAAcy4GeH72R26JgV4N8ohvm2H8E&amp;s=%7B%22blockId%22:%22%22,%22itemId%22:%22%22%7D">Browse the official photo gallery in search of your candids</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[6 App Growth Annual workshops you can watch for free (+ presenter slides) ]]></title>
      <link>https://www.revenuecat.com/blog/company/app-growth-annual-2026-workshops</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/app-growth-annual-2026-workshops</guid>
      <pubDate>Mon, 02 Nov 2026 13:25:11 GMT</pubDate>
      <dc:creator><![CDATA[Lorelei Whitman]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Out of the vault: hands-on sessions on AI ad creative, customer language, and building apps people will talk about]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/3d714f6342db9f2f28384f8f5dfcf756715f8562-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>At App Growth Annual 2026, over 5,000 people signed up to watch the day online and take part in six virtual workshops. Now, we’re opening the virtual vault: all six of these previously-exclusive workshops are now free to watch, complete with the presenter’s full slide deck, handouts, and our curated further reading to help put the workshop’s lessons into practice.</p>
<aside class="tip"><strong>Missed the rest of the day?</strong><p>Catch up on the keynotes from Babbel, Burner, Tolan, and Ravi Mehta (ex-Klarna) — plus five experiments they inspired — <a href="https://www.revenuecat.com/blog/company/app-growth-annual-2026">in our full App Growth Annual 2026 recap</a>.</p></aside>
<h2><strong>Lisa Kennelly (Klarna) — Copy that converts: the language-market fit teardown</strong></h2>
<p>AI-written onboarding and paywall copy often reads fine, but it fails at conversion. Because it doesn't use your customers' language. Lisa Kennelly (Growth and Product Market Strategist, Klarna) gave a great workshop on how to write copy with language-market fit: <strong>copy that sounds the way customers already speak.</strong></p>
<p>Lisa walked through her process for mining App Store reviews, competitor reviews, cancellation reasons, support tickets, Reddit thread, and interviews, where she looks for five things:</p>
<ol>
<li>The outcome users want</li>
<li>The trigger that made them download</li>
<li>The alternatives they tried (including doing nothing)</li>
<li>Their anxieties</li>
<li>The exact words they use</li>
</ol>
<p>Everything goes verbatim into a single ‘language bank’ that every draft starts from. Before shipping, every line faces a five-point check — starting with whether it uses a phrase customers actually repeat.</p>
<p><a href="https://www.youtube.com/watch?v=H7yLaK9Sez8">Watch on YouTube</a></p>
<p><strong>Resources:</strong></p>
<p><a href="https://www.revenuecat.com/documents/app-growth-annual-lisa-kennelly-workshop
">Download Lisa's workshop slides </a>
Download Lisa’s handouts: <a href="https://www.revenuecat.com/documents/lisa-prompt-pack ">prompt pack</a> + <a href="https://www.revenuecat.com/documents/lisa-language-bank-template">language bank template</a></p>
<p><strong>Further reading:</strong></p>
<ul>
<li><strong>📕 </strong><a href="https://www.revenuecat.com/blog/growth/review-mining-for-subscription-apps/">From review to roadmap: how mining reviews can inform your app's features</a> by Daphne Tideman</li>
<li><strong>🎧 </strong>Sub Club podcast: <a href="https://www.revenuecat.com/blog/scale-your-app-marketing-team-from-scratch">How to Scale Your App Marketing Team from Scratch</a> ft. Lisa Kennelly</li>
<li><strong>📗 </strong><a href="https://www.revenuecat.com/blog/growth/paywalls-study-guide/">Start here: a paywalls study guide</a></li>
</ul>
<h2><strong>Bria Sullivan (Honey B Games) — The playbook behind 23 million organic downloads</strong></h2>
<p>Bria Sullivan's game Boba Story has 18 million organic downloads, and Focus Friend — the gamified focus timer built with Hank Green — passed 5 million, most of them arriving long after Hank stopped posting about it. Her secret is a <strong>simple, two-pronged repeatable research funnel</strong>:</p>
<ul>
<li>Short surveys to find people who already use apps to solve your problem</li>
<li>Customer interviews to understand them</li>
<li>Disciplined pattern-finding in the feedback</li>
</ul>
<p>Surveys ask <em>what</em>; interviews ask <em>why</em> and <em>how</em>, starting with the user's relationship to the problem before you show them anything. Bria’s workshop walks through her research process, including how she tests her ideas, the questions she asks, and how many interviews to aim for.</p>
<p><a href="https://www.youtube.com/watch?v=O36y6ePr0ds">Watch on YouTube</a></p>
<p><strong>Resources:</strong></p>
<p><a href="https://www.revenuecat.com/documents/app-growth-annual-bria-sullivan-workshop">Download Bria's workshop slides</a></p>
<p><strong>Further reading:</strong></p>
<ul>
<li><strong>🎧 </strong>Launched podcast: <a href="https://www.revenuecat.com/blog/growth/bria-sullivan-focus-friend-launched-podcast-2026">The Hail Mary pitch that launched a #1 App Store hit</a> ft. Bria Sullivan</li>
<li><strong>📕 </strong><a href="https://www.revenuecat.com/blog/growth/informed-empathy-user-interviews-ad-creatives/">How user interviews can inform your ad creatives</a> by Nathan Hudson</li>
<li>📗 <a href="https://www.revenuecat.com/blog/growth/solve-app-problems-emotionally/">Don't just solve a problem — solve it emotionally</a> by Daphne Tideman</li>
</ul>
<h2><strong>Sven Jürgens — From competitor ad to finished creative, in one hour</strong></h2>
<p>Your competitors have already paid to learn what works in your category — and thanks to the impression filter in Meta's Ad Library, their best performers are public. At App Growth Annual, growth consultant Sven Jürgens shared how he uses these competitor ads to inform his own creative, with AI-generated video fast-tracking the process.</p>
<p>Sven’s session shows his nine-step AI workflow in real time, with the audience voting on the app and actor. About 10 minutes and roughly $3 later, out came a lip-synced UGC-style Cal AI ad. With the heavy lifting done by AI, Sven spent the rest of the workshop on what actually makes a good output:</p>
<ul>
<li>Research into your users' pains and awareness stage</li>
<li>Rich context (brand guidelines, user insights, real app footage) rather than clever prompts</li>
<li>Ads built from swappable ‘Lego blocks’ (hook, body, call to action) so winners are fast to iterate</li>
</ul>
<p>The full workshop covers Sven’s AI ad process, his golden rule for reporting, copywriting guidelines, brand examples, and how to structure your documentation.</p>
<p><a href="https://www.youtube.com/watch?v=ACZgBZU2K6w">Watch on YouTube</a></p>
<p><strong>Resources:</strong></p>
<p><a href="https://www.revenuecat.com/documents/app-growth-annual-sven-jurgens-workshop">Download Sven's workshop slides</a>
<a href="https://www.revenuecat.com/documents/sven-workflow-ai-creative ">Download Sven’s nine-step diagram</a></p>
<p><strong>Further reading:</strong></p>
<ul>
<li><strong>📕 </strong><a href="https://www.revenuecat.com/blog/growth/ad-generated-ads-ugc/">AI-generated ads: balancing attention and trust in user-generated content</a><strong> </strong>by Anthony Morcillo</li>
<li><strong>🎧</strong> Sub Club podcast: <a href="https://www.youtube.com/watch?v=Woz2m8b8wno">How to Go Viral on TikTok (and Profit From It)</a> ft. Joseph Choi</li>
<li>📗 <a href="https://www.revenuecat.com/blog/growth/subscription-app-creative-testing">Forget chasing viral ads — build a creative testing system that scales</a> by David Vargas</li>
</ul>
<h2><strong>Yuliya Lennox (Genesis Tech) — Nothing works until this does: the app marketing sequence</strong></h2>
<p>Most apps don't stall because of bad tactics; they stall because they skipped a step. In her workshop, growth marketer Yuliya Lennox (BetterMe, Replika, Palta) presents app marketing as four stages, each one a prerequisite for the next:</p>
<ol>
<li>Prove there's demand</li>
<li>Build a journey that converts to paid</li>
<li>Make the economics work</li>
<li>Scale</li>
</ol>
<p>Her advice for stage one: charge from day one (free-user retention tells you nothing about willingness to pay), test demand with a simple funnel before building the full product, and test genuinely different creative <em>concepts</em> rather than 20 variations of one slogan. Once something works, she pushes teams to count every cost — store fees, payment fees, AI infrastructure, refunds — and aim for payback as close to day one as possible.</p>
<p><a href="https://www.youtube.com/watch?v=V1wGXnZzvec">Watch on YouTube</a></p>
<p><strong>Resources:
</strong><a href="https://www.revenuecat.com/documents/app-growth-annual-yuliya-lennox-workshop">Download Yuliya's workshop slides</a></p>
<p><strong>Further reading:</strong></p>
<ul>
<li><strong>📕 </strong><a href="https://www.revenuecat.com/blog/growth/product-market-fit-subscription-apps/">Finding product-market fit: a guide for subscription apps</a> by Daphne Tideman</li>
<li><strong>🎧</strong> Sub Club podcast: <a href="https://www.youtube.com/watch?v=jzlPf100vy4">Make Ugly Ads to Grow Your App</a> ft. Yuliya Lennox</li>
<li>📗 <a href="https://www.revenuecat.com/blog/growth/fix-onboarding-funnels/">Stop chasing growth hacks — fix your funnel first</a> by Hannah Parvaz</li>
</ul>
<h2><strong>Marcus Burke (Ctrl Shift) — 6+1 AI workflows to run and scale Meta ads from your phone</strong></h2>
<p>Checking whether one Meta ad is profitable can mean hopping between Ads Manager, Events Manager, your MMP, RevenueCat, product analytics, and a spreadsheet. Meta ads advisor Marcus Burke now has AI do the tab-hopping for him.</p>
<p>His setup is deliberately simple: Notion as a central, model-agnostic ‘brain’ holding his SOPs, client context, and a change log, with Claude connected to it, and to his data tools via MCPs. On top sit the workflows he runs across client accounts, including a regular ‘account patrol’, age and placement breakdowns that predict trial-to-paid before the data matures, and value rules. Others cover format research across ad libraries and TikTok, voice-of-customer copywriting, and an ad–onboarding fit check that flags when a scaling ad promises something your funnel never pays off.</p>
<p>The new workflows have cut Marcus’s time dramatically, from two hours to 10 minutes in some places. Check out his full workshop for a detailed walkthrough of his AI setup and each workflow.</p>
<p><a href="https://www.youtube.com/watch?v=YnttM5vybwM">Watch on YouTube</a></p>
<p><strong>Resources:</strong></p>
<p><a href="https://www.revenuecat.com/documents/app-growth-annual-marcus-burke-workshop">Download Marcus's workshop slides</a>
<a href="https://app.notion.com/p/Meta-Ads-Account-X-Ray-3fe9167646b9810ba6bfe2111816be52?source=copy_link">Meta Ads account x-ray</a></p>
<p><strong>Further reading:</strong></p>
<ul>
<li><strong>🎧 </strong>Sub Club podcast: <a href="https://www.youtube.com/watch?v=Pb5TuG77O7M">Selling Your Subscription App with Meta Ads</a> ft. Marcus Burke</li>
<li><strong>📕 </strong><a href="https://www.revenuecat.com/blog/growth/7-meta-ad-testing-frameworks-for-subscription-apps">7 Meta ad testing frameworks for subscription apps</a> by Daphne Tideman</li>
<li>📺 Sub Club Live: <a href="https://www.youtube.com/watch?v=pvi185dSiMc">Meta Ads for Apps: Creative, Signal &amp; Scaling in 2026</a> ft. Marcus Burke</li>
</ul>
<h2><strong>Julien Daver (SplitMetrics) — Make AI-generated ads that actually convert</strong></h2>
<p>In our last workshop, Julien Daver (Head of UA at SplitMetrics) shares the AI creative framework his entire team uses to find winning ad creatives.</p>
<p>It starts with a research agent that continuously collects your own data (brand material, audience insights, onboarding answers, campaign results) alongside external sources like competitor ads, Reddit, app reviews, and YouTube comments. A second agent turns that into concepts, angles, and hooks, kept separate so you can trace what actually moved performance — helped by naming every ad after its hook, angle, and concept.</p>
<p>Julien generates video, voiceover, and music as separate components, so fixing one element doesn't mean paying to regenerate the whole ad. Finally, results get fed back into the research agent to close the loop. Watch Julien’s workshop now to see the sleek workflow in action.</p>
<p><a href="https://www.youtube.com/watch?v=oxNHkifsFA0">Watch on YouTube</a></p>
<p><strong>Resources:</strong></p>
<p><a href="https://www.revenuecat.com/documents/app-growth-annual-julien-daver-workshop">Download Julien's workshop slides</a></p>
<p><strong>Further reading:</strong></p>
<ul>
<li><strong>📕 </strong><a href="https://www.revenuecat.com/blog/growth/creative-fatigue-mobile-apps-roas">Why creative fatigue is killing your ROAS</a> by David Vargas</li>
<li>📗 <a href="https://www.revenuecat.com/blog/growth/meta-ads-cac-creative-testing">The creative testing system that slashed our CAC (and scaled our spend)</a> by Nathan Hudson</li>
<li><strong>🎧 </strong>Sub Club podcast: <a href="https://www.youtube.com/watch?v=UYIgu02h8cs">The AI Growth Playbook for Subscription Apps</a> ft. Phil Carter</li>
</ul>
<h2><strong>Keep the momentum going</strong></h2>
<p>Six workshops, one common thread: AI can now do the heavy lifting on research, copy, creative, and reporting — but only when you feed it real context about your users and keep a human checking the output. Humans are uniquely placed to understand their users, and that’s where AI is most at risk of failing.</p>
<p>Pick the session closest to your biggest bottleneck, grab the slides, and try one idea this week.</p>
<p>Still hungry for more? <a href="https://www.youtube.com/playlist?list=PLZypamu0Cy0g">Watch every App Growth Annual 2026 main stage session on YouTube</a>, or <a href="https://www.revenuecat.com/blog/company/app-growth-annual-2026">read our ultimate recap blog</a>. We'll see you at App Growth Annual 2027?</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Everything we announced at App Growth Annual 2026]]></title>
      <link>https://www.revenuecat.com/blog/company/everything-we-announced-at-app-growth-annual-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/everything-we-announced-at-app-growth-annual-2026</guid>
      <pubDate>Thu, 29 Oct 2026 15:32:30 GMT</pubDate>
      <dc:creator><![CDATA[Courtney Touchstone]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[AI-powered paywall creation, real-time experimentation, ad monetization, and a first look at what’s next for RevenueCat.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/ae6d3e5d3471b9b3be222446f9d925d03ca75b1d-2400x1260.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Building an app has gotten faster. Turning it into a successful business still takes work: understanding your customers, finding the right offer, testing what converts, and giving people a reason to stay.</p>
<p>At App Growth Annual last week, we announced new tools to help with each of those jobs. Some are available today, others are entering public beta, and a few offer a preview of where RevenueCat is going next.</p>
<p>Together, they bring more of the work of growing an app into RevenueCat, from building your first paywall to understanding revenue across subscriptions and ads.</p>
<h2><strong>Available now</strong></h2>
<h3><strong>Build and refine paywalls with AI</strong></h3>
<p>Describe the paywall you want, and the <a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls#paywalls-ai-editor">Paywalls AI Editor </a>will build it. You can also start with a screenshot, ask for changes, and keep refining the design through conversation.</p>
<p>Underneath the design, you still have RevenueCat’s paywall infrastructure connecting your products, purchases, and data. That means less time between having an idea for a paywall and having something ready to test.</p>
<h3><strong>Take customers from the web into your app with Funnels</strong></h3>
<p><strong><a href="https://www.revenuecat.com/feature/funnels">RevenueCat Funnels</a></strong> lets you build and launch web-to-app funnels using the same editing engine as RevenueCat Paywalls.</p>
<p>Build your web experience, connect checkout, and help customers move from their purchase on the web into your app. RevenueCat handles the connections that make that transition work, so you have fewer pieces to assemble yourself.</p>
<p>Funnels has no additional platform fee. Funnel revenue counts toward your monthly tracked revenue and is subject to RevenueCat’s standard pricing, plus applicable payment processing fees.</p>
<h3><strong>See Experiment results in real time</strong></h3>
<p><strong><a href="https://www.revenuecat.com/feature/experiments">Experiments</a></strong> results now update in real time, so you can follow a test without waiting for the next daily refresh.</p>
<p>Results also include <strong>predicted 12-month LTV</strong>, helping you compare variants beyond their initial conversion rates. A variant that converts more customers today may have a different long-term outlook, and those predictions give you more information to work with when deciding what to ship.</p>
<p>Once you’ve made that decision, you can roll out the winning variant in one click.</p>
<h3><strong>Bring RevenueCat into your AI tools</strong></h3>
<p>The<strong> </strong><strong><a href="https://www.revenuecat.com/docs/tools/ai-toolkit">AI Toolkit</a></strong><strong> </strong>gives your AI assistant the RevenueCat context and tools to help you set up monetization, ship paywalls, troubleshoot purchases, and understand performance.</p>
<p>It includes skills, the RevenueCat MCP, and the RevenueCat CLI, which lets you or your agent run commands from the terminal. One plugin install sets up the skills and MCP server in Claude Code, Codex, Cursor, Gemini CLI, and VS Code.</p>
<p>Ask your agent to review your monetization setup, investigate trial conversion, or summarize experiment results, then keep working in the tools you already use.</p>
<h3>Expand web checkout with RevenueCat Web</h3>
<p><strong><a href="https://www.revenuecat.com/feature/web">RevenueCat Web</a></strong> lets customers complete a purchase on the web and access what they’ve bought in your app. You can sell through <strong><a href="https://www.revenuecat.com/billing">RevenueCat Billing</a></strong><strong>,</strong> Stripe Billing, or Paddle Billing, and use the same paywalls, funnels, and customer data across web and mobile.</p>
<p>RevenueCat Billing also supports app-to-web purchases, letting customers start in your app, complete checkout on the web, and return with access to their purchase.</p>
<p>In the EU, Apple’s new terms let you offer your own checkout inside your app at 20% commission. In-app checkout works with Stripe Billing and Paddle Billing today, with RevenueCat Billing to follow. RevenueCat handles the transaction reporting Apple requires, so you don’t have to build your own reporting integration.</p>
<h3><strong>Understand customer groups with Audiences</strong></h3>
<p><strong><a href="https://www.revenuecat.com/docs/dashboard-and-metrics/audiences">Audiences</a></strong><strong> </strong>lets you group customers by shared characteristics and understand how different groups use and pay for your app.</p>
<p>Create an Audience using subscription status, customer attributes, or acquisition source. Membership updates in real time as customers’ information changes, keeping those groups current.</p>
<p>You can use Audiences to filter charts, explore customer segments, and export groups for further analysis. They’re also a foundation for the new targeting and ad monetization capabilities currently in beta.</p>
<h3><strong>Access your proceeds sooner with Early Payouts</strong></h3>
<p>The time between earning revenue and the app store paying you can make it harder to fund acquisition, cover expenses, or invest in your next release.</p>
<p><strong><a href="https://www.revenuecat.com/early-payouts">Early Payouts</a></strong> lets qualifying apps advance up to 80% of app store proceeds at a 2.5% fee. The remaining 20% arrives on your normal payday, with no fee on that remaining payout. Fees only apply to the amount you advance.</p>
<p>You can choose automatic advances or draw funds when you need them, with no minimum usage requirement and the ability to pause.</p>
<p>At RAGA, we also announced plans to expand to 13 additional countries, including the UK, France, and Canada.</p>
<p><strong><a href="https://www.revenuecat.com/early-payouts#form">See if you qualify &gt;</a></strong></p>
<p><em>$100 minimum transaction amount applies for on demand payouts. RevenueCat is not a bank. Banking services are provided by Core Bank, Member FDIC. Deposits are FDIC insured up to $250,000 per depositor, per insured bank, subject to limitations.</em></p>
<h2><strong>Now in public beta</strong></h2>
<h3><strong>Meet Rico, your agent inside RevenueCat</strong></h3>
<p><strong><a href="https://www.revenuecat.com/feature/ai-agent">Rico</a></strong> is RevenueCat’s new in-dashboard AI agent. Ask Rico a question like ‘How much revenue did we make in Germany last month?’ Then, ask follow-up questions to dig into the numbers.</p>
<p>Rico can also take action. Ask it to set up an Experiment or build a Paywall, so you can move from a question to trying an idea without navigating every step yourself.</p>
<p>The AI Toolkit brings RevenueCat into your existing tools. Rico gives you a way to work conversationally while you’re already in the dashboard.</p>
<h3><strong>See ad revenue alongside purchases</strong></h3>
<p>Subscriptions and in-app purchases may only tell part of your app’s revenue story. <strong><a href="https://www.revenuecat.com/docs/ad-monetization">Ad Monetization</a></strong> brings in-app ad revenue into RevenueCat so you can see customer lifetime value across hybrid monetization.</p>
<p>That gives you a fuller view of what customers earn for your business, including those who use your app without ever making a purchase.</p>
<p>Ad activity can also inform your Audiences and Experiments, connecting what you learn about ad engagement with how you segment customers and test monetization experiences.</p>
<h3><strong>Reward ad-views through the AdMob integration</strong></h3>
<p><strong><a href="https://www.revenuecat.com/docs/ad-monetization/rewards">Rewarded Ads</a></strong> lets you give customers in-app currency or temporary access to paid features in exchange for watching an ad. Through the AdMob integration, RevenueCat verifies that a customer earned the reward and delivers it. For example, a customer could watch an ad to earn coins or unlock a premium feature for 30 minutes, with RevenueCat managing those rewards alongside purchases and subscription access.</p>
<h2><strong>Coming next</strong></h2>
<h3><strong>Match each moment to the right experience with Targeting by Checkpoint</strong></h3>
<p><strong>Targeting by Checkpoint</strong> is coming soon. Choose what to show customers based on where they are in your app and which Audience they belong to. Then, run Experiments to learn what works best. A checkpoint is a location or moment you add to your app, such as finishing onboarding or trying to access a premium feature. Once it’s implemented, you can configure rules in the RevenueCat dashboard to control what customers see there. For example, a run-training app could show a marathon-focused paywall to customers who selected marathon training as their goal during onboarding. At the same checkpoint, customers who arrived through a particular ad campaign could see a paywall that matches the campaign’s message.</p>
<p>You’ll be able to run Experiments directly within those targeting rules. Test different paywalls or multi-step flows for a specific Audience at a specific checkpoint, with the rules and variants managed together.</p>
<p>Targeting by Checkpoint also allows you to reuse a single Offering across multiple paywalls, so you don’t have to duplicate it every time you want to try a new paywall design.</p>
<p>Support for additional experiences, including ads and offers, is planned to give you more ways to tailor monetization to each customer and moment.</p>
<p>Interested in joining the upcoming private beta of Targeting by Checkpoint?<strong> </strong><strong><a href="https://revenuecateventfeedback.typeform.com/to/syjOsyn9">Fill out this form</a></strong> and we’ll reach out if you’re a good fit for early testing. </p>
<h2><strong>More time to work on what makes your app worth paying for</strong></h2>
<p>Build a paywall from an idea. Tailor it to your customers. Test it, see what’s working, and roll out the winner. These releases bring those steps closer together, so you can spend less time connecting tools and more time trying things that could grow your business.</p>
<p>Start building with the features available today, try Rico and our ad capabilities in public beta, and stay tuned for Targeting by Checkpoint. We’re making RevenueCat a place where you can put more of your ideas to work, learn from them faster, and keep improving what comes next.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Praktika's CEO didn't like the creator video that took it from $1M to $12M ARR in a month]]></title>
      <link>https://www.revenuecat.com/blog/growth/adam-turaev-praktika-sub-club-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/adam-turaev-praktika-sub-club-podcast-2026</guid>
      <pubDate>Wed, 28 Oct 2026 13:45:51 GMT</pubDate>
      <dc:creator><![CDATA[David Barnard]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Adam Turaev’s rule since day one: hire influencers for their reach, then don’t tell them how to do their job.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/178368964a668bf9feaad8f742f1e096f4922cb7-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Praktika had spent ten months on creator outreach before it found the one that mattered. &quot;I had spent probably like ten months doing this. It was a full time job,&quot; says Adam Turaev, Praktika's co-founder and CEO. &quot;I just kept sending those emails, messages to all these influencers and just a few of them just worked out.&quot; One of the few was a Brazilian creator named Gustavo, who sent over a video for approval. &quot;I was like, I don't like this video, but, you know, do whatever, like, you can post it.&quot;</p>
<p><a href="https://www.youtube.com/watch?v=g6RoPxoCqLA">Watch on YouTube</a></p>
<p>He posted it. By Adam's count it did &quot;like 10 million views in Instagram, like another 10 million in TikTok, another 10 million in YouTube,&quot; and that was the month Praktika went from about $1M ARR to $12M. The video wasn't about the app at all. It showed a coach correcting a learner's mistakes mid-conversation, a format Adam says is now &quot;the main structure of the video&quot; for language-learning apps generally. His instinct had been to ask Gustavo to showcase the product instead. Gustavo's answer, as Adam recalls it, was &quot;believe me.&quot;</p>
<p>Paid spend had little to do with it. &quot;We put some budget behind it, but it was like, you know, a tiny budget,&quot; Adam says, with first-day costs per trial &quot;literally close to zero.&quot; What turned one viral post into a $12M month was replication: &quot;we started just replicating those videos with other creators in other countries as well.&quot; Gustavo was hired immediately and still runs Praktika's social media.</p>
<p>The rule behind all of this predates the video. &quot;That was always my, like, rule since the beginning. Like, you know, do not tell influencers to do, like, how to do their job,&quot; Adam says. &quot;If you're hiring them because they're like getting an amazing rich, right. So let them do their job.&quot; Jacob Eiting's version, from the other side of the table, is that a founder who forces everything through their own approval filter &quot;gonna miss things that are just like, you're blind.&quot;</p>
<h2>Under 5,000 followers, tens of millions of views</h2>
<p>Gustavo came out of a filter, not a shortlist. Adam had no budget for established influencers in Brazil, so he went to a creator discovery platform and set two constraints: a following under 5,000, and views far above what that following should produce. &quot;If you have big following then yeah, you're charging a lot. So I limited this to like less than 5000 followers. But at the same time I filtered out those who are getting like a lot of use these days.&quot; The creator it surfaced was doing tens of millions of views on educational videos, had no collaboration email on his profile, and &quot;didn't even realise what he's into.&quot; Adam DMed him on TikTok.</p>
<p>Jacob reads it as buying an underpriced asset. Adam pushes back on the word. &quot;Not even about underpriced, but it's more that you're approaching someone on their pick even before they're picking.&quot; The point is timing, not price. And he says the filter still works beyond one lucky find: &quot;it can be used as a framework,&quot; not targeting micro-influencers as such but &quot;filtering those out who are, uh, hitting like a lot of use with low followings.&quot;</p>
<h2>How the program runs at $50M ARR</h2>
<p>Praktika is now at $50M ARR, &quot;reaching almost like half a million paying subscribers,&quot; Adam says, with a team of &quot;a little bit more than 40 people,&quot; which puts ARR per employee above $1M. It is profitable. The creator program is past 100 influencers and is still being scaled country by country.</p>
<p>The structure is an influencer lead and country coaches, all of them former creators who were making Praktika videos before they were hired. The lead was making videos in Poland; &quot;she had done an amazing job. And then we hired her as an influencer lead. And she's now running the world programs.&quot; Each coach runs &quot;a roster of 20 or 30 influencers, and they just rotate various angles.&quot; Knowing what good content is, Adam says, is &quot;the key skill you need to have,&quot; because the management side is &quot;not that complicated especially now.&quot; The creators stay contractors. Deals are usually a fixed fee plus a bonus on views, but the coaches can restructure for talent: &quot;if there is a good talent just do whatever to to hire this person.&quot;</p>
<p>The decision that keeps it working is that paid and organic never share a roster. &quot;This is paid ads and organic content. This creator program that those two are separate streams. We don't mix them up,&quot; Adam says. Creators are left to find their own angles because the in-house team eventually runs dry on them, and &quot;every new influencer upcoming is a potential for this.&quot; When a video goes viral, it is handed to the paid team, which &quot;reproduce this video with like their own roster of like internal influencers or contractors.&quot;</p>
<h2>Praktika sold Brazil a job, not a language</h2>
<p>Brazil was chosen from a report on English-language penetration: one of the lowest-penetrated large markets, with an income multiplier for English that Adam calls &quot;insane.&quot; The warning that came with it was that Brazilians &quot;typically don't pay for consumer subscriptions.&quot; Praktika launched worldwide and pointed its distribution at Brazil anyway, because that is where the early adopters were.</p>
<p>Getting a $100-a-year subscription to sell there meant changing what was being sold. &quot;We didn't sell English to them. We sold them an opportunity to get a better job, so it was just a bit of a different angle,&quot; Adam says. Every claim in the go-to-market followed, including the &quot;50 x cheaper than a tutor&quot; line he says &quot;everybody uses now.&quot; David Barnard's generalization for anyone picking a problem: &quot;look for a problem where you're selling money.&quot;</p>
<h2>The avatars that worked in ads and failed in the app</h2>
<p>Adam's biggest fail of the past year was moving a growth idea into the product. Unhinged, cursing avatars &quot;were going viral in Upper Funnel,&quot; so the team added them to the app. It made more people unhappy than happy, and they didn't understand it. &quot;The image is fine. Like, you know, I want to talk to this avatar, but why is it cursing, right?&quot; The principle it clarified: &quot;we're not serving AI companions. We're serving AI coaches.&quot;</p>
<p>Praktika now draws the line explicitly. Product has clear, agreed boundaries on &quot;a one sheet of description.&quot; Growth has almost none. &quot;We let influencers to create and to come up with like pretty much any angle that they want,&quot; Adam says, and the difference between the two sides &quot;is pretty dramatic.&quot;</p>
<p>In <a href="https://www.youtube.com/watch?v=g6RoPxoCqLA" target="_blank" rel="noopener noreferrer">the full episode</a>, Adam also explains why Praktika's biggest win of the year was moving from Flutter to native iOS, why he builds for the best model quality and trusts inference costs to fall, and why fear, not skill, is the reason the app uses avatars at all.</p>
<h2><strong>Guest links</strong></h2>
<ul>
<li>Adam Turaev on LinkedIn: <a href="https://www.linkedin.com/in/adamturaev/">https://www.linkedin.com/in/adamturaev/</a></li>
<li>Adam Turaev on X: <a href="https://x.com/adampraktika">https://x.com/adampraktika</a></li>
<li>Praktika: <a href="https://praktika.ai/">https://praktika.ai/</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Do holiday offers bring subscription apps more than a sales spike? Data from 5K apps]]></title>
      <link>https://www.revenuecat.com/blog/growth/holiday-offers-subscription-apps</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/holiday-offers-subscription-apps</guid>
      <pubDate>Tue, 27 Oct 2026 08:14:54 GMT</pubDate>
      <dc:creator><![CDATA[Margarita Loktionova]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[We analyzed 5,000 subscription apps to see how new paying customers, six-month revenue, and paywall patterns changed across Black Friday, Christmas, and New Year.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/d1a2e86cfce5efda29fd2753cd5a3ebbf82d39c8-3200x1700.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>With the holidays around the corner, we all get the itch to plan seasonal offers and big discounts.</p>
<p>It’s hard not to when it feels like every competitor is doing the same.</p>
<p>For subscription apps, there’s more to consider than sales. Discount an annual plan, and you’re accepting less revenue for a year. Offer a cheaper first month, and some subscribers may leave when the price goes up.</p>
<p>Does that mean holiday offers are too risky? Not necessarily. But they do require planning.</p>
<p>We analyzed nearly 5,000 apps worldwide during the 2025–26 holiday season to help you decide when to run a promotion and how to assess its results.</p>
<p>The research compares changes in new paying customers around Black Friday, Christmas, and New Year for apps with and without detected promotions. It also looks at what those buyers spent over six months, their renewals and refunds, and how apps presented their holiday offers.</p>
<h2>Summary</h2>
<ul>
<li>Apps with detected Black Friday promotions get more new paying customers and higher six-month revenue than in their September 15–October 12 baseline. Revenue per customer is lower, but the median app makes up for it with a larger customer base.</li>
<li>Existing free users play a substantial role in Black Friday growth. Among apps with detected promotions, users who installed the app more than three months before their first payment account for about half the average increase in new paying customers.</li>
<li>Christmas-only promotions show no clear acquisition advantage. Apps with offers detected only at Christmas perform similarly to apps without detected offers.</li>
<li>New Year and January lead the holiday season for new paying customers, particularly in Health &amp; Fitness and Gaming. Apps with detected New Year promotions get 14% more new paying customers per day than in September–October, while those without detected promotions get 4% fewer.</li>
<li>Holiday paywalls put savings and urgency front and center. Discount claims, crossed-out prices, and limited-time messaging are common, and more paywalls show a single plan.</li>
</ul>
<h2>Holiday acquisition is lower overall, but apps with offers tend to perform better</h2>
<p>Over the holidays, most app categories get fewer new paying customers than in their September 15–October 12 baseline. Even New Year and January, the strongest stretch of the season, come in slightly below it for the median app (3% fewer new paying customers per day).</p>
<p>Part of that dip comes from the baseline itself. Mid-September to mid-October is a strong stretch for new paying customers, so even ordinary weeks right after it are about 6% below. The 2024–25 season shows the same pattern.</p>
<p>Ad costs may also play a part. <a href="https://www.biddyco.com/blog-posts/meta-ads-black-friday-cpm">One analysis</a> of large US Meta ad accounts found that CPMs for apps and services rose sharply around Black Friday, peaked in mid-December, and dropped below October levels in January. Higher ad prices can make new users more expensive to acquire in the run-up to Christmas.</p>
<p>Around New Year, new paying customers briefly rise above the September–October baseline. The peak doesn’t last, so the full period still comes out below it.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d7b9106c09aaf132754be7a2bae7f02fd4ba093e-3200x1800.png" alt="Daily new paying customers during the 2025–26 holiday season, mostly below the autumn baseline with a brief peak around New Year."/></figure>
<p>The two exceptions are Health &amp; Fitness and Gaming, which stay above baseline across New Year and January (11% and 10% more new paying customers per day).</p>
<p>That’s likely when demand peaks for both: New Year resolutions for fitness apps, and new devices and time off for games.</p>
<p>But overall, the typical app gets fewer new paying customers per day than during that earlier period.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/1b69ed2c000d99da4ee7b23590fae0dc2c8438d3-3200x2100.png" alt="Holiday acquisition by app category. Health &amp; Fitness and Gaming exceed the autumn baseline during New Year and January; other categories remain below it."/></figure>
<p>However, if we look at apps with detected promotions (421 at Black Friday, 208 at Christmas, and 236 at New Year), the picture changes.</p>
<p>During Black Friday and New Year, apps with detected promotions tend to perform better.</p>
<p>Black Friday shows the widest gap. During Black Friday week (November 24–December 1), the median app with a detected promotion gets 20% more new paying customers per day than in its baseline, while the median app without one gets 16% fewer. New Year and January follow the same pattern on a smaller scale: 14% more versus 4% fewer.</p>
<p>That said, Christmas shows a smaller gap.</p>
<p>Apps with detected Christmas promotions get 11% fewer new paying customers, while those without get 22% fewer. Most of that difference comes from apps that were also running promotions during Black Friday. Apps with promotions detected only at Christmas show no clear advantage.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/1f7504efcc07bf00a054c2a01ef4b1d3e351b61a-3200x1800.png" alt="New paying customers versus autumn baseline: Black Friday +20% with detected promotions versus −16% without; Christmas −11% versus −22%; New Year +14% versus −4%."/></figure>
<p><em>We compare apps where we detect a promotion with those where we don’t. Most signals come from purchases, so offers with few or no sales can go undetected. Apps flagged only by holiday wording on their paywall, the one signal that doesn’t need sales, were 2% below baseline duringBlack Friday. Apps that run promotions may also spend more on ads. The results describe these two groups, rather than predict how much a discount would increase your sales.</em></p>
<h2>More Black Friday buyers make up for lower revenue per buyer</h2>
<p>Among apps with detected promotions, the increase in new paying customers during Black Friday and Cyber Monday appears large enough to make up for lower revenue per buyer over six months.</p>
<p>During November 24–December 1, 2025, these apps get 20% more new paying customers per day than in September–October.</p>
<p>Most of the extra customers are acquired in the five days from Thanksgiving to Cyber Monday. New paying customers peak on Black Friday (November 28) at 49% above baseline and stay at least 25% above it through Cyber Monday (December 1).</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/7bb9f192dc22c2ac832484f4a0a3967c185a8f02-3200x1800.png" alt="Apps with detected promotions peak at 49% more new paying customers on Black Friday and 25% more on Cyber Monday, versus autumn baseline."/></figure>
<p>Each Black Friday buyer is worth a little less, but the extra buyers make up for it. Over the next six months, the median app earns 8% less revenue per buyer.</p>
<p>For the median app, buyers acquired on an average sale day generate 12% more revenue over six months than buyers acquired on an average baseline day. Overall, 60% of apps come out ahead.</p>
<p>That gain is limited to the sale week. Once the quieter weeks around it are included (November 10–December 14), the median app with a detected promotion is 1% below its baseline, versus 12% below for apps without one.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ced71e1191ecaf844d86a2d8a390f29a8339471c-3200x1800.png" alt="Six-month revenue per acquisition day rises 12% for Black Friday week at apps with detected promotions, versus a 15% decline at apps without."/></figure>
<h3>About half of Black Friday growth comes from existing free users</h3>
<p>For apps with detected promotions, about half of the Black Friday increase came from people who’d had the app for over three months.</p>
<p>Black Friday buyers also look different from usual. During Black Friday week, 55% of these apps’ new paying customers had installed the app at least a day before paying, up from 45% in September–October.</p>
<p>So if you’re planning a Black Friday offer, don’t focus only on new installs. Some of the opportunity may already be in your free user base.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/df49c6ae03e2f6eb0be3b506fcd595b093cc7261-3200x1600.png" alt="Among Black Friday first-time buyers at apps with detected promotions, 55% are existing users and 45% pay on their first day."/></figure>
<p>Black Friday offers also reach current and lapsed subscribers. In 9% of apps with detected promotions, these subscribers redeem at least three times as many offers per day as in September–October.</p>
<h3>Annual plans account for nearly half of new paying customers</h3>
<p>More Black Friday buyers start on an annual plan.</p>
<p>For apps with detected promotions, 49% of new paying customers bought an annual plan during Black Friday and Cyber Monday, up from 41% in September–October, mostly at the expense of monthly plans.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/89e365822108e907e9d64fdb091691ced6dad802-3200x1800.png" alt="Annual plans rise from 41% to 49% of first purchases during Black Friday, while monthly plans fall from 31% to 22%."/></figure>
<p>So far, Black Friday buyers on weekly and monthly plans renew about as often as usual. Weekly renewal rates change by less than one percentage point, while monthly second renewals are two percentage points lower.</p>
<p>Annual plan buyers look stable too, at least early on. Fewer of them request a refund within 30 days (2.7%, down from 4%), and auto-renew cancellations in the first 30 days don’t increase. Whether they renew is still an open question: their first renewal falls outside our six-month follow-up.</p>
<h3>Utilities and Health &amp; Fitness show the largest acquisition gains</h3>
<p>The Black Friday gap varies widely by category. Among apps with detected promotions, Utilities and Health &amp; Fitness get 46% and 37% more new paying customers per day than in their baseline, while Education and Social &amp; Lifestyle stay roughly level.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/66f1497d0dd1b6c0fe8f1bc59eead08b7d64e653-3200x2000.png" alt="Utilities and Health &amp; Fitness lead Black Friday acquisition gains among apps with detected promotions, at 45.6% and 36.5% above autumn baseline."/></figure>
<p>Apps without detected promotions are below baseline in every category group. If you’re setting a campaign target, your category’s results may be a better benchmark than the overall 20% increase.</p>
<h3>Black Friday discounts extend beyond introductory offers</h3>
<p>Apps offer introductory deals, reduce prices on existing subscriptions, or add cheaper subscription options.</p>
<p>Among the apps with detected Black Friday promotions, we spotted:</p>
<ul>
<li><strong>Offers for first-time subscribers</strong>. In 45% of apps, purchases using first-time subscriber offers increased, mainly through introductory deals such as a discounted first month. Both new users and existing free users can redeem them.</li>
<li><strong>Lower prices on existing products</strong>. 16% of apps sold an existing product at a lower price. About half of these may be regular price changes, not holiday promotions.</li>
<li><strong>New, cheaper subscription options</strong>. 16% of apps introduced a temporary subscription priced below their usual one.</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3c3c3011a66bb7917819b40d459c03f553f03779-3200x1800.png" alt="Among apps with detected promotions, 45% show increased use of first-time subscriber offers, 16% lower existing prices, and 16% introduce cheaper subscriptions."/></figure>
<p><em>Apps can use more than one type of offer, and some promotions were detected through other signals, such as sale-named products, so these shares don’t add up to 100%.</em></p>
<h2>Christmas offers don’t show the same promise as Black Friday</h2>
<p>The Christmas results give us less reason to expect an increase in new paying customers.</p>
<p>During December 15–25, apps with offers detected only at Christmas get 24% fewer new paying customers per day than in their baseline, almost the same drop as apps without detected offers (22%).</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/629a6e21a0e728650c5c6a3754c186f2b7d563de-3200x1800.png" alt="New paying customers fall 24% at apps with Christmas-only offers, 22% without detected Christmas offers, and 7% with both Black Friday and Christmas offers."/></figure>
<p>The stronger result comes from apps with promotions during both Black Friday and Christmas, which are down just 7%. But those apps were already 24% above baseline during Black Friday week, so their smaller decline doesn’t show that a Christmas discount has a notable impact.</p>
<h2>Apps with New Year offers see more new paying customers into January</h2>
<p>January is when apps with detected promotions pull ahead again. Over December 26–February 1, they get 14% more new paying customers per day than in their baseline, while apps without detected offers get 4% fewer.</p>
<p>January buyers also generate 10% more revenue per customer over six months than Black Friday buyers, with little difference in early renewal rates. That comparison is within apps with detected Black Friday promotions, so it doesn’t tell us whether New Year offers improve revenue. In apps without detected Black Friday promotions, January and Black Friday buyers are worth about the same (+2%).</p>
<p>The acquisition increase is strongest around New Year and smaller later in January:</p>
<ul>
<li><strong>December 26–January 4</strong>: 24% more new paying customers per day for apps with detected offers, versus 4% fewer for those without</li>
<li><strong>January 5–February 1</strong>: 11% more, versus 6% fewer</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3332212ea7225d8097c658408baf86e72424182b-3200x1800.png" alt="Apps with detected New Year offers gain 24% more new paying customers in early January and 11% later, versus declines of 4% and 6% without offers."/></figure>
<p>The New Year lift is strongest in Health &amp; Fitness, which goes from 28% below baseline at Christmas to 11% above over New Year and January. Gaming is also 10% above baseline over the same period.</p>
<p>These category figures cover apps with and without promotions, so they show where acquisition is stronger across the season (not where discounts work best).</p>
<h2>How subscription apps present their holiday offers</h2>
<p>Holiday paywalls differ most from ordinary ones in how they present the deal. Percentage discounts, crossed-out prices, and urgency appear far more often on holiday paywalls. Free trials remain common, and lifetime options and single-plan layouts appear more often.</p>
<p>The percentage off is the most common holiday addition. It appears on 61% of Black Friday paywalls and 54% of New Year paywalls, compared with 8% outside the holidays. Crossed-out prices and urgency wording jump too, from under 5% of ordinary paywalls to roughly a third or more of holiday ones.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/7f1ecb6fed1bc0f892ed261c12fa89ac77676d4d-1840x1380.png" alt="Book-summary app paywall with an owl holding a gift, a 19% Cyber Monday discount, and a “Get the offer” button."/></figure>
<p>During Black Friday week, nearly three in four paywalls showing a percentage discount offered 50% off or more.</p>
<p>Our acquisition findings also suggest what to test in your paywall message. Existing free users account for a substantial part of the Black Friday increase. To target these users, test a paywall built around the paid features missing from their current experience, with the discount attached to that upgrade.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4e64e59d8f69c2b1e1a2bd34992dbdf71c82ce41-2000x1500.png" alt="Gaming paywall highlighting unlimited games, no ads, and offline play, with a Black Friday offer of 40% off at $9 per month."/></figure>
<p>Lifetime access shows up about twice as often as usual: on 35–38% of Black Friday and New Year paywalls, versus 17% outside the holidays. The buyer gets lasting access at a sale price, while the app collects revenue upfront instead of relying on future renewals.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/62e4a1e5b84caa13f1b912c84fcf32dc31527b51-2000x1500.png" alt="Travel app paywall offering 40% off lifetime access for $119.99, alongside monthly and annual options, with a November 30 deadline."/></figure>
<p>Finally, more holiday paywalls show a single plan (30–36%, versus 20% of ordinary paywalls). A single-plan offer screen removes the need to compare subscription lengths and may help focus the sale.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b24e6d7908af46999cab9364d79fadfa517d39f4-2048x1536.png" alt="1. Study app paywall featuring one monthly plan, a January offer of 40% off for three months, and a “Get Pro” button."/></figure>
<h2>What to test in your next holiday campaign</h2>
<p>Our data gives subscription apps a good reason to experiment with holiday offers. But there’s no universal playbook. Start with how users in your category behave, then adjust your approach for each holiday.</p>
<p>Gaming apps, for example, tend to do well around Christmas and New Year, when people get new devices and have time off. Fitness apps, on the other hand, pick up in January, once New Year resolutions start.</p>
<p>It’s also worth looking beyond net new users. During Black Friday week, about half of the increase in new paying customers came from users who installed the app more than three months earlier.</p>
<p>Converting existing free users, winning back past subscribers, and upselling current ones can all add revenue during this period.</p>
<p>Beyond timing and audience, a few parts of the offer are worth testing:</p>
<ul>
<li><strong>Discount size</strong>: Most holiday paywalls that state a percentage go to 50% or more. A deep cut grabs attention, but it isn’t always needed. In January, for example, when more people buy even without an offer, a smaller discount might do the job.</li>
<li><strong>Urgency</strong>: About a third of Black Friday paywalls use limited-time wording, and one in five adds a countdown. Too much pressure can feel pushy, so test different nudges for each segment, such as showing long-time free users the premium features they keep running into.</li>
<li><strong>Lifetime access vs. subscriptions</strong>: A one-time plan appeals to people tired of subscriptions and brings in revenue upfront. Those buyers never renew, though, so price it with your annual subscribers in mind.</li>
<li><strong>Plan setup</strong>: Test featuring a lifetime option, focusing on a single offer, labeling one plan as the best value, or discounting a different plan length</li>
<li><strong>Price display</strong>: Holiday paywalls show crossed-out prices more often than ordinary ones, and many break down the price by week or month. Christmas paywalls also lean on gift wording and holiday emoji, so test which presentation fits your offer.</li>
</ul>
<aside class="tip"><strong>Put these ideas to the test</strong><p>Build your holiday offer in <a href="https://www.revenuecat.com/feature/paywalls">RevenueCat Paywalls</a> with AI or a template, show it to specific audiences with <a href="https://www.revenuecat.com/feature/targeting/">Targeting</a>, and use <a href="https://www.revenuecat.com/feature/experiments">Experiments</a> to see which version brings in more subscribers and revenue.</p></aside>
<h2>How we collected the data</h2>
<p>We analyzed 4,964 apps worldwide during the 2025–26 holiday season, comparing each app’s daily average of new paying customers with its own September 15–October 12, 2025 baseline. Apps needed at least 30 new paying customers a week in the baseline, and iOS and Android versions count as separate apps. A new paying customer is someone paying for an app for the first time, whether they installed it recently or had been using it for free.</p>
<ul>
<li><strong>Holiday windows</strong> cover November 24–December 1 for Black Friday and Cyber Monday, December 15–25 for Christmas, and December 26–February 1 for New Year and January</li>
<li><strong>Promotion detection </strong>looks for increases in offer redemptions by new or existing subscribers, sale-named products or Offerings, lower prices on existing products, new cheaper subscription products, and holiday wording on RevenueCat Paywalls. Each signal must stand out compared with both the baseline and March 2026. Signals can overlap.</li>
<li><strong>Revenue and retention</strong> follow buyers for 180 days. Revenue is net of refunds and before store commission; cohort comparisons account for acquisition-window length. Annual renewals fall outside this follow-up.</li>
<li><strong>Paywall analysis</strong> covers RevenueCat Paywalls with holiday wording shown at least 100 times: 176 at Black Friday, 69 at Christmas, and 104 at New Year, one per app project. We compare these with 5,193 non-holiday paywalls shown October 13–November 9.</li>
</ul>
<p>Results generally describe the median app; the install-timing split uses an average. Most promotion signals depend on purchases, so unsuccessful offers may go undetected.</p>
<p>The comparisons show associations, not direct proof that promotions cause growth or that particular paywall designs convert better.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[When Is KSP the Right Tool?]]></title>
      <link>https://www.revenuecat.com/blog/engineering/when-ksp</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/when-ksp</guid>
      <pubDate>Tue, 27 Oct 2026 01:29:41 GMT</pubDate>
      <dc:creator><![CDATA[Jaewoong Eum]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[In this article, you'll start from a KSP processor you might write for this job, then trace inward through KSP's input model, and the conditions where it fits]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/c836bac0216f2e039b0e576914237b88eb1533ab-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>You add <code>@JsonClass(generateAdapter = true)</code> to a data class, build the project, and a <code>JsonAdapter</code> you never wrote appears under <code>build/generated/ksp</code>. KSP, Kotlin Symbol Processing, has become the default way Android libraries turn annotations into generated code, so it's natural to reach for it the moment you want generated code of your own.</p>
<p>The RevenueCat Codegen Gradle Plugin generates type safe Kotlin accessors for your entitlements (what a purchase unlocks) and offerings (the products your paywall shows), yet it contains no KSP processor. KSP appears in its source in one place, a fix for a build failure. So why would a code generator skip KSP, and how could KSP still break its build?</p>
<p>In this article, you'll start from a KSP processor you might write for this job, then trace inward through KSP's input model, the conditions where it fits, the point where it stops, the plugin's two Gradle tasks, and the one place its output meets KSP.</p>
<h2><strong>The fundamental problem: A generated copy that goes stale</strong></h2>
<p>The <a href="https://www.revenuecat.com/blog/engineering/android-codegen">RevenueCat Codegen Gradle Plugin</a> reads the entitlements and offerings configured in your RevenueCat dashboard and writes Kotlin accessors for them, so that a string lookup like this one:</p>
<pre><code>val isPremium = customerInfo.entitlements[&quot;premium_access&quot;]?.isActive == true</code></pre>
<p>becomes a property the compiler can check:</p>
<pre><code>val isPremium = customerInfo.isPremiumAccessActive</code></pre>
<p>Generated code like this is a copy of a source of truth, and a copy is only useful while it matches the original. That gives every generator two questions to answer: where does the input come dfrom, and how does the build find out that the input changed?</p>
<p>If you were building this generator yourself, the obvious first attempt would be a KSP processor. Assume the dashboard's configuration, the schema, was already downloaded to a file. In a small JVM project, a processor reads the file's path from a KSP option and generates a constant, <code>Schema.VALUE</code>, from the file's contents. If you've ever passed <code>room.schemaLocation</code> to Room this way, the build script looks familiar:</p>
<pre><code>ksp {
    arg(&quot;schema.path&quot;, file(&quot;schema.txt&quot;).absolutePath)
}</code></pre>
<p>The first build generates the right value. Then change only schema.txt from v1 to v2, and build and run the app again. On KSP 2.3.12 with Gradle 9.8.1, the relevant console lines are:</p>
<pre><code>&gt; Task :app:kspKotlin UP-TO-DATE
&gt; Task :app:compileKotlin UP-TO-DATE
...
&gt; Task :app:run
Schema.VALUE=v1</code></pre>
<p>The processor never ran, and the app printed the old value. The generator answered the first question and got the second one wrong: the build succeeded, and the app ran with data that was already out of date. To see why, you need to look at what KSP actually reads and tracks.</p>
<h2><strong>What KSP is: A processor that reads your declarations</strong></h2>
<p>KSP's README describes it as &quot;an API that you can use to develop lightweight compiler plugins,&quot; and a plugin built with it is called a symbol processor. A symbol is the compiler's description of something you declared, such as a class, a function, or a property, with its name, modifiers, and types. A processor never parses your source text. It receives your program as a tree of symbols, such as <code>KSClassDeclaration</code>, <code>KSFunctionDeclaration</code>, and <code>KSPropertyDeclaration</code>.</p>
<p>It can also ask for any type to be resolved: which class a name refers to, whether it's nullable, and what its type arguments are. It responds by writing new source files, which the Kotlin compiler compiles together with your code in the same build.</p>
<p>To see what KSP hands a processor and what it expects back, imagine a processor the plugin doesn't have, for a world where your entitlements were declared in code instead of in a dashboard:</p>
<pre><code>@Target(AnnotationTarget.CLASS)
annotation class Entitlements(vararg val keys: String)

@Entitlements(&quot;premium_access&quot;, &quot;pro_photo&quot;)
object AppEntitlements</code></pre>
<p>The processor starts by asking the Resolver, KSP's entry point for querying the code being compiled, for annotated symbols:</p>
<pre><code>class EntitlementsProcessor(
    private val codeGenerator: CodeGenerator,
) : SymbolProcessor {

    override fun process(resolver: Resolver): List&lt;KSAnnotated&gt; {
        resolver.getSymbolsWithAnnotation(&quot;com.example.Entitlements&quot;)
            .filterIsInstance&lt;KSClassDeclaration&gt;()
            .forEach { declaration -&gt; generate(declaration) }
        return emptyList()
    }</code></pre>
<p>For each declaration it finds, the processor reads the annotation's arguments. A vararg argument arrives as a List:</p>
<pre><code>    private fun generate(declaration: KSClassDeclaration) {
        val keys = declaration.annotations
            .first { it.shortName.asString() == &quot;Entitlements&quot; }
            .arguments.single().value as List&lt;*&gt;</code></pre>
<p>Then it builds the file with KotlinPoet, a library that writes Kotlin source from builder calls instead of string templates, and hands the file to KSP:</p>
<pre><code>        val idObject = TypeSpec.objectBuilder(&quot;RCEntitlementId&quot;)
            .addOriginatingKSFile(declaration.containingFile!!)
        for (key in keys) {
            idObject.addProperty(
                PropertySpec.builder(key.toString().uppercase(), String::class, KModifier.CONST)
                    .initializer(&quot;%S&quot;, key)
                    .build(),
            )
        }

        FileSpec.builder(&quot;com.example.rc&quot;, &quot;RCEntitlementId&quot;)
            .addType(idObject.build())
            .build()
            .writeTo(codeGenerator, aggregating = false)
    }
}</code></pre>
<p>Three parts of this code define what KSP is:</p>
<ul>
<li><strong>Input</strong>: <code>getSymbolsWithAnnotation</code> returns declarations from the code being compiled. This processor doesn't open any files. It reads declarations that KSP has already parsed, and asks KSP to resolve a type only when it needs one.</li>
<li><strong>Output</strong>: the file is written with KSP's <code>CodeGenerator</code>, not to a path you choose. KSP places it under <code>build/generated/ksp/</code>.</li>
<li><strong>Origin</strong>: <code>addOriginatingKSFile</code> records which source file the output came from, and <code>aggregating = false</code> says the output depends on that file alone. KotlinPoet's KSP module, <code>kotlinpoet-ksp</code>, packs both into a KSP <code>Dependencies</code> object in <code>writeTo</code>.</li>
</ul>
<p>That object is how KSP keeps incremental builds correct, meaning how it decides what to regenerate when only some files changed. Here is its declaration in KSP's <code>CodeGenerator.kt</code>, with the KDoc trimmed. Look at what the public constructor accepts:</p>
<pre><code>class Dependencies
private constructor(
    val isAllSources: Boolean,
    val aggregating: Boolean,
    val originatingFiles: List&lt;KSFile&gt;,
) {

    constructor(
        aggregating: Boolean,
        vararg sources: KSFile,
    ) : this(false, aggregating, sources.toList())

    companion object {
        val ALL_FILES = Dependencies(true, true, emptyList())
    }
}</code></pre>
<p>The public constructor accepts <code>KSFile</code>s, the source files of the current compilation, and nothing else. No other KSP API lets an output depend on a file path, a URL, or an option value either.</p>
<p>The <code>aggregating</code> flag chooses between two kinds of output. An <strong>isolating</strong> output (<code>aggregating = false</code>) is like the translation of a single page: it needs redoing only when that page changes. An <strong>aggregating</strong> output is like a book's index: any edited or new page might change it, and <code>ALL_FILES</code> ties an output to every file at once.</p>
<p>On the next build, KSP compares the changed source files with the origins each output recorded, hands your processor only the files that need processing again, and puts back every other output from a copy it saved on the previous build. You get correct incremental builds without writing any tracking code, as long as each output names the source files it came from and everything it depends on is code this compilation sees. The <a href="https://kotlinlang.org/docs/ksp-incremental.html">KSP incremental processing guide</a> covers the full rules.</p>
<h2><strong>When KSP is the right tool: Three conditions</strong></h2>
<p>KSP fits a generator best when three conditions hold:</p>
<ul>
<li><strong>Input declared in your code</strong>: annotations, classes, function signatures, anything the compiler parses as part of the build.</li>
<li><strong>Output that needs the compiler's knowledge</strong>: annotation arguments, resolved types, nullability, <code>suspend</code> modifiers, supertypes, and type parameters all arrive as symbols, so you never write a Kotlin parser.</li>
<li><strong>Regeneration tied to those declarations</strong>: the output should change exactly when the declarations change, and KSP's per file tracking gives you that for free.</li>
</ul>
<p>The processors you already use are built around all three. Room, for example, has to know whether a DAO method is <code>suspend</code> to generate the right implementation, and that information is a property of a <code>KSFunctionDeclaration</code>.</p>
<p>KSP also limits what a processor can produce. <a href="https://kotlinlang.org/docs/ksp-overview.html">Its overview</a> says it directly: &quot;KSP-based processors can't examine expressions or statements, and they can't modify the source code.&quot; A processor can't look inside function bodies, and it can only add new files next to yours. A tool that needs to change code you wrote has to work below KSP.</p>
<p>A full Kotlin compiler plugin runs inside the compiler and can change what gets compiled, which is how kotlinx.serialization adds serializers to your classes and how the Compose compiler rewrites <code>@Composable</code> functions. A bytecode transform works on compiled classes instead. Hilt's Gradle plugin uses one to swap the superclass of each <code>@AndroidEntryPoint</code> class for the generated <code>Hilt_</code> class, so your source never has to name it.</p>
<h2><strong>Where KSP's model stops: Inputs outside your source files</strong></h2>
<p>The <code>schema.txt</code> processor fails the first condition, and the stale value follows directly from it. Before running a task, Gradle fingerprints every input the task declared, a hash of each value and file, and compares the result with the previous run. If nothing changed, it prints <code>UP-TO-DATE</code> and skips the task. The declared input for an <code>arg()</code> option is the string itself: the path, which didn't change.</p>
<p>Gradle never looks inside the file, and KSP's own tracking can't help either, because it runs inside the task, and the task never ran. Switching the output to <code>aggregating = true</code> or <code>Dependencies.ALL_FILES</code>, or turning off KSP's incremental mode with <code>ksp.incremental=false</code>, produced the same stale value. It stayed stale until some unrelated change, such as an edit to a Kotlin file, made Gradle run the task again.</p>
<p>Tying the output to a source file, the way <code>EntitlementsProcessor</code> does, makes the failure quieter. Picture a processor that finds an annotated class the same way, reads <code>schema.txt</code>, and records the annotated class's file as the origin of its output. Edit <code>schema.txt</code> and, in the same build, add a new, unrelated Kotlin file. The new file changes a declared input, so Gradle runs <code>kspKotlin</code> this time, and KSP hands the processor only the source file that changed: the new one.</p>
<p>The annotated class isn't in it, so <code>getSymbolsWithAnnotation</code> returns nothing, and the processor writes nothing, even though it just read <code>v2</code>. The origin file didn't change either, so KSP puts back the copy it saved on the previous build. The app prints <code>v1</code> again, this time with <code>kspKotlin</code> visibly running in the build log.</p>
<p>Neither result is a KSP bug. KSP tracked every source file exactly as promised, and <code>schema.txt</code> was never one of them.</p>
<p>The fix has to come from Gradle's side, because Gradle decides whether the task runs. Instead of a plain string, pass KSP a <code>CommandLineArgumentProvider</code>: a small class whose annotated properties Gradle checks like any other task input, and whose <code>asArguments()</code> produces the option the processor reads. Mark the file with <code>@InputFile</code>, and Gradle fingerprints the file's contents, not just its path:</p>
<pre><code>class SchemaArgProvider(
    @get:InputFile
    @get:PathSensitive(PathSensitivity.RELATIVE)
    val schema: File,
) : CommandLineArgumentProvider {
    override fun asArguments() = listOf(&quot;schema.path=${schema.absolutePath}&quot;)
}

ksp {
    arg(SchemaArgProvider(file(&quot;schema.txt&quot;)))
}</code></pre>
<p>Now an edit to schema.txt reruns the task, and Gradle's info output shows how:</p>
<pre><code>Task ':app:kspKotlin' is not up-to-date because:
  Input property 'commandLineArgumentProviders.$0.schema' file .../app/schema.txt has changed.
The input changes require a full rebuild for incremental task ':app:kspKotlin'.</code></pre>
<p>The output is fresh, but notice the last line. Gradle can tell a task which source files changed, but only for the inputs the task marks as incremental. A change to any other input, this file included, makes Gradle run the task from scratch. The KSP Gradle plugin then throws away KSP's records of which output came from which file, and every source file in the module is processed again. You get the right value, but every schema edit now costs a full reprocess, which is the work KSP's incremental model exists to avoid.</p>
<p>Room made the same trade. Its DAOs meet all three conditions, but it also writes schema files during the build and reads them back to generate migrations automatically, so those files are inputs too. Its documentation says that if you pass <code>room.schemaLocation</code> as a processor option, you &quot;must use Gradle's CommandLineArgumentProvider&quot; for correct incremental and cached builds, and the Room Gradle plugin, added in Room 2.6.0, sets that up for you.</p>
<p>RevenueCat's input isn't a file at all. Check it against the three conditions: the input isn't declared in your code, the output is built from JSON and needs nothing only the compiler knows, and it should change when the dashboard changes, not when your source files do. None of them holds.</p>
<p>The network adds a practical problem on top. A processor that called the RevenueCat API would run only when the KSP task runs, which brings back the stale value from the first experiment. With Gradle's build cache turned on, even that run could be skipped. The KSP task is cacheable, so if an earlier build stored outputs for the same inputs, Gradle reuses them without running the task, and the API is never called.</p>
<p>The download needs its own rule for when to call the API again, one that runs whether or not your source files changed. The clearest home for that rule is a Gradle task of its own, whichever tool generates the code. Once that task has saved the schema to a file, a processor would only add cost: the file would have to be declared as a KSP input as well, and every schema change would reprocess the whole module. That's the design the plugin uses: no processor, and code generation in a Gradle task as well.</p>
<h2><strong>Two Gradle tasks: Separating the network from the generator</strong></h2>
<p>The plugin splits the job along the line between what Gradle can fingerprint and what it can't. In the <a href="https://github.com/RevenueCat/purchases-android/tree/main/codegen">plugin's source</a>, <code>RevenueCatCodeGenPlugin.apply()</code>, the function Gradle calls when a build applies the plugin, registers both tasks. The first one owns the network. In its registration, four <code>set</code> calls pass along values from the <code>revenuecat { }</code> block in your build script. The one to watch is the fifth: <code>cacheDir</code> is a fixed directory under <code>build/</code> that the plugin picks itself:</p>
<pre><code>val cacheDir = project.layout.buildDirectory.dir(&quot;revenuecat/cache&quot;)
val outputDir = project.layout.buildDirectory.dir(&quot;generated/revenuecat/kotlin&quot;)

val fetchTask: TaskProvider&lt;FetchSchemaTask&gt; = project.tasks.register(
    &quot;rcFetchSchema&quot;,
    FetchSchemaTask::class.java,
) { task -&gt;
    task.apiKey.set(extension.apiKey)
    task.projectId.set(extension.projectId)
    task.cacheTtlMinutes.set(extension.cacheTtlMinutes)
    task.offlineMode.set(extension.offlineMode)
    task.cacheDir.set(cacheDir)
}</code></pre>
<p>The second task owns the code and receives the same cacheDir. Here is its registration, with the generation flags left out:</p>
<pre><code>val generateTask: TaskProvider&lt;GenerateCodeTask&gt; = project.tasks.register(
    &quot;rcGenerateCode&quot;,
    GenerateCodeTask::class.java,
) { task -&gt;
    task.packageName.set(extension.packageName)
    task.namingStyle.set(extension.namingStyle)
    task.cacheDir.set(cacheDir)
    task.outputDir.set(outputDir)
    task.dependsOn(fetchTask)
}</code></pre>
<p>Notice the handoff: <code>cacheDir</code> is the output of <code>rcFetchSchema</code> and the input of <code>rcGenerateCode</code>. A single JSON file in that directory, <code>revenuecat-schema.json</code>, is the only thing the generator ever reads. Everything about the network stays on one side of that file, and everything about Kotlin stays on the other.</p>
<h3><strong>The fetch task: An input Gradle can't fingerprint</strong></h3>
<p>The real input of <code>rcFetchSchema</code> is the current state of your dashboard, and Gradle has no way to fingerprint that: it lives on RevenueCat's servers, and nothing on your disk changes when someone renames an entitlement. So <code>FetchSchemaTask</code> opts out of Gradle's check in its <code>init</code> block:</p>
<pre><code>init {
    group = &quot;revenuecat&quot;
    description = &quot;Fetches entitlement and offering schema from RevenueCat API v2&quot;
    // Always evaluate the task action so the TTL check inside runs
    outputs.upToDateWhen { false }
}</code></pre>
<p>With upToDateWhen { false }, Gradle runs the task's code on every build, and the code makes the decision itself:</p>
<pre><code>val cache = SchemaCache(cacheDir.get().asFile)

if (cache.isValid(cacheTtlMinutes.get())) {
    logger.lifecycle(&quot;RevenueCat schema cache is still valid, skipping fetch.&quot;)
    return
}</code></pre>
<p><code>isValid</code> reads the timestamp stored inside the cached JSON and compares its age with <code>cacheTtlMinutes</code>, which defaults to 30. Only a missing, unreadable, or expired cache triggers the API calls. The TTL (time to live) is a policy for an input that never announces its changes: the plugin can't know when your dashboard changed, so it caps how long a successful fetch is reused. A failed fetch is a different case. The default, <code>USE_CACHE_OR_SKIP</code>, logs a warning and builds from the old cache, or skips code generation if there's no cache yet. Setting <code>offlineMode</code> to <code>FAIL</code> turns the failed fetch into a failed build.</p>
<h3><strong>The generate task: A pure function of the cache</strong></h3>
<p><code>rcGenerateCode</code> sits on the other side of the line. Its output depends only on the cached JSON and a few settings, and it declares all of them. Here are its properties, minus the package name and the four generation flags, which are declared like <code>namingStyle</code>:</p>
<pre><code>// Note: @CacheableTask is intentionally omitted. The cache JSON includes a timestamp that
// changes on every fresh fetch, so the Gradle build cache would never produce a hit.
public abstract class GenerateCodeTask : DefaultTask() {

    @get:Input
    public abstract val namingStyle: Property&lt;NamingStyle&gt;

    @get:InputDirectory
    @get:PathSensitive(PathSensitivity.RELATIVE)
    public abstract val cacheDir: DirectoryProperty

    @get:OutputDirectory
    public abstract val outputDir: DirectoryProperty
}</code></pre>
<p>Because every input is declared, Gradle's ordinary up to date check decides when to skip it. On a second build with nothing changed, exactly one task does any work:</p>
<pre><code>&gt; Task :app:rcFetchSchema
RevenueCat schema cache is still valid, skipping fetch.

&gt; Task :app:rcGenerateCode UP-TO-DATE
...
&gt; Task :app:kspDebugKotlin UP-TO-DATE
&gt; Task :app:compileDebugKotlin UP-TO-DATE</code></pre>
<p><code>rcFetchSchema</code> runs because it has to, finds a valid cache, and returns without touching the JSON file. The file's fingerprint matches the previous build, so <code>rcGenerateCode</code> is skipped, and everything downstream stays up to date. When a fetch writes a new file, the fingerprint changes and the generator runs again.</p>
<p>The comment above the class is about Gradle's build cache, which reuses outputs that any build, on any machine, stored for the same inputs. The timestamp inside the JSON changes on every fresh fetch, even when the schema doesn't, so two machines or two CI runs that each fetch on their own never have identical inputs. Marking the task cacheable would mostly store outputs that no other build could reuse.</p>
<p>Inside the task, generation isn't incremental at all: it deletes the output directory and writes every file again. That's the difference in granularity from KSP. A KSP processor tracks each output file so it can regenerate one out of hundreds, while this task treats its whole output as one unit and lets Gradle decide whether to rebuild it.</p>
<h3><strong>The generator: The same KotlinPoet, a different destination</strong></h3>
<p>The code that actually writes Kotlin will look familiar from the processor earlier in this article. Looking at <code>EntitlementsGenerator</code>:</p>
<pre><code>private fun generateIdObject(entitlements: List&lt;EntitlementSchema&gt;, outputDir: File) {
    val objectBuilder = TypeSpec.objectBuilder(&quot;RCEntitlementId&quot;)
        .addKdoc(&quot;Auto-generated entitlement ID constants from RevenueCat dashboard.&quot;)

    for (entitlement in entitlements) {
        val constName = NamingConfig.toConstant(entitlement.lookupKey)
        objectBuilder.addProperty(
            PropertySpec.builder(constName, String::class)
                .addModifiers(KModifier.CONST)
                .initializer(&quot;%S&quot;, entitlement.lookupKey)
                .addKdoc(&quot;%L&quot;, entitlement.displayName)
                .build(),
        )
    }

    FileSpec.builder(packageName, &quot;RCEntitlementId&quot;)
        .addType(objectBuilder.build())
        .build()
        .writeTo(outputDir)
}</code></pre>
<p>Put it next to that processor and the builders are the same. The differences are where the data comes from and where the file goes. The input is a <code>List&lt;EntitlementSchema&gt;</code> parsed from the cached JSON instead of a <code>KSClassDeclaration</code>, and the last call writes to a plain directory instead of to KSP's <code>CodeGenerator</code>. There's no <code>addOriginatingKSFile</code> call, because no source file exists for the output to originate from.</p>
<h2><strong>Where the plugin meets KSP: Generated sources are KSP inputs</strong></h2>
<p>Generated code only helps if it's compiled with your app, so the plugin adds its output directory to the <code>main</code> Kotlin source set. The registration that takes effect is equivalent to:</p>
<pre><code>android.sourceSets.getByName(&quot;main&quot;).kotlin.srcDir(outputDir)</code></pre>
<p>Here <code>outputDir</code> is that same directory, resolved to a plain <code>java.io.File</code>: just a path, with no record of which task writes to it. That detail matters in a moment. Every task that reads the <code>main</code> source set now reads <code>build/generated/revenuecat/kotlin</code>. </p>
<p>That includes <code>compileDebugKotlin</code>, and it also includes <code>kspDebugKotlin</code>, because KSP processes the same sources the compiler compiles. In a test app with a small processor that logs every file <code>getAllFiles()</code> returns, KSP listed each generated RevenueCat file next to the app's own source. The plugin has no processor, yet KSP still reads everything it generates, and that's where the build can break.</p>
<p>The plugin's first prototype made only the <code>compile</code> tasks wait for <code>rcGenerateCode</code>. Take the clause that matches <code>ksp</code> task names out of the shipped plugin's wiring, shown below, and the test app fails on Gradle 9.8.1 with AGP 9.4.1 and KSP 2.3.12:</p>
<pre><code>* What went wrong:
A problem was found with the configuration of task ':app:kspDebugKotlin' (type 'KspAATask').
Property has implicit dependency
  Gradle detected a problem with the following location: '.../app/build/generated/revenuecat/kotlin'
    Task ':app:kspDebugKotlin' uses this output of task ':app:rcGenerateCode' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed</code></pre>
<p>Gradle isn't saying the tasks ran in the wrong order. In all six failing runs, across AGP 8 and 9, with KSP 2.3.11 and 2.3.12, <code>rcGenerateCode</code> had already finished before <code>kspDebugKotlin</code> started. It's saying that nothing guarantees that order. Gradle can see that <code>kspDebugKotlin</code> reads files from a directory that <code>rcGenerateCode</code> declares as its output, but no rule says KSP must wait for it. A <code>java.io.File</code> can't supply that rule, because it's only a path. Without a rule, a later build could run the two tasks in either order, so since Gradle 8.0 this fails the build instead of printing a warning.</p>
<p>Failing is the better outcome here. With the same broken wiring, running only <code>:app:kspDebugKotlin</code> succeeds silently: <code>rcGenerateCode</code> isn't part of that build at all, so nothing gets flagged, and KSP processes an empty or stale directory.</p>
<p>This is the one place KSP appears in the plugin's source. The shipped plugin declares the rule by task name: any task whose name looks like a Kotlin compile, KSP, or KAPT task runs after <code>rcGenerateCode</code>.</p>
<pre><code>private fun configureKotlinTaskDependencies(
    project: Project,
    generateTask: TaskProvider&lt;GenerateCodeTask&gt;,
) {
    project.tasks.configureEach { task -&gt;
        val isKotlinTask = task.name.endsWith(&quot;Kotlin&quot;) &amp;&amp; (
            task.name.startsWith(&quot;compile&quot;) ||
                task.name.startsWith(&quot;ksp&quot;) ||
                task.name.startsWith(&quot;kapt&quot;)
            )
        if (isKotlinTask) {
            task.dependsOn(generateTask)
        }
    }
}</code></pre>
<p><code>configureEach</code> applies the rule to every task in the project, including the <code>ksp</code> and <code>kapt</code> tasks whenever those plugins register them, so the rule holds for every Android variant without the plugin knowing which processors you use. Matching by name covers the consumers the plugin knows about. A new consumer with a different naming scheme would need a new clause.</p>
<h3><strong>The Variant API alternative: Registering the directory with its producer</strong></h3>
<p>The alternative is to never hand Gradle a bare <code>File</code>. AGP's Variant API, the <code>androidComponents</code> hooks that let a plugin adjust each build variant, can take the task that produces a directory instead of just its path. This isn't what the plugin ships today, but in the same test app, this wiring replaced both the source set call and the name matching:</p>
<pre><code>val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.onVariants(androidComponents.selector().all()) { variant: Variant -&gt;
    variant.sources.java?.addGeneratedSourceDirectory(generateTask, GenerateCodeTask::outputDir)
}</code></pre>
<p><code>addGeneratedSourceDirectory</code> receives the task and the property that holds its output, so Gradle works out the order by itself: every task that reads the variant's sources, the compiler and KSP included, now runs after <code>rcGenerateCode</code>. Without name matching or any <code>dependsOn</code> on the Kotlin and KSP tasks, the test app built on both AGP 9.4.1 and AGP 8.13.2, with <code>rcGenerateCode</code> running first and KSP seeing every generated file.</p>
<p>The trade off is a closer coupling to how AGP behaves across versions. The registration goes through <code>sources.java</code>, even though the files are Kotlin. On AGP 8, Kotlin support comes from the separate <code>org.jetbrains.kotlin.android</code> plugin, and that plugin and KSP pick up a variant's generated directories only from <code>sources.java</code>. In the test app, a directory added to <code>sources.kotlin</code> was silently ignored, and the build failed with unresolved references to the generated code.</p>
<p>The call itself has also changed between releases. Before AGP 8.12, <code>addGeneratedSourceDirectory</code> overwrote the output location a task had set. Since 8.12, it only supplies a default. The test app covered only AGP 8.13.2 and 9.4.1, and SQLDelight and Apollo Kotlin both carry version checks for these differences. None of this affects apps that use the plugin today, because it never calls <code>addGeneratedSourceDirectory</code>. It registers its directory through the <code>main</code> source set and orders the compile, KSP, and KAPT tasks by name.</p>
<h2><strong>Conclusion</strong></h2>
<p>Before you write a generator, find its source of truth and ask who notices when it changes. If the compiler parses it, a KSP processor gives you incremental builds without any tracking code of your own. If it lives anywhere else, such as a schema file or a <code>.proto</code>, make it a declared input of a Gradle task, because the build only sees what you declare. Then make sure every task that reads the generated sources, KSP's included, knows which task produces them.</p>
<p>KSP asks one question: which of your source files changed? A Gradle task asks another: which of my declared inputs changed? A dashboard on a server can answer neither, so the plugin turns it into a file that can. Choosing where code generation runs is choosing which of those two questions your input can answer.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Designing RevenueCat Paywalls for both iPhone Duo displays]]></title>
      <link>https://www.revenuecat.com/blog/company/designing-revenuecat-paywalls-for-both-iphone-duo-displays</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/designing-revenuecat-paywalls-for-both-iphone-duo-displays</guid>
      <pubDate>Fri, 23 Oct 2026 15:13:34 GMT</pubDate>
      <dc:creator><![CDATA[Courtney Touchstone]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Keep content readable on the open display, make room on the closed screen, turn a crowded paywall into a multi-step flow, and use the Paywall AI Editor to be ready in minutes. ]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/be54dcf74e33628bb2c92d9617c5c6cdc3e3c756-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>iPhone Duo <a href="https://www.apple.com/iphone-duo/" target="_blank" rel="noopener noreferrer">arrives today</a> with plenty of debate around its price, proportions, and how useful that outer display really is. <a href="https://www.wired.com/review/apple-iphone-duo/" target="_blank" rel="noopener noreferrer">Early reviews</a> praise the expansive inner display while raising questions about the compromises of using it as an everyday phone. For app developers, launch day brings a practical question: how does your app look when customers open it on either display? Your paywall is a good place to start.</p>
<p>iPhone Duo gives your paywall two very different spaces to work with. Closed, vertical space is tight. Open, a layout designed for a narrow phone can leave text and purchase buttons stretching across a wide display.</p>
<p>With RevenueCat, you can improve both experiences by controlling component widths, keeping fixed footers compact, and spreading longer benefit lists across multiple screens. Start with your existing paywall and work through these adjustments.</p>
<h2><strong>Give your content a maximum width</strong></h2>
<p>A full-width purchase button can look right on a narrow screen and oversized on an open Duo. The same goes for a stack of headlines, benefits, and subscription options.</p>
<p>RevenueCat’s new min/max sizing controls let you put limits on components whose width or height is set to <strong>Fill</strong>. They use the available space until they reach the limit you’ve set.</p>
<p><strong>BEFORE:</strong></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b813f9214a1c553b872f809271947c599ec5b263-3200x2000.png" alt=""/></figure>
<p><strong>AFTER:</strong></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/1eef3b11c45bde11874d5d23bb8ad0ee1acac205-3200x2000.png" alt=""/><figcaption>Use Fill with Max width to give content and buttons room on either side of the unfolded display.</figcaption></figure>
<p>For a starting point:</p>
<table>
<thead><tr>
<th><p><strong>Element</strong></p></th>
<th><p>Setting to try</p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Background container</p></td>
<td><p>Keep it full width</p></td>
</tr>
<tr>
<td><p>Main content stack</p></td>
<td><p>Fill, maximum width around 600pt, centered within its parent</p></td>
</tr>
<tr>
<td><p>Purchase button</p></td>
<td><p>Fill, maximum width around 400pt, centered within its parent</p></td>
</tr>
</tbody>
</table>
<p>These are starting values to adjust for your design. A short headline, a detailed benefit list, and an image-heavy paywall may need different widths.</p>
<p>The limits apply wherever the paywall appears. You don’t need to target the Duo specifically: content can fill a narrower space and stop growing in a wider one.</p>
<p>Min/max sizing works with <strong>Fill</strong>, rather than <strong>Fit</strong> or <strong>Fixed</strong>. On iOS, it requires <a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/component-properties#minimum-and-maximum-sizes">RevenueCat SDK </a><strong><a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/component-properties#minimum-and-maximum-sizes">5.93.0 or later</a></strong>. Older SDKs ignore the limits.</p>
<h2><strong>Make room on the closed screen</strong></h2>
<p>On the closed Duo, pay attention to how much space your fixed footer occupies.</p>
<p>A footer containing plan choices, a purchase button, supporting copy, and generous spacing can leave little room for the content above it. Review the whole screen together: can someone understand the offer, read the price, and reach the controls comfortably?</p>
<p>Start by tightening unnecessary spacing and shortening repetitive copy. Keep pricing, subscription details, and purchase controls clear.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f9f3fb4ebd45ea41b495ad56949e0f73fc5ad391-3680x1880.png" alt=""/><figcaption>A compact footer leaves more room for the content that explains your offer.</figcaption></figure>
<p>If the screen still feels crowded, consider changing how much you ask it to communicate at once.</p>
<h2><strong>Give your benefits their own screen</strong></h2>
<p>A multi-step paywall is worth trying when your current design packs several benefits into one screen.</p>
<p>You might introduce your benefits on one screen, and then present the subscription offer on the next screen. RevenueCat lets you build a multi-step paywall, design each screen separately, and add buttons in the editor to move between screens. Customers choose a subscription plan and purchase on the final screen.</p>
<p>The Paywall AI Editor makes it even easier to turn a single-screen paywall into a multi-step flow. Add a blank screen before your existing paywall, then ask the AI Editor to move your benefits onto it. It will match your paywall’s branding, generate a relevant headline, and move the benefit copy to the new screen, leaving more room for the offer on the final screen.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/5650c62f75f83026beb8aab9da93c77f76607833-3200x2000.png" alt=""/><figcaption>Split your single-step paywall into a multi-step flow so the benefits show clearly in the available iPhone Duo height.</figcaption></figure>
<p>Review the generated copy, navigation, and final purchase screen before publishing. Adding steps changes the experience, so treat it as a design to test with your audience.</p>
<h2><strong>Let the Paywall AI Editor handle the adjustments</strong></h2>
<p>You can also ask the Paywall AI Editor to optimize your existing paywall for iPhone Duo. It can make the layout edits for you, helping your content fit comfortably on the outer display and use the extra room on the inner display.</p>
<p>Start with a simple request:</p>
<p><em>Optimize this paywall for iPhone Duo so it looks good on both the open and closed displays.</em></p>
<p>Review the changes, then preview your paywall in both configurations. You can keep refining it through follow-up prompts, whether you want a more compact footer, narrower content, or more room for your benefits.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/60832ecda5b548ca92a69eb6974c82ccc08f1956-3200x2000.png" alt=""/><figcaption>Ask the Paywall AI Editor to adapt your existing design, then review the changes across both displays.</figcaption></figure>
<h2><strong>Keep your close button in the right place</strong></h2>
<p>If your paywall includes a close button so customers can dismiss it without subscribing, check its placement on iPhone Duo. Depending on the display, orientation, and how your app presents the paywall, iOS can move navigation controls to the side. A close button inside your paywall won’t automatically move with them.</p>
<p>To let iOS handle its placement, select the button in the RevenueCat Paywall Editor and enable <strong>Use system navigation</strong> in the button’s properties. This replaces the close button with a native iOS close control.</p>
<p>If you have a multi-step paywall, review the close and back buttons on each screen and enable the setting where appropriate. Then preview and test the flow on both displays.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3a72ff5a3acdd42565b95deb22c906b5609e2d99-3200x2000.png" alt=""/></figure>
<h2><strong>Check the whole experience</strong></h2>
<p>Once you’ve made the adjustments, test your paywall closed, open, and rotated, using the same presentation your app uses.</p>
<p>Look for:</p>
<ul>
<li><strong>Readable content:</strong> Headlines and benefit copy wrap cleanly, with no clipping.</li>
<li><strong>Balanced widths:</strong> Buttons and text remain comfortable to read on the open display.</li>
<li><strong>Usable footers:</strong> Fixed content leaves enough room for the rest of the paywall.</li>
<li><strong>Accessible controls:</strong> Close, back, continue, and purchase controls remain visible and usable.</li>
<li><strong>Complete navigation:</strong> Every step works, and customers can reach the final offer.</li>
</ul>
<p>Check the SDK version shipped in your app, too. Multi-step paywalls require iOS SDK <strong>5.83.0 or later</strong>; older versions display only the final screen. Using min/max sizing alongside them requires <strong>5.93.0 or later</strong>.</p>
<p>See the <a href="https://www.revenuecat.com/docs/tools/paywalls#required-sdk-versions">SDK requirements</a> for other frameworks.</p>
<h2><strong>Start with the paywall you already have</strong></h2>
<p>With iPhone Duo arriving today, it’s a good time to give your paywalls another look. Keep content at a comfortable width, make room above your footer, and consider multiple screens when your benefits need more space.</p>
<p><a href="https://app.revenuecat.com" target="_blank" rel="noopener noreferrer">You can make these adjustments in RevenueCat today</a>, with the Paywall AI Editor helping you rework the layout. Start with your existing paywall, check it folded and open, and make sure your offer is easy to read and act on in both.</p>
<section><h3>Optimize your RevenueCat Paywalls for iPhone Duo</h3><p><a href="https://app.revenuecat.com/">Open the Paywall Editor &gt;</a></p></section>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Introducing Google Play Billing & RevenueCat Handbooks]]></title>
      <link>https://www.revenuecat.com/blog/engineering/google-play-handbook</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/google-play-handbook</guid>
      <pubDate>Thu, 22 Oct 2026 04:29:14 GMT</pubDate>
      <dc:creator><![CDATA[Jaewoong Eum]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[In this article, you'll explore what each handbook covers, how the two line up chapter by chapter, and which reading path fits your situation.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/bc9458b1e08d84dfaaa5264cc21d36aebcf227b4-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Getting a first purchase working with the Play Billing Library (PBL) can take more than just several days. Getting subscriptions right in production takes much longer: a subscription moves through a state machine, a failed renewal passes through a grace period and an account hold, your backend has to verify every purchase token, and lifecycle events arrive as Real Time Developer Notifications (RTDN) through Cloud Pub/Sub.</p>
<p>Google's documentation covers every piece of this, spread across dozens of pages that you have to connect yourself. RevenueCat has published two free handbooks that each put the whole lifecycle in one place: one for building directly on the Play Billing Library, and one for building on the RevenueCat Android SDK.</p>
<p>In this article, you'll explore what each handbook covers, how the two line up chapter by chapter, which reading path fits your situation, how the companion skills bring the same material into your coding agent, and what has shipped since the handbooks were written.</p>
<h2><strong>The Google Play Billing Handbook: Building it yourself</strong></h2>
<p><a href="https://www.revenuecat.com/guides/google-play-billing">The Google Play Billing Handbook</a> is written for Android developers who know Kotlin, Activities, ViewModels, and Gradle but know Google Play Billing only partly or not at all. It takes you from zero billing knowledge to a production ready integration.</p>
<p>The 18 chapters are organized in seven parts that build on each other:</p>
<ol>
<li><strong>Foundations</strong> (chapters 1 and 2): how Google Play's billing system fits together, and how to set up your environment.</li>
<li><strong>Products</strong> (chapters 3 and 4): one time products and subscriptions.</li>
<li><strong>Client side integration</strong> (chapters 5 to 8): the Play Billing Library, the purchase flow, plan changes, and error handling.</li>
<li><strong>Server side integration</strong> (chapters 9 and 10): purchase verification with the Google Play Developer API, and RTDN.</li>
<li><strong>The subscription lifecycle</strong> (chapters 11 to 14): the subscription state machine, payment recovery, cancellations, pauses, winback, and price changes.</li>
<li><strong>Production readiness</strong> (chapters 15 to 17): security and fraud prevention, testing, and catalog management at scale.</li>
<li><strong>Beyond standard billing</strong> (chapter 18): alternative billing and external offers.</li>
</ol>
<p>Five appendices, A to E, collect the material you look up rather than read: a complete RTDN reference table, a <code>BillingResult</code> response code reference, a Google Play Developer API quick reference, a migration guide from PBL 7 to PBL 8, and a pull out subscription state diagram.</p>
<p>The code is Kotlin on both the client and the server, so you read the whole integration in one language, and the client code uses the coroutine extensions from the <code>billing-ktx</code> artifact. The handbook targets Play Billing Library 8, specifically versions 8.0.0 through 8.3.0, and the last section of this article covers what has shipped since. You can read every chapter on the web or download the whole handbook as a <a href="https://www.revenuecat.com/documents/google-play-handbook">287 page PDF</a>, and neither requires signing up.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2ffae0677b136f7049c342bb70b106764ec75f88-1624x1038.png" alt=""/></figure>
<h2><strong>The RevenueCat Android SDK Handbook: The same problems, solved with the SDK</strong></h2>
<p><a href="https://www.revenuecat.com/guides/revenuecat-android-sdk/understanding-revenuecat">The RevenueCat Android SDK Handbook</a> is for developers who have read the first handbook or have equivalent experience with Google Play Billing, and it solves the same problems with the SDK. Because it assumes that background, it uses concepts like base plans, offers, replacement modes, and purchase tokens without explaining them in full again. Its structure mirrors the first handbook chapter for chapter. Where the SDK simplifies an implementation, the chapter shows the code. Where the SDK removes a problem entirely, the chapter explains what RevenueCat handles on your behalf and what, if anything, you still need to do.</p>
<p>Two appendices close the handbook. Appendix A is a quick reference for the SDK's API. Appendix B compares what RevenueCat replaces on the client and on the server, and lists what remains your responsibility.</p>
<p>The code uses the SDK's coroutine extensions, such as <code>awaitOfferings()</code> and <code>awaitPurchase()</code>, and targets RevenueCat Android SDK 9.x, which wraps Play Billing Library 8.x. The handbook is available on the web and as a free <a href="https://www.revenuecat.com/documents/revenuecat-handbook">63 page PDF</a>, whose cover calls it The RevenueCat Handbook.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9f345b84d027665a6ce806adfc72eb8ad5305502-1237x1160.png" alt=""/></figure>
<h2><strong>Reading the handbooks side by side</strong></h2>
<p>Because the chapter numbers match, you can open the same chapter in both handbooks and see how the work changes. A few pairs show the pattern:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/87fc962c51e2e1e8d18fac67560a978880519544-1600x1471.png" alt=""/></figure>
<p>Not all of the gap between 287 and 63 pages, both at A4 size, is work that the SDK takes over. The first handbook has 38 pages of appendices against 7, explains concepts the second one assumes, and spends 19 pages on Google's alternative billing programs, which the second covers in one page because the SDK had no dedicated APIs for them when the handbook was written. Where the SDK does take over the work, the gap is real: the chapters on integrating the library, the backend, notifications, and subscription states run 60 pages in the first handbook and 11 in the second.</p>
<h2><strong>Choosing a reading path</strong></h2>
<p>The Google Play Billing Handbook's preface suggests two paths depending on where you start, and a third one fits if you're evaluating RevenueCat:</p>
<ul>
<li><strong>Building a new integration from scratch</strong>: read the first four parts (chapters 1 to 10) in order, then the subscription lifecycle chapters as you prepare for production, and the production readiness chapters before launch.</li>
<li><strong>Upgrading an existing integration from PBL 7</strong>: start with Appendix D, the PBL 7 to PBL 8 migration guide, then read the chapters that cover your knowledge gaps.</li>
<li><strong>Deciding whether to use RevenueCat</strong>: start with Appendix B of the RevenueCat handbook, then read the chapter pairs for the parts of the lifecycle that matter most to your app.</li>
</ul>
<h2><strong>The handbooks as agent skills</strong></h2>
<p>A coding agent doesn't need a 287 page book to handle one failed renewal. It works better with short, task oriented instructions that it loads only when your request calls for them. <a href="https://github.com/RevenueCat/play-billing-skills">Play Billing Skills</a> turns both handbooks into 45 agent skills, split into two collections that answer the same questions at two layers. The <code>google-play/</code> collection has 25 skills for the raw Play Billing Library, the Google Play Developer API, and RTDN, derived from the Google Play Billing Handbook. The <code>revenuecat/</code> collection has 20 skills for the RevenueCat Android SDK, RevenueCat's REST API, and webhooks, derived from the RevenueCat Android SDK Handbook.</p>
<p>Most skills distill one chapter or appendix of their source handbook into a numbered workflow, and the YAML frontmatter at the top of every skill records where it came from. In the repository, the webhooks skill starts like this:</p>
<pre><code>name: webhooks
description: Use this skill when consuming RevenueCat webhooks on your backend. Covers the normalized event schema, the full event type list, idempotency via the event id field, and the correct handling for CANCELLATION versus EXPIRATION versus RENEWAL.
license: Apache-2.0; see LICENSE
metadata:
  author: RevenueCat
  source: revenuecat-handbook chapter 10
  keywords:
  - android
  - revenuecat
  - webhooks
  - event-types
  - idempotency
  - cancellation
  - expiration</code></pre>
<p>The <code>description</code> is what the agent matches your request against, and the <code>source</code> field ties the skill back to the chapter you can read yourself.</p>
<h3><strong>Installing the skills</strong></h3>
<p>The <code>revenuecat/</code> collection also ships as the <code>revenuecat-play-billing</code> plugin in the <a href="https://github.com/RevenueCat/ai-toolkit">RevenueCat AI Toolkit</a> marketplace. In Claude Code, add the marketplace, then install the plugin next to the main <code>revenuecat</code> plugin, which connects the agent to your RevenueCat project through the MCP server:</p>
<pre><code>claude plugin marketplace add RevenueCat/ai-toolkit
claude plugin install revenuecat@RevenueCat
claude plugin install revenuecat-play-billing@RevenueCat</code></pre>
<p>The plugin is also available in Cursor, in Codex after <code>codex plugin marketplace add RevenueCat/ai-toolkit</code> and <code>codex plugin add revenuecat-play-billing@RevenueCat</code>, and in VS Code, where plugin marketplaces are still experimental. Gemini CLI installs one extension per GitHub repository and only picks up the main plugin, so on Gemini CLI, install the skills from the Play Billing Skills repository instead.</p>
<p>The <code>google-play/</code> collection isn't in the plugin. To install both collections, clone the repository and run its install script:</p>
<pre><code>git clone https://github.com/RevenueCat/play-billing-skills.git ~/play-billing-skills
~/play-billing-skills/scripts/install-skills.sh</code></pre>
<p>he script links every skill into <code>~/.claude/skills</code> and prefixes each link with <code>gp-</code> or <code>rc-</code>, so both collections can be installed side by side even where they share a name, such as <code>setup</code> and <code>purchase-flow</code>. If you already installed the plugin, run the script with <code>--only google-play</code> so that the RevenueCat skills aren't installed twice. The repository's README covers the script's other options, such as installing into a single project, and how to point Android Studio's agent mode, Gemini CLI, and Cursor at the skill files.</p>
<p>Once the skills are installed, you describe the task in plain language:</p>
<p>&quot;Handle RevenueCat webhooks on our backend. Ignore duplicate deliveries, and don't revoke access on a cancellation until the subscription actually expires.&quot;</p>
<p>Claude Code picks skills by matching your request against each skill's description, so this request can load the webhooks skill shown above, installed as <code>rc-webhooks</code>. The skill has the agent deduplicate events by <code>event.id</code>, treat <code>CANCELLATION</code> as a subscriber turning off auto renew rather than as an access change, and revoke access on <code>EXPIRATION</code>.</p>
<h2><strong>Beyond the handbooks: Play Billing Library 9 and SDK 10</strong></h2>
<p>Both handbooks target Play Billing Library 8, which has been the minimum for new apps and app updates since August 31, 2026, with an extension available on request until November 1, 2026. Since the handbooks were written, Google has shipped PBL 9.0.0 (May 19, 2026) and 9.1.0 (June 18, 2026).</p>
<p>For Play Billing Library 9.0, read <a href="https://www.revenuecat.com/blog/engineering/play-billing-v9">Google I/O 2026: What's New in Google Play and Play Billing Library 9.0</a>. It walks through the four code level changes in 9.0.0 and the billing changes aimed at involuntary churn, so it picks up where the Google Play Billing Handbook stops. Version 9.1.0 added the Billing Choice APIs, which <a href="https://www.revenuecat.com/blog/engineering/play-billing-choice">the complete guide to Google Play's Billing Choice</a> covers. If you're still on v7, <a href="https://www.revenuecat.com/blog/engineering/play-billing-8-migration">the v7 to v8 migration guide</a> pairs with Appendix D.</p>
<p>The skills have already moved past the handbooks on library versions. The <code>google-play/</code> collection targets Play Billing Library 9 and adds two migration skills for it: one for apps on 8.x, and one that takes a 7.x integration to 9.x in two stages, through the v8 changes first.</p>
<p>One lifecycle figure is out of date in both the Google Play Billing Handbook and the <code>google-play/</code> skills, which describe account hold as lasting up to 30 days. Since December 1, 2025, Google sets the account hold length automatically by default, to 60 days minus any grace period, and you can still choose a custom length in the Play Console.</p>
<p>On the RevenueCat side, the Android SDK is on 10.x today and still wraps Play Billing Library 8.3.0, so the handbook's PBL 8 basis matches the SDK's billing layer. The handbook's main APIs are still in the SDK, with one deprecation: 10.3.0 introduced <code>StoreReplacementMode</code> and <code>replacementMode()</code>, which replace the <code>GoogleReplacementMode</code> enum and the <code>googleReplacementMode()</code> builder method that chapter 7 uses.</p>
<h2><strong>Conclusion</strong></h2>
<p>Keep the Google Play Billing Handbook close when you need to understand what Google Play is actually doing, such as why a subscription is on hold or what happens to the purchase token after a plan change. Its appendices work best as the reference material they were written to be. </p>
<p>Reach for the RevenueCat handbook when you're evaluating RevenueCat or integrating it, and read Appendix B before you decide what to build yourself. If you work with a coding agent, install the skills so that its billing answers start from these handbooks instead of from whatever version of the library it happened to be trained on.</p>
<p>Reading the same chapter in both handbooks is a direct way to see what an SDK takes on for you. The subscription lifecycle doesn't get any simpler when you change layers. What changes is which parts of it live in your code.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[The 2026 Shippies: your winners]]></title>
      <link>https://www.revenuecat.com/blog/company/shippies-2026-winners</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/shippies-2026-winners</guid>
      <pubDate>Wed, 21 Oct 2026 04:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Lorelei Whitman]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[The winning apps of 2026, and what made them stand out]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/2f6001e2945e27e486861e2ecafe99b22c31d267-1280x720.jpg" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>The Shippies celebrate outstanding achievement in app monetization, recognizing apps that don't just ship, but nail the entire subscription journey — from first onboarding screen to hundredth renewal.</p>
<p>This year, the Shippies got a night of their own. And because we never do things by halves, we held it on a real ship: the Intrepid Museum aircraft carrier in New York. Surrounded by great company (and a retired space shuttle), we had finalists, founders, and family gathered in one room to celebrate remarkable apps and the thinking behind them.</p>
<p>Every winner took home a personalized Golden Shippy trophy, and a spot on a Times Square billboard during <a href="https://www.revenuecat.com/blog/company/new-york-app-week-2026">New York App Week</a>. Plus bragging rights, of course.</p>
<p>Here’s our seven winners, and the why behind their wins.</p>
<h2><strong>Full Steam Ahead: Runna</strong></h2>
<p><em>This award celebrates subscription growth excellence: the app that found the smartest routes to growth, not just the most obvious ones.</em></p>
<p><strong>Runna</strong> has built a growth engine that reaches well beyond the App Store: web subscriptions, onboarding, and win-backs. Since joining Strava in 2025, Runna has been sold as part of a combined annual subscription. That gives Runna a new route to customers and puts its training plans right where runners already track and share their activity.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>Distribution where users already are:</strong> the Strava + Runna bundle puts Runna inside runners’ existing routine and is available directly through Strava’s app and website</li>
<li><strong>Growth across the funnel:</strong> web subscriptions, onboarding, and cancellation recovery all pull their weight</li>
<li><strong>A place on the start line: </strong>an official training partner for the TCS New York City Marathon and the Boston Marathon, Runna reaches potential users at their most committed moment</li>
<li><strong>Thinking beyond the paywall:</strong> growth comes from partnerships and lifecycle work, not just price tests</li>
</ul>
<p>Runna is a strong reminder that your biggest growth lever might not live inside your app at all.</p>
<h2><strong>Treasure Chest: Audiomack</strong></h2>
<p><em>Treasure Chest recognizes the app with the most creative monetization: the most inventive ways to turn value into revenue.</em></p>
<p><strong>Audiomack</strong> reaches more than 50 million listeners a month, with a truly global audience. Audiomack recognizes that a global audience varies widely in how willing and able people are to pay. So rather than forcing one model onto every listener in every market, it combines ads, subscriptions, and direct artist support.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>Built for global reality:</strong> monetization reflects real differences in willingness and ability to pay, like offering day/week/month passes to Audiomack+ in Nigeria</li>
<li><strong>Several ways in:</strong> hybrid monetization offers value to (and from) all types of listener — free listening with ads, a subscription priced for local wallets, and direct ways for fans to support the artists they love</li>
<li><strong>Fans and creators both win:</strong> direct artist support ties revenue to the community at the heart of the app, via premium plans allowing fans to buy badges and hear music before release</li>
<li><strong>Paying the way people actually pay:</strong> partnerships with Flutterwave and MTN add bank transfers, local cards, and mobile money</li>
</ul>
<p>Audiomack shows that creative monetization often means offering more ways to pay, rather than trying to force different users through the same funnel.</p>
<h2><strong>Clear Waters: Structured</strong></h2>
<p><em>This category is about making the complicated simple. The award goes to an app that takes something messy, and makes it feel effortless for the user.</em></p>
<p><strong>Structured</strong> turns a scattered day into one clear picture. Tasks, to-dos, and calendar events all sit on a single visual timeline, so you can see your whole day and what’s coming next. There’s no juggling between apps and no wading through detail.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>One timeline for everything: </strong>tasks, to-dos, and appointments live together in one visual timeline at the core of the app</li>
<li><strong>Works with the tools you already use:</strong> Structured syncs with your calendar and Apple Reminders, and imported to-dos land straight in your inbox or timeline</li>
<li><strong>Guardrails against overplanning:</strong> the optional Energy Monitor warns you when you schedule more than you can handle</li>
<li><strong>Somewhere for loose thoughts:</strong> the inbox holds ideas and tasks until you’re ready to schedule them, enabling you to capture thoughts without becoming overwhelmed</li>
<li><strong>Recognized by Apple:</strong> App Store Editors’ Choice, a 2025 finalist for iPad App of the Year, and a 2026 Apple Design Awards finalist for Inclusivity</li>
</ul>
<p>Structured is proof that there’s a surprising amount of complexity behind design that appears simple.</p>
<h2><strong>Level Up: Reading.com</strong></h2>
<p><em>Level Up is about the best learning experience: celebrating the app that helps people learn in a way that actually sticks.</em></p>
<p><strong>Reading.com</strong> gives parents a structured way to teach their children to read, no teaching background or extra resources needed. The lessons are built to support two people at once: the child who's learning, and the parent who's helping them. Lessons are fully scripted and built on phonics and the Science of Reading.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>Designed for two learners:</strong> lessons support both the child and the parent in their respective roles</li>
<li><strong>No training required:</strong> simple UI and fully scripted lessons removes any guesswork, meaning any parent can step into the teacher’s role with confidence</li>
<li><strong>Grounded in science:</strong> lessons use explicit phonics instruction that follows the Science of Reading</li>
<li><strong>A clear path: </strong>the experience follows how learning actually happens, with step-by-step lessons building letter recognition towards different reading levels</li>
<li><strong>Practice that reinforces every lesson:</strong> interactive, decodable storybooks come with videos, games, and printables</li>
</ul>
<p>The best learning products are designed around the learner, and Reading.com goes one step further by designing around the whole household.</p>
<h2><strong>Beyond the Chatbox: Suno</strong></h2>
<p><em>This award goes to the app that offers the best AI experience; using AI to create something more than a text box and a reply.</em></p>
<p><strong>Suno</strong> lets anyone turn an idea into a song, then keep shaping it until it sounds right. The model is impressive, but the real magic is the creative workflow built around it. An editor, studio tools, and personalization keep users in the creative seat long after the first take.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>A workflow, not a one-off:</strong> the Song Editor lets users rearrange, extend, and replace sections and rewrite lyrics — continuing to edit and develop songs as long as they like</li>
<li><strong>Creative control stays with the user:</strong> Covers, Personas, and Remaster let people explore new takes on a song, while Stems split a track into as many as 12 editable parts</li>
<li><strong>Personal by design:</strong> Voices, Custom Models, and My Taste shape what Suno creates around each user’s personality and preferences, creating a truly unique experience for every user</li>
</ul>
<p>Suno shows what an AI app looks like when the prompt is simply the creative jumping point, rather than the entire product.</p>
<h2><strong>Worth Coming Back For: Fitbod</strong></h2>
<p><em>Recognizing the app that has the best ongoing value, this category is about earning a subscription month after month.</em></p>
<p><strong>Fitbod</strong> builds every workout around where you are right now. It tracks how recovered each muscle group is, learns from the sets you log, and adjusts as you get stronger. Your training keeps pace with your progress, and the app changes as your needs do, which gives people a strong reason to stay subscribed.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>Progress you can see:</strong> Strength Score and Estimated Strength show how you’re improving over time and give users motivation to stay long-term</li>
<li><strong>Workouts that respond to your body:</strong> every muscle group gets a recovery score, and Fitbod builds each session around the freshest muscles, meaning the lessons never get stale</li>
<li><strong>Learns from you:</strong> Fitbod’s adaptive AI learns from your edits to its recommendations, alongside your goals, equipment, and experience</li>
</ul>
<p>Fitbod is a textbook case of recurring value supporting recurring revenue.</p>
<h2><strong>Small Crew, Big Ship: CardPointers</strong></h2>
<p><em>This award celebrates the indie developer proving that you don't need a big team to build an exceptional app business.</em></p>
<p><strong>CardPointers</strong> is the work of independent developer Emmanuel Crouvisier, who built a focused product around one problem: helping people get more value from the credit cards they already have. It started in 2019 as an evenings-and-weekends project. Within a year, <a href="https://www.revenuecat.com/customers/cardpointers">it was earning him more than his day job</a>.</p>
<p><strong>Why it won:</strong></p>
<ul>
<li><strong>A sharp focus:</strong> it solves one specific problem, and solves it well</li>
<li><strong>Takes on mental load: </strong>CardPointers tells you which card to use for each purchase, so you get the most from category bonuses without memorizing them</li>
<li><strong>Everywhere you spend: </strong>works across iPhone, iPad, Mac, Apple Watch, CarPlay, and Android, plus browser extensions</li>
<li><strong>Smart business decisions:</strong> web checkout through Stripe saves Emmanuel as much as 27% in fees, and staying in Apple’s Small Business Program saves another 15% on in-app purchases</li>
</ul>
<p>CardPointers is proof that with a clear problem and a lot of focus, a small crew can sail a very big ship.</p>
<h2><strong>Until next year…</strong></h2>
<p>The 2026 Shippies was a memorable night, not only celebrating our seven Shippies winners, but also recognizing the winners of Shipaton 2026, RevenueCat’s global mobile hackathon. <a href="https://www.revenuecat.com/blog/company/shipaton-2026-winners"><strong>Check out the list of all 58 Shipaton winners right here</strong></a><strong>.</strong></p>
<p>To the finalists, winners, and everyone who shipped something this year: congratulations! Like any voyage, it's not always smooth sailing, but shipping anything is a huge accomplishment. We’re excited to see what you build next.</p>
<p>So keep shipping — we'll see you at the Shippies ’27. Maybe next time, it'll be your app up on that billboard.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Shipaton 2026 winners]]></title>
      <link>https://www.revenuecat.com/blog/company/shipaton-2026-winners</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/shipaton-2026-winners</guid>
      <pubDate>Tue, 20 Oct 2026 13:23:32 GMT</pubDate>
      <dc:creator><![CDATA[Perttu Lähteenlahti]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Meet the 58 winning apps of Shipaton 2026, from thoughtful everyday tools to inventive games, and the builders behind them.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/e3ff2e4f4e6b0f1195de55f7d1489ae9834b57c1-2400x1260.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>This year’s Shipaton saw tens of thousands of people taking part, submitting a total of 3985 projects. The growth is of course not surprising, as AI tooling has improved significantly in the last year, and we are glad to see that this year we got great apps from both new builders as well as existing app builders. And even though at times it seemed like Apple was not going to have time to review all the apps you submitted, we ended up seeing almost 3000 apps submitted to the App Store, Play Store, and Galaxy Store.</p>
<p>From all those submissions, our judges ended up choosing 58 winning apps that showcase the best apps in their categories. These decisions were not easy, and were made harder by the sheer amount of apps that made it to the final judging stage. </p>
<p>And now it's time to reveal the winners of Shipaton 2026: </p>
<h2>Grand Prize Winner</h2>
<p>The Grand Prize was about what happened after an app shipped. We looked for developers who kept improving the product, found their first users, and gave people a reason to stay. Downloads, retention, and revenue helped tell that story, alongside the decisions that moved the app forward during Shipaton.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/mio-self-care-habit-tracker" target="_blank" rel="noopener noreferrer"><u>Mio</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/896cee73cbc1e4b02e354f4fb90bb0948e5bd2a3-2400x1260.png" alt="Mio — Shipaton 2026 winner"/></figure>
<p>When we looked at the revenue and growth across all the apps submitted to Shipaton, Mio was a clear winner. Mio’s traction is even more impressive, when you take into account that it competes in a very tough category of habit trackers. </p>
<p>Mio is a cozy, self-care and habit tracker, where building habits earns you coins, levels, new companions and collectibles. Gamification is a big part of the app, with the user leveling up a pixel companion as they build their own habits. On top of the gamification, Mio also has social features, allowing you to add friends and create groups.
</p>
<p><a href="https://apps.apple.com/us/app/mio-self-care-habit-tracker/id6783861950" target="_blank" rel="noopener noreferrer"><u>App Store</u></a>
<a href="https://play.google.com/store/apps/details?id=com.DWGLabs.Mio" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>#BuildInPublic </h2>
<p>Building in public means inviting people into the process while there's still time for their feedback to change things. For this award, we looked at how developers shared their progress, what they learned from the conversations, and how those lessons made their way into the app.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/boop-catch-every-dog-cat" target="_blank" rel="noopener noreferrer"><u>Boop</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e54fb48e78bc526697cb26d9fbd626809c4dce4a-2400x1260.png" alt="Boop — Shipaton 2026 winner"/></figure>
<p>Boop turns the cats and dogs you meet on walk into a collection: point your phone at a pet, identify its breed, and save it as a sticker in your book. Taiwo took the idea out into the world through his 100 Boops challenge, handing owners printed portraits and letting their feedback shape the app. A very different take on building in public, but one that worked. </p>
<p>The judges particularly liked the effort Taiwo put into building in public, describing it as unmatched.</p>
<p><a href="https://apps.apple.com/us/app/boop-catch-every-dog-cat/id6794737842" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/scroll-survival-block-apps" target="_blank" rel="noopener noreferrer"><u>Scroll Survival</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/c527f991232f604e06301d0ff86acbd5384dd49f-2400x1260.png" alt="Scroll Survival — Shipaton 2026 winner"/></figure>
<p>Putting your phone down feels a little more rewarding when there's a village growing while you're away. Scroll Survival turns focus sessions into buildings, villagers, and unfolding stories. Eric Vargas shared the build through daily updates and a public roadmap, with community feedback becoming dark mode, widgets, and even a story-based consequence for ending a session early.</p>
<p>The judges highlighted how thoroughly Eric documented the public build, with a clear record of the journey and its key moments.</p>
<p><a href="https://apps.apple.com/us/app/scroll-survival-block-apps/id6769904241" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.scrollsurvival.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/feedbackthread-triage-board" target="_blank" rel="noopener noreferrer"><u>FeedbackThread</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b7c782742258e13767280e6131a2c4b62837c243-2400x1260.png" alt="FeedbackThread — Shipaton 2026 winner"/></figure>
<p>Anyone juggling several apps knows that <a href="https://www.revenuecat.com/blog/growth/review-mining-for-subscription-apps"><u>customer feedback</u></a> rarely arrives in one tidy place. Aivars Meijers built FeedbackThread to bring reviews, bug reports, and feature requests into a single queue, with public boards showing what's happening next. He also opened up the build through livestreams, sharing the decisions and detours between an early prototype and an App Store release.</p>
<p>The judges appreciated how the livestreams and social posts made the whole development journey easy to follow.</p>
<p><a href="https://apps.apple.com/app/id6802637955" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Best Game Award</h2>
<p>For the Best Game Award, we looked for games that made us want to play another round. That meant engaging mechanics, a distinctive world, and care in the details, from controls to sound and animation. We also considered how paid features fit into the experience of playing.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/nitelore" target="_blank" rel="noopener noreferrer"><u>Nitelore</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0619e0fa4a27d3f5aa9acf32e1e394ed72a3177e-2400x1260.png" alt="Nitelore — Shipaton 2026 winner"/></figure>
<p>Nitelore brings the neon tracks and hovering vehicles of classic anti-gravity racers to the phone.</p>
<p>There are ghost time trials for chasing your own best run, combat races for causing trouble, and online multiplayer when you want company. Its tracks, vehicles, and sounds are generated in code, giving the game a distinctive retro-futurist world to race through.</p>
<p>Free tracks give players a way into the game before paid content unlocks, with daily and weekly challenges adding reasons to return for another race.</p>
<p>The judges singled out the game's art style, effects, and haptics.</p>
<p><a href="https://apps.apple.com/us/app/nitelore/id6790147739" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.crabware.nitelore.galaxy" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/keepsweeper" target="_blank" rel="noopener noreferrer"><u>Keepsweeper</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9711db67748ced83c8a188d6a48a75791bab0cfe-2400x1260.png" alt="Keepsweeper — Shipaton 2026 winner"/></figure>
<p>Michael Flarup took the familiar logic of Minesweeper and hid a small fantasy kingdom underneath it. In Keepsweeper, uncovering a tile might reveal resources, a threat, or another chance to grow your economy. Farms benefit from windmills, recruits need blacksmiths, and the whole thing lives inside a Windows 95-style window. It's a lovely setting for a surprisingly involved strategy game.</p>
<p><a href="https://apps.apple.com/app/id6814857804" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.flarup.keepsweeper" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/hearsay-family-party-game" target="_blank" rel="noopener noreferrer"><u>Hearsay</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f82082742f4dbb92b8d47edcc5da7bf738b86ca5-2400x1260.png" alt="Hearsay — Shipaton 2026 winner"/></figure>
<p>Every family has a story that nobody remembers quite the same way. Hearsay puts that to good use: one person answers a personal question truthfully, everyone else invents a lie, and the group tries to spot the real answer. There's a separate vote for the funniest response, too, so a wildly implausible story can still earn its place at the table.</p>
<p><a href="https://apps.apple.com/us/app/hearsay-family-party-game/id6793556159" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>RevenueCat Peace Prize</h2>
<p>The RevenueCat Peace Prize celebrates apps built to make life a little better for someone. That might mean helping people understand an invisible difference, make sense of their health, or feel closer to one another. We looked for a clear understanding of the people behind the problem and a thoughtful way to help them.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/see-as-me-colorblind" target="_blank" rel="noopener noreferrer"><u>See as Me: Colorblind</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/352e6d8fa681c9a3c9a8a4f61c22b1dd332e8f36-2400x1260.png" alt="See as Me: Colorblind — Shipaton 2026 winner"/></figure>
<p>Javier Jiménez discovered he had protanopia after painting a figure green as a child.</p>
<p>See as Me gives him a way to answer the question that followed: what does the world look like to you? Its split-screen camera shows different forms and degrees of color blindness side by side, while a shareable link lets someone else see the comparison without installing the app.</p>
<p>Javier describes the app as free, with no ads or accounts. That keeps the focus on helping classmates, colleagues, and friends understand a difference they might otherwise never see.</p>
<p>The judges highlighted the personal story behind the app: its creator lives with the very problem he's helping others understand.</p>
<p><a href="https://apps.apple.com/us/app/see-as-me-colorblind/id6799407092" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/josie" target="_blank" rel="noopener noreferrer"><u>Josie</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/71d93f9014810f24cb73cf62acd1b32ae7974853-2400x1260.png" alt="Josie — Shipaton 2026 winner"/></figure>
<p>Josie's founders have PCOS themselves and built the app they wished they'd had while trying to understand it. Lab markers, cycle information, and daily habits sit together, making changes easier to follow over time. The care extends to the language: Josie avoids judgmental normal-or-abnormal alerts, helping people build a clearer record to discuss with their clinicians.</p>
<p>The judges appreciated that Josie addresses a real problem and that the founder's involvement with it extends beyond the hackathon.</p>
<p><a href="https://apps.apple.com/us/app/josie/id6781270808" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=care.josie.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/loomi-daily-couples-lessons" target="_blank" rel="noopener noreferrer"><u>Loomi</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d7309b585e185137987ca5bb93533730f1238401-2400x1260.png" alt="Loomi — Shipaton 2026 winner"/></figure>
<p>Loomi makes room for the relationship skills that are easy to put off until something goes wrong. Couples get short lessons, daily questions, shared memories, and date ideas, with each lesson ending in something to try together. Nik Shevchenko connects the idea to his parents' divorce and a wish to help people start those conversations earlier.</p>
<p>The judges particularly liked the interface and the interactive features that give couples more ways to use what they're learning.</p>
<p><a href="https://apps.apple.com/app/id6801512183" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>RevenueCat Design Award</h2>
<p>The RevenueCat Design Award was about the care you can feel when using an app. We looked at how the interface, interactions, and animation worked together, with room for original ideas and a little personality. The best entries made everyday tasks feel clear, considered, and enjoyable.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/clucky-loud-alarm-clock" target="_blank" rel="noopener noreferrer"><u>Clucky</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/817f56fbe22ad69f07ab047258085065db8d9ed0-2400x1260.png" alt="Clucky — Shipaton 2026 winner"/></figure>
<p>Clucky is an alarm clock with a rooster's determination to get you out of bed.</p>
<p>Dismissing it can mean solving a problem, moving around, or finding an object, and the character follows you from setup through your morning missions. Even choosing the alarm time becomes part of the design: scroll the rooster's eyes and use its yellow beak as the pointer.</p>
<p>The same character appears throughout onboarding, missions, and paywalls, while animation and haptics carry its personality into the details. It's the consistency of that little world that makes Clucky memorable.</p>
<p>The judges singled out the rooster's eyes as a time picker, a small detail that shows how much creativity went into the design.</p>
<p><a href="https://apps.apple.com/app/clucky-loud-alarm-clock/id6792550911" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/aweigh" target="_blank" rel="noopener noreferrer"><u>Aweigh</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0fda2d6a3e40f9f7e698468d3ffb46dcdd1c150f-2400x1260.png" alt="Aweigh — Shipaton 2026 winner"/></figure>
<p>Catching a ferry is a pretty good reason to want clear, reliable information on your phone. Aweigh brings Washington State sailing times, vessel locations, cameras, and service alerts to iPhone and Apple Watch. Zach Coleman gives that changing information a familiar shape: a departure board that flips and reshuffles like the signs at a terminal, complete with haptic feedback.</p>
<p><a href="https://apps.apple.com/us/app/aweigh/id6773885528" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/buck-coin-identifier" target="_blank" rel="noopener noreferrer"><u>Buck</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f5debc325b5d721d34a2e6d75f726094c479c671-2400x1260.png" alt="Buck — Shipaton 2026 winner"/></figure>
<p>A box of coins inherited from his grandfather gave Buck's creator a collecting project and plenty of research to do. Buck makes that first step easier, identifying coins and banknotes and helping users explore their history and estimated value. The scan reveal gets its own little celebration, with animation, sound, and haptics before the find joins a digital collection.</p>
<p>Reviewing its monetization, the judges particularly liked the personalized paywall, which shows the user's own scanned coin at a clear moment of value.</p>
<p><a href="https://apps.apple.com/us/app/buck-coin-identifier/id6787937347" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.getbuck.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Catvertising Award</h2>
<p>For the Catvertising Award, the challenge was to find a creative place for RevenueCat Ads within the app experience. We wanted to see how an ad could connect to something a user already wanted to do, and how that choice worked alongside the app's other ways of earning revenue.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/adversary-memory-game-twist" target="_blank" rel="noopener noreferrer"><u>Adversary</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/381e09cf50e5ef9e749a1a627a4702e2ef00532d-2400x1260.png" alt="Adversary — Shipaton 2026 winner"/></figure>
<p>Niv Ben-porath wondered whether trying to avoid an ad could become the game itself.</p>
<p>Adversary hides an unpaired Danger card among a board of matching pairs; reveal it, and the advertising becomes part of the board you're trying to remember. It's a small twist on a familiar memory game that gives the ad a role in the action.</p>
<p>Players get an initial ad-free period, then choose whether to keep playing with ads or buy a permanent unlock that removes them and adds more difficulty options.</p>
<p>The judges particularly liked how the ad became part of the gameplay itself, calling it the most creative integration they'd seen while judging.</p>
<p><a href="https://apps.apple.com/us/app/adversary-memory-game-twist/id6789494101" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/wallo-live-sticker-guestbook" target="_blank" rel="noopener noreferrer"><u>WALLO</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/174798dd26201cc286abe03119f6813d8fd9d2e1-2400x1260.png" alt="WALLO — Shipaton 2026 winner"/></figure>
<p>WALLO turns the guestbook at a gathering into something everyone can contribute to at once. Scan a code, add a photo or sticker, and watch the shared canvas fill up. Its rewarded-ad idea is particularly fun: one guest becomes the Room Hero, shortening the posting cooldown for everyone. That's an ad whose reward the whole room gets to enjoy.</p>
<p>The judges highlighted the shared reward: one person watching an ad unlocks more for everyone in the room.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.jimiroi.wallo" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/ate-collect-what-you-eat" target="_blank" rel="noopener noreferrer"><u>ate</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4bbd89b47b5c3f80221e9de9eb7b4ef205488cb1-2400x1260.png" alt="ate — Shipaton 2026 winner"/></figure>
<p>Egor Kabantsov wanted a place for food memories without another set of numbers to track. ate turns a meal photo into a cut-out sticker, building a collection of dishes and cuisines over time. When the free scans run out, an optional rewarded video unlocks another one, giving someone a way to save the meal already in front of them.</p>
<p>The judges liked how naturally the rewarded ad fits into the experience, giving users a choice about whether to keep the sticker they've just created.</p>
<p><a href="https://play.google.com/store/apps/details?id=app.ate" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Next Gen Award</h2>
<p>The Next Gen Award gave student developers a place to show what they could build. Entrants shared demos and public source code, letting us see both the idea and the work behind it. The category made room for promising projects that hadn't yet reached an app store.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/bubble-games" target="_blank" rel="noopener noreferrer"><u>Bubble Games</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ee8672571fceb0a695d37e5a9e362060a215cced-2400x1260.png" alt="Bubble Games — Shipaton 2026 winner"/></figure>
<p>Bubble Games asks everyone to put their phones on the table, then turns those screens into one shared game board.</p>
<p>Objects travel between devices as friends race, compete, and pass a virtual hot potato. Built by four software engineering students at Université Laval, it's a playful answer to the problem of everyone sitting together while looking at separate screens.</p>
<p>The submission includes ten minigames and public source code, showing how the students made several separate devices behave like one playful surface.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.reallifecorp.bubblegames&amp;pcampaignid=web_share" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/1-lock-screen-time-blocker" target="_blank" rel="noopener noreferrer"><u>1% Lock</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/8920015ac007e0940147b4e77c596298f6f0c2c6-2400x1260.png" alt="1% Lock — Shipaton 2026 winner"/></figure>
<p>Ryunosuke Ishigami built 1% Lock after seeing how much time on his phone had affected his studies. The app pairs blocking with a motivational feed focused on studying and work, giving the familiar urge to scroll somewhere else to go. Built solo in SwiftUI and shared as open source, it's a student's very personal take on finding focus.</p>
<p><a href="https://apps.apple.com/app/id6792271074" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/after-you" target="_blank" rel="noopener noreferrer"><u>After You</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4e626a5847e32ee5bef35fe1ebf41a43b49f3e15-2400x1260.png" alt="After You — Shipaton 2026 winner"/></figure>
<p>Finding time to play together gets harder when your friends live in different time zones. Aamir Abdul Azeez built After You around that reality: each person plays when they can, then watches a replay where both runs appear together. The phone recreates the players' actions inside the game, making a shared moment possible without coordinating another evening online.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.aamirazeez.afteryou" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>HAMM Award (Help Apps Make Money)</h2>
<p>Our mission is to help apps make more money, and the HAMM Award asked developers to put that into practice. We looked at how pricing, paywalls, packaging, and purchases fit the product and its audience. A thoughtful RevenueCat integration should make it easy to understand what's worth paying for.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/gridlock-ultimate-tic-tac-toe" target="_blank" rel="noopener noreferrer"><u>GRIDLOCK</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/23ffd7b95d65782e659e4145ca24a69e4a8bb8f5-2400x1260.png" alt="GRIDLOCK — Shipaton 2026 winner"/></figure>
<p>Ayush Chaurasia's question was how far tic-tac-toe could grow while still feeling recognizable.</p>
<p>GRIDLOCK answers with matches for up to four players, online rooms, and eleven special moves. Its monetization is built around a shared Credits currency: players can earn through rewarded ads, buy packs, or subscribe, while the core modes remain available for free.</p>
<p>The subscription combines ad removal, recurring credits, and other extras. That gives regular players a different option from someone who only wants an occasional credit pack.</p>
<p>The judges highlighted the thoughtful mix of subscriptions, consumable packs, and ads.</p>
<p><a href="https://apps.apple.com/app/id6808733376" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/short-drama-series-dramasip" target="_blank" rel="noopener noreferrer"><u>DramaSip</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3bf838e3a5219fc1470b7404f3e4efa008af2d85-2400x1260.png" alt="DramaSip — Shipaton 2026 winner"/></figure>
<p>DramaSip serves up short dramas in a vertical feed, with the next episode a swipe away. Vadim Savin gave viewers several ways to keep watching: free episodes, Premium subscriptions, permanent episode unlocks with coins, and rewarded ads. RevenueCat connects those choices to the same customer identity, carrying purchases along as someone moves from guest viewing to an account.</p>
<p>The judges particularly liked the range of revenue streams, with subscriptions, coin purchases, and ads working within the same app.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.dramasip.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/ritmo-dating-sport" target="_blank" rel="noopener noreferrer"><u>Ritmo – Dating &amp; Sport</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d679308e1686c0e7e3f69fc453e036dc51762eb7-2400x1260.png" alt="Ritmo – Dating &amp; Sport — Shipaton 2026 winner"/></figure>
<p>Adel Benzehda kept hearing the same question at run clubs: who's single? Ritmo builds a dating app around that natural overlap, with sport-based profiles, shared activity suggestions, and local clubs. Subscriptions sit alongside individual Boost and Super Like purchases, while the team experiments with pricing and paywalls that appear when someone wants to use a particular feature.</p>
<p>The judges highlighted how closely the subscriptions, Boosts, Super Likes, and contextual paywalls fit into the product.</p>
<p><a href="https://apps.apple.com/app/id6800200502" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.rork.ritmoandroid" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Influencer Award - Nutrition &amp; Healthy Eating: Abbey's Kitchen</h2>
<p>In Abbey Sharp's challenge, the goal was to make satisfying meals easier to put together. Developers were asked to start with what someone already wanted to eat and suggest useful additions, such as protein, fibre, or healthy fats. The emphasis was on enjoying food and feeling satisfied, without calorie counting or rigid meal plans.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/just-add-round-out-your-plate" target="_blank" rel="noopener noreferrer"><u>Just Add</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/bf455e08541fce898a60582512e62be013f086af-2400x1260.png" alt="Just Add — Shipaton 2026 winner"/></figure>
<p>Just Add began with someone close to the creator who found food noise and counting-focused nutrition tools exhausting.</p>
<p>Describe what you feel like eating, take a photo, or say it aloud, and the app suggests three meals built around it. Protein, fibre, and healthy fats come in as useful additions, with shopping help and hands-free cooking to carry the idea through to dinner.</p>
<p>Remembered preferences, a cookbook, and a quick question about how satisfying the meal was help the app learn what someone actually wants to cook again.</p>
<p>The judges liked the approach to meal building, especially the dinner-host feature that includes other people's preferences.</p>
<p><a href="https://apps.apple.com/us/app/just-add-round-out-your-plate/id6804743234" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/satisfed-snap-track-meals" target="_blank" rel="noopener noreferrer"><u>satisfed</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/be9843ac0700a628c454a92b2e901e3e9eecdd77-2400x1260.png" alt="satisfed — Shipaton 2026 winner"/></figure>
<p>Irem Duran and Burak Eren Demir recognized their own experiences of nutrition guilt in Abbey Sharp's challenge. Their answer, satisfed, starts with the meal you already want and suggests easy additions to make it more satisfying. A visual Trio Ring brings protein, fibre, and healthy fats into focus, leaving room for leftovers, restaurant meals, and cravings along the way.</p>
<p>The judges particularly liked the welcoming design, which felt less rigid than other meal trackers and calorie counters.</p>
<p><a href="https://apps.apple.com/us/app/satisfed-snap-track-meals/id6808959617" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/the-plate" target="_blank" rel="noopener noreferrer"><u>The Plate</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0658d01e2925fe6f3c92dd16ab32a5ca98f5026f-2400x1260.png" alt="The Plate — Shipaton 2026 winner"/></figure>
<p>The Plate starts with a useful dinner question: what could I add to this? Describe a meal, take a photo, or say it aloud, and the app suggests additions that account for time, price, and preferences. Ramy Magdy's Egyptian and MENA-aware catalog also makes space for dishes such as koshari, fuul, and molokhia. The food you actually eat belongs here.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.platepatch.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Influencer Award - Productivity: Christopher Lawley</h2>
<p>In Christopher Lawley's challenge, the goal was to make the things you use repeatedly easier to find and reuse. Think of the reply you keep typing, the document you always need, or the image buried in your library. Developers were asked to give that material a fast, focused home that fits naturally into everyday work.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/snipstash-snippet-manager" target="_blank" rel="noopener noreferrer"><u>SnipStash</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e299e2a0794410f5f68b5493cb7967c043cbdf34-2400x1260.png" alt="SnipStash — Shipaton 2026 winner"/></figure>
<p>SnipStash is for the reply you've typed twenty times, the PDF you can never find, and the image you keep scrolling back for.</p>
<p>It keeps reusable material in one library across iPhone, iPad, and Mac, then brings it into reach through a keyboard, quick palette, hotkeys, and Shortcuts. Templates can hold several kinds of content together, so a repeated task takes fewer trips between apps.</p>
<p>Private iCloud sync keeps the library available across devices. At submission, Pro was offered as a one-time universal purchase rather than another subscription to manage.</p>
<p>The judges felt SnipStash delivered exactly what they'd hoped for in the challenge, particularly praising its speed and native interface.</p>
<p><a href="https://apps.apple.com/app/id6802945896" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/ribbons-1" target="_blank" rel="noopener noreferrer"><u>Ribbons</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/25fb728eccc53525f5bece2da5f42a5f0feb0b68-2400x1260.png" alt="Ribbons — Shipaton 2026 winner"/></figure>
<p>Paul Waweru's travel-agent wife kept losing saved photos as they slipped back to their original dates in her library. Ribbons gives those useful bits of information a home organized around what they're for. A Drawer can hold bookings, scans, links, and photos together, with search inside images and PDFs and shared Drawers for the material someone else needs too.</p>
<p>The judges particularly liked shared Drawers, making it easy to keep a useful set of snippets available to someone else.</p>
<p><a href="https://apps.apple.com/ie/app/ribbons-1/id6804689418" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/ledge-clipboard-snippets" target="_blank" rel="noopener noreferrer"><u>Ledge</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0ad34598bc4178fcd142ddb33ceef13eac7263e0-2400x1260.png" alt="Ledge — Shipaton 2026 winner"/></figure>
<p>Copy something new, lose the thing you copied before it. Gaurav Kukreja built Ledge around that everyday annoyance, keeping clipboard history and reusable snippets close at hand on the Mac, with an iPhone companion alongside it. A quick palette handles search and paste, while Halo puts pinned items around the pointer. The things you keep reaching for stay within reach.</p>
<p>The judges particularly liked the capture tab, where having the options together made saving content fast.</p>
<p><a href="https://apps.apple.com/jp/app/ledge-clipboard-snippets/id6815005714" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Influencer Award - Career Coaching: Leadership Heather</h2>
<p>In Leadership Heather's challenge, the goal was to help people practice the conversations they tend to put off. Giving feedback or pushing back on a request can feel very different once another person responds. Developers were asked to make that practice realistic, with useful feedback people could take into the actual conversation.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/rehearse-hard-conversations" target="_blank" rel="noopener noreferrer"><u>Rehearse - Hard Conversations</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a1e5f49e20a120876009947cc595e85c57d73825-2400x1260.png" alt="Rehearse - Hard Conversations — Shipaton 2026 winner"/></figure>
<p>As a new product manager, Akil Saji struggled to push back when his manager promised more work than the team could take on.</p>
<p>Rehearse gives people a place to practice those conversations aloud with an AI counterpart that responds. Feedback points to the line where things changed, and a prep card helps carry the practice into the conversation that matters.</p>
<p>There are 33 prepared scenarios alongside custom situations, with feedback covering clarity, empathy, assertiveness, and structure.</p>
<p>The judges particularly liked the simplicity of Rehearse, which let it focus on doing its main job well.</p>
<p><a href="https://apps.apple.com/us/app/rehearse-hard-conversations/id6812669011" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.rehearse.rehearse" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/bravy" target="_blank" rel="noopener noreferrer"><u>Bravy — The work journal that teaches real conversations</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2b8c752f4cf6c17f513d9ba3c5f413a83e4bbb11-2400x1260.png" alt="Bravy — The work journal that teaches real conversations — Shipaton 2026 winner"/></figure>
<p>Leon Kraus had to give difficult feedback to the working student who'd helped onboard him. Rehearsing in his head hadn't made saying it aloud any easier. Bravy connects a work journal with voice, text, and avatar-based practice, so users can rehearse situations involving their actual colleagues and agreements. Feedback from early testers helped make that personal context central to the app.</p>
<p><a href="https://apps.apple.com/us/app/bravy/id6811919028" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/firmly-practice-hard-talks" target="_blank" rel="noopener noreferrer"><u>Firmly</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a5b50cd1b4a930b9cad9c7e68ccc9c1564ab1326-2400x1260.png" alt="Firmly — Shipaton 2026 winner"/></figure>
<p>It's easy to prepare a good opening line. Staying clear when the other person pushes back takes practice. Firmly gives managers characters who argue, dodge the issue, or appeal to friendship, with different levels of resistance to work through. Debriefs quote the user's own words and suggest what to try next, followed by a check-in after the real conversation.</p>
<p>The judges loved how challenging the practice felt, giving users a chance to work through a genuinely difficult conversation.</p>
<p><a href="https://apps.apple.com/us/app/firmly-practice-hard-talks/id6804925012" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Influencer Award - Gaming: Mr Lewis Blogs Gaming</h2>
<p>Mr Lewis Blogs Gaming's challenge started with a familiar problem: seeing a game you want to play, then forgetting about it. The goal was to help people save discoveries, organize their backlog, and keep track of what they finished. Ratings and sharing would give those collections a life beyond the wishlist.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/game-magnet" target="_blank" rel="noopener noreferrer"><u>Game Magnet</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2a1404050e4746cd833d578bf461403592e44530-2400x1260.png" alt="Game Magnet — Shipaton 2026 winner"/></figure>
<p>Kundan Sai Pannuru kept seeing games in short videos, then forgetting their names when he wanted to play them.</p>
<p>Game Magnet lets you share the clip instead: it identifies the games and saves them to your backlog. From there, shelves, ratings, and ranked lists help turn a fleeting discovery into something you might actually get around to playing.</p>
<p>Clips process in the background, with a notification when the result is ready. That means saving a discovery doesn't have to interrupt watching it.</p>
<p>The judges liked how fully Game Magnet answered the challenge, with a layout they found easy to use.</p>
<p><a href="https://apps.apple.com/tw/app/game-magnet/id6813435828" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/gamenight-what-should-we-play" target="_blank" rel="noopener noreferrer"><u>GameNight</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9c61869d619852e9c75ca1b5befd07994b889cce-2400x1260.png" alt="GameNight — Shipaton 2026 winner"/></figure>
<p>Sometimes choosing a game takes longer than anyone wants to admit. GameNight brings friends' Steam libraries, wishlists, and ratings together to find the overlap, then offers a slot-machine-style pick when the discussion still goes nowhere. Matthew Walmsley also built in reviews, challenges, and leaderboards, giving the group somewhere to keep the conversation going after they've finally picked something.</p>
<p>The judges particularly liked how the rewards and challenges made using the app and getting around to playing games feel fun.</p>
<p><a href="https://apps.apple.com/us/app/gamenight-what-should-we-play/id6762072408" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/slyko-your-game-collection" target="_blank" rel="noopener noreferrer"><u>Slyko</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b5422981f6100ab4cb5b135db3002af488358d6d-2400x1260.png" alt="Slyko — Shipaton 2026 winner"/></figure>
<p>Enzo De la pena wanted the pleasure of keeping a film diary, applied to games. Slyko brings wishlists, reviews, library sync, and public favorites into one place, helping players remember what they've enjoyed and what they want to try. Share a social post and it can identify the game for you, saving that discovery before it disappears down the feed.</p>
<p>The judges appreciated having games from different libraries in one place, saving players from checking across multiple platforms.</p>
<p><a href="https://apps.apple.com/app/id6804737481" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.app.slyko" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Influencer Award - Yoga &amp; Fitness: Simone Sharice</h2>
<p>In Simone Sharice's challenge, the goal was to make choosing how to move feel more manageable. Developers explored ways to account for someone's energy, available time, and recovery needs, then offer a clear plan for the day. The focus was on helping people get started with something that fits how they feel.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/feelgood-movement-menu" target="_blank" rel="noopener noreferrer"><u>FeelGood: Movement Menu</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/cfc86a6bc440b07393eef34ccc4762ba32594aee-2400x1260.png" alt="FeelGood: Movement Menu — Shipaton 2026 winner"/></figure>
<p>Sometimes the hardest part of moving is choosing what to do.</p>
<p>Yusra Salam and Ameena Malik built FeelGood around a short check-in, turning your energy, available time, and desired feeling into a small movement menu. An appetizer, main, and optional sides make the choices approachable; rest counts, and coming back after a break doesn't bring a broken streak with it.</p>
<p>The menu spans Pilates, yoga, strength, walking, stretching, and breathing, with a plain-language reason for each suggestion.</p>
<p>The judges particularly liked the theme and colors, describing the app as inviting, calm, and easy to look at.</p>
<p><a href="https://apps.apple.com/ie/app/feelgood-movement-menu/id6806042303" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/chair-yoga-workout-for-seniors" target="_blank" rel="noopener noreferrer"><u>GO: Chair Yoga for Seniors</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/19645a181f0aebfb7661c4867c472c1866a9db42-2400x1260.png" alt="GO: Chair Yoga for Seniors — Shipaton 2026 winner"/></figure>
<p>Subham Bhalawat wanted older adults to get the same care in fitness programming as everyone else. GO builds a daily plan from mobility goals, movement preferences, and available time, with chair-based and supported-standing sessions. Real trainer videos, previews, and voice guidance make the next step clear. Users get one workout to start, rather than another library to sort through.</p>
<p>The judges highlighted the clean onboarding and how well it was tailored to the intended audience.</p>
<p><a href="https://apps.apple.com/us/app/chair-yoga-workout-for-seniors/id6810185372" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/sadhana-yoga-meditation" target="_blank" rel="noopener noreferrer"><u>Sadhana – Yoga &amp; Meditation</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ae6bf30b12d76fd268b7856926d9f09d5f7d7df3-2400x1260.png" alt="Sadhana – Yoga &amp; Meditation — Shipaton 2026 winner"/></figure>
<p>Sadhana grew out of a yoga teacher's frustration with conflicting instructions and too much choice. It offers one daily Hatha practice combining movement, breathing, and meditation, then asks whether it felt too gentle, just right, or too much. That feedback shapes the next session, with room for rest and a gradual return after a break. Hand-painted artwork completes the calm setting.</p>
<p>The judges particularly liked the hand-painted images, which they found beautiful and calming.</p>
<p><a href="https://apps.apple.com/app/sadhana-yoga-meditation/id6806279084" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Ship Kotlin Everywhere</h2>
<p>JetBrains' Ship Kotlin Everywhere award challenged developers to build a polished experience across platforms using Kotlin Multiplatform or Compose Multiplatform. We looked at how shared technology supported the finished product, from the interface to the work underneath it. The goal was an app that felt considered wherever someone picked it up.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/boatbrawl" target="_blank" rel="noopener noreferrer"><u>BoatBrawl</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/76dcff1ec0d1ff2328dfde42f8557611fa0253ff-2400x1260.png" alt="BoatBrawl — Shipaton 2026 winner"/></figure>
<p>BoatBrawl brings the cheerful chaos of couch multiplayer to friends playing across phones and computers.</p>
<p>Up to eight players steer, fire, and scramble through interactive arenas, with bots and shared-screen play available too. Rehaan Cubes and Ashwin K M used Kotlin Multiplatform and Compose Multiplatform to share the game across platforms, keeping the same rules underneath every battle.</p>
<p>The shared approach covers the interface and much of the rendering and networking, across iOS, Android, Mac, Windows, and web.</p>
<p>The judges were especially impressed by how smoothly BoatBrawl played in the browser.</p>
<p><a href="https://apps.apple.com/app/boatbrawl/id6808540987" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.xcelaudit.boatbrawl" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/afterfade" target="_blank" rel="noopener noreferrer"><u>AfterFade</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/c093d0aca97f87d6800a9fdcf15b8d68165bf6c6-2400x1260.png" alt="AfterFade — Shipaton 2026 winner"/></figure>
<p>Kanon Fujita could look back at photos and remember how a day looked, but not how it sounded. AfterFade turns seven five-second clips into a music video, composing a lo-fi soundtrack from their colors and ambient sound. Kotlin and Compose share the interface and music engine across iOS and Android, with composition happening privately on the phone.</p>
<p>The judges particularly liked the ingenuity and creativity behind the idea.</p>
<p><a href="https://apps.apple.com/us/app/afterfade/id6800247416" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.afterfade.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/lumeo-your-second-brain" target="_blank" rel="noopener noreferrer"><u>Lumeo</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3e23c4ca176bbb01eb4229fea403f1dbd190f7d2-2400x1260.png" alt="Lumeo — Shipaton 2026 winner"/></figure>
<p>Some thoughts arrive while you're walking, commuting, or doing anything except sitting at a keyboard. Lumeo lets you speak them, then transcribes, organizes, and connects them with earlier notes. Piotr Prus uses a constellation view to make those links visible. Kotlin runs through the project, from shared mobile code to the backend and even the module that rendered its demo film.</p>
<p>The judges singled out the project's contributions to the Kotlin community.</p>
<p><a href="https://apps.apple.com/us/app/lumeo-your-second-brain/id6811937018" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=dev.piotrprus.lumeo" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Most Viral App</h2>
<p>Noise's Most Viral App award focused on the creative work of getting an app noticed. Developers experimented with videos, hooks, and formats that could introduce the product to new audiences. We looked for a clear connection between the content people wanted to watch and the value waiting for them inside the app.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/trazo-golf-ball-tracer" target="_blank" rel="noopener noreferrer"><u>Trazo: Golf Ball Tracer</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/31c0020bccb0f5081c956170e40cbe4a73200de8-2400x1260.png" alt="Trazo: Golf Ball Tracer — Shipaton 2026 winner"/></figure>
<p>Trazo gives amateur golf videos the ball-flight line usually seen on a broadcast.</p>
<p>Julian Praest built the processing to run on the phone, starting from two selections of the ball. For the app's marketing, golf humor and advice became repeatable formats, with the tracer reveal giving viewers a reason to care about the product at the end.</p>
<p>The team evaluated formats for how well they could attract attention, be repeated, and lead to interest in the app, then stopped weaker ideas and kept working on stronger ones.</p>
<p>The judges highlighted the team's understanding of how to use organic content to introduce people to the app.</p>
<p><a href="https://apps.apple.com/us/app/trazo-golf-ball-tracer/id6803598502" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/platediary-food-photo-journal" target="_blank" rel="noopener noreferrer"><u>PlateDiary – Food Journaling</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/28dd1001f16ed57aa0e8456ac490a4e67a18000a-2400x1260.png" alt="PlateDiary – Food Journaling — Shipaton 2026 winner"/></figure>
<p>Most of us have a few good meals buried somewhere in our camera roll. PlateDiary gives those photos a home, organizing food and drink into a visual journal worth returning to. John Michael Degojas carried that familiar behavior into creator campaigns, testing hooks and formats that move from a recognizable habit to the pleasure of seeing the finished collection.</p>
<p>The judges saw the value in turning a food photo into a saved memory, particularly with its location on a map.</p>
<p><a href="https://apps.apple.com/us/app/platediary-food-photo-journal/id6787356977" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.platediary.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/water-tracker-quenched" target="_blank" rel="noopener noreferrer"><u>Quenched – Water Tracker</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/fa1a045444f5234025ae5338ccba2588dd23f1e9-2400x1260.png" alt="Quenched – Water Tracker — Shipaton 2026 winner"/></figure>
<p>Jason Clardy built Quenched after a doctor's visit revealed he was dehydrated despite feeling normal. The app brings together a personal water goal, reminders, widgets, and quick logging, with water that reacts as you tilt the phone. That little physical detail gives the daily habit some personality, while marketing experiments tested different ways to introduce it to new users.</p>
<p><a href="https://apps.apple.com/us/app/water-tracker-quenched/id6776401215" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=co.swiftfox.quenched" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Best App for Galaxy</h2>
<p>Samsung's Best App for Galaxy award asked developers to think about what their app could do on Galaxy devices. Publishing to the Galaxy Store was part of the challenge, along with adapting the experience to the hardware. Foldables offered particular room to explore how layouts and interactions could change with the screen.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/grandfolio-optcg-scanner" target="_blank" rel="noopener noreferrer"><u>Grandfolio: OPTCG Card Game Scanner</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/30dcbaf0f7e3a51957438ba04209e7946ea145bf-2400x1260.png" alt="Grandfolio: OPTCG Card Game Scanner — Shipaton 2026 winner"/></figure>
<p>For One Piece Card Game collectors, two cards that look almost the same can be very different finds.</p>
<p>Aris Guimerá Orozco built Grandfolio to identify exact artwork variants, manage duplicates, and track a collection's prices and progress. Its Galaxy version adapts across folded, tabletop, and open-screen layouts, giving a growing collection more room when the device has it.</p>
<p>A dedicated Galaxy Store build also connects Samsung billing to RevenueCat, making distribution and purchasing part of the platform work.</p>
<p>The judges particularly liked the polish and foldable optimization, applied to a distinctive collecting use case.</p>
<p><a href="https://apps.apple.com/us/app/grandfolio-optcg-scanner/id6798271181" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.aristidevs.optcgtracker" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/rainroam" target="_blank" rel="noopener noreferrer"><u>Rainroam</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/97b7d1bafe7af26d15b10f99daf1ecd9645eb6e4-2400x1260.png" alt="Rainroam — Shipaton 2026 winner"/></figure>
<p>Rainroam lets players switch from a 3D adventure to pixel art in the middle of a fight. Both views run the same game, so the islands, quests, and bosses carry on without a restart. Hanna Shcharbakova and Yauheni Draitsau also worked through Galaxy folding, unfolding, and multi-window behavior, bringing that flexibility to the device as well as the art style.</p>
<p><a href="https://apps.apple.com/app/id6813862126" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/sugarcurve-gdm-coach-tracker" target="_blank" rel="noopener noreferrer"><u>SugarCurve: Gestational Diabetes Coach, Meal Planner &amp; Glucose Tracker</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2f7ed0ec00e297f63cac424b87eda3090359e332-2400x1260.png" alt="SugarCurve: Gestational Diabetes Coach, Meal Planner &amp; Glucose Tracker — Shipaton 2026 winner"/></figure>
<p>A husband-and-wife team's experience of gestational diabetes became the starting point for SugarCurve. Glucose records, meal planning, food information, and reminders follow the journey through pregnancy and postpartum. On Galaxy foldables, readings, targets, and trends sit side by side, using the extra screen space to make a personal record easier to look through.</p>
<p>The judges liked the overall presentation and noted the work on foldable optimization.</p>
<p><a href="https://apps.apple.com/app/id6793947158" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.dalolabs.sugarcurve" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h2>Replit's Idea to Income</h2>
<p>Replit's Idea to Income award followed the journey from an idea to an app someone could download and pay for. Developers used Replit and RevenueCat to build the product, get it into users' hands, and set up purchases. The category celebrated taking those first practical steps toward an app business.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/let-s-get-support-by-3dadz" target="_blank" rel="noopener noreferrer"><u>Let’s Get Support by 3Dadz</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/37404b28cca5b7014e461a77992c76950e8cf3c9-2400x1260.png" alt="Let’s Get Support by 3Dadz — Shipaton 2026 winner"/></figure>
<p>Let's Get Support focuses on the people affected by someone else's addiction: partners, parents, and friends who need support themselves.</p>
<p>Joshua Burgess and Aaryn B. built it from community conversations in Abbotsford and the Fraser Valley, bringing together check-ins, journaling, boundary-setting tools, and local resources. Replit helped the pair turn those conversations into a live app, with the core tools available for free.</p>
<p>Optional Plus purchases support the app, while journals and weekly calculations stay on the device. The team's submission describes an early launch with working purchases and community-led distribution.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.letsgetsupport.app" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/quarterday-home-maintenance" target="_blank" rel="noopener noreferrer"><u>Quarterday</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/5968fac44aca7fb87be9f5d72afce087bb2c28fa-2400x1260.png" alt="Quarterday — Shipaton 2026 winner"/></figure>
<p>Home maintenance has a habit of becoming urgent just after you've forgotten about it. Quarterday starts with a quiz about your home, builds an annual schedule, and shows what's due now. Adrian Ferguson used Replit to get the first version working, then added a logbook and photo receipts to help homeowners keep track of the jobs they've actually done.</p>
<p><a href="https://apps.apple.com/us/app/quarterday-home-maintenance/id6802919249" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.midknight.quarterday" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/family-prayer-for-muslims" target="_blank" rel="noopener noreferrer"><u>Family Prayer</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4c072c5ec777c496c06c8517233caa69671d63e4-2400x1260.png" alt="Family Prayer — Shipaton 2026 winner"/></figure>
<p>Family Prayer began with a mother's experiments in helping her sons build a daily prayer habit. Children get stories, achievements, and points, while parents choose real-world rewards that suit their family. Replit, Expo, and RevenueCat helped a first-time builder take that idea to the App Store. At submission, the team reported its first two annual subscribers.</p>
<p><a href="https://apps.apple.com/us/app/family-prayer-for-muslims/id6759014479" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>The Growth Loop Award</h2>
<p>Layers' Growth Loop Award was about learning what helps an app grow. Developers were asked to run focused experiments, measure what happened, and use the results to decide what to try next. An experiment that challenged the original plan could be just as useful as one that confirmed it.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/poquitotalk-bocas-del-toro" target="_blank" rel="noopener noreferrer"><u>PoquitoTalk: Bocas del Toro</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d40b3a5884fca1cac4c826bfb188a91670199ead-2400x1260.png" alt="PoquitoTalk: Bocas del Toro — Shipaton 2026 winner"/></figure>
<p>After 14 years in Bocas del Toro, Dorien Van den Abbeele had seen how often textbook Spanish misses the way people actually speak there.</p>
<p>PoquitoTalk turns English into local Spanish voice notes for WhatsApp and helps translate the replies. Recipients don't need the app, making it useful in a community where a voice message is often how everyday things get arranged.</p>
<p>Dorien's growth experiments compared automated clips with handmade local content. The submission also records where the evidence was inconclusive and where measurement needed fixing.</p>
<p>The judges highlighted the team's extensive experimentation, along with its openness about a measurement bug that the Layers data confirmed.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.heroapps.poquitotalk" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/better-yoga-pilates-workout" target="_blank" rel="noopener noreferrer"><u>Better Yoga</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/57af77ac07b97a42ba972ac3fc8f2a03d606add3-2400x1260.png" alt="Better Yoga — Shipaton 2026 winner"/></figure>
<p>Better Yoga turns a quick check-in about energy, soreness, and time into one manageable yoga or Pilates session. Fakher Hakim applied the same willingness to adapt to the app's marketing, testing humor, product demos, and rest-positive messages through Layers. When the results challenged the original idea, the messaging changed. The submission makes the learning visible, including the surprises.</p>
<p>The judges appreciated that the team followed through on its experiment even when the results disproved the original hypothesis.</p>
<p><a href="https://apps.apple.com/us/app/id6798437282" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.better.yoga.pilates" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/green-hour-time-outside" target="_blank" rel="noopener noreferrer"><u>Green Hour: Time Outside</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4209c939e794ae60c4081dc91d3ec4601379a9f9-2400x1260.png" alt="Green Hour: Time Outside — Shipaton 2026 winner"/></figure>
<p>Natasha Wilson found that short breaks outside helped her days, but winter made the habit easy to lose. Green Hour records mood before and after time outdoors, helping users see their own pattern. Its Layers experiments bring similar care to growth, separating social attention and store visits from installs the team could actually attribute. The early learning stays grounded in the evidence.</p>
<p>The judges singled out the thorough use of the platform, including nine experiments, separate channel links, audience personas, and bot filtering.</p>
<p><a href="https://apps.apple.com/app/id6793054267" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Keep Them Coming Back Award</h2>
<p>OneSignal's Keep Them Coming Back award asked developers to give people a useful reason to return. That could be an alert they'd requested, a timely update, or a message connected to their own progress. We looked at how communication supported the app's purpose and continued to help between visits.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/parky-theme-park-tracker" target="_blank" rel="noopener noreferrer"><u>Parky!</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/265a7a96ed84bf373dfbe406763f8a1017df4c8e-2400x1260.png" alt="Parky! — Shipaton 2026 winner"/></figure>
<p>Chase Van Derrick brought his experience as a Disney Cast Member to Parky!, a companion for Disney and Universal visits.</p>
<p>It combines wait times, dining availability, and park-day memories with the kind of timely help a guest might ask for at a front desk. Dining alerts can even become a conversation from the Lock Screen, where you can ask about another time or restaurant.</p>
<p>Requested alerts, queue Live Activities, and recap messages give the app reasons to get in touch that fit the visit, from planning a meal to remembering the day afterward.</p>
<p>The judges particularly liked the interactive notifications, including the ability to reply directly.</p>
<p><a href="https://apps.apple.com/us/app/parky-theme-park-tracker/id6800289506" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/inference-your-energy-agent" target="_blank" rel="noopener noreferrer"><u>Inference</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e34c4bf172ea0eace2f60b45637476b76e3ced27-2400x1260.png" alt="Inference — Shipaton 2026 winner"/></figure>
<p>Samuel Anozie found that budgeting money and time still left a harder question: where was his energy going? Inference uses journal entries to spot patterns around work, health, relationships, and rest. OneSignal carries follow-ups through push, in-app messages, Live Activities, and email, including a monthly letter that connects the user's history with the goals they're working toward.</p>
<p>The judges highlighted the thoughtful use of Live Activities, in-app messages, emails, and journeys to keep the conversation going between sessions.</p>
<p><a href="https://apps.apple.com/us/app/inference-your-energy-agent/id6778176668" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/tikku-to-do-block-apps" target="_blank" rel="noopener noreferrer"><u>Tikku</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9f4f929c837fcf42ba6d353c974775b3f5ba0b39-2400x1260.png" alt="Tikku — Shipaton 2026 winner"/></figure>
<p>Tikku joins two things that make more sense together: choosing what to do and protecting the time to do it. Tasks and routines connect directly to distraction blocking, while OneSignal messages wait until focus sessions finish. The device adds personal Screen Time figures locally, keeping those details on the phone. Even its reminders respect the time someone set aside.</p>
<p>The judges praised both the polish of the app and its use of OneSignal, from rich push notifications and journeys to Live Activities and in-app messages.</p>
<p><a href="https://apps.apple.com/us/app/tikku-to-do-block-apps/id6790735660" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Funnel Vision Award</h2>
<p>Stripe's Funnel Vision Award focused on the journey from the web into an app. Developers built payment funnels with RevenueCat Funnels and Stripe, connecting an introduction to the product with checkout and access in the app. The challenge was to make that whole path work, from the first question to a completed purchase.</p>
<h3>1st Place: <a href="https://apps.shipaton.com/app/in-your-own-words" target="_blank" rel="noopener noreferrer"><u>In Your Own Words</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d5a8088c735b09993eb8cdd53f5f6be8bfe78b78-2400x1260.png" alt="In Your Own Words — Shipaton 2026 winner"/></figure>
<p>In Your Own Words gives people a minute to practice saying something they might otherwise avoid.</p>
<p>Natasha Wilson's app offers real-world conversation scenarios and feedback on pace, hesitation, and filler words, with speech analysis happening on the phone. Its web quiz starts that journey before download, guiding visitors to a relevant practice deck and letting a web purchase unlock it in the app.</p>
<p>The submission documented a £2.99 web purchase successfully unlocking the right deck on an anonymous install, a small but complete demonstration of the path from quiz to practice.</p>
<p><a href="https://apps.apple.com/app/id6801430533" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h3>2nd Place: <a href="https://apps.shipaton.com/app/kettlebell-body-home-workouts" target="_blank" rel="noopener noreferrer"><u>Kettlebell Body</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a1294f17eacf9033589a0ac0f7d303593f6b90d2-2400x1260.png" alt="Kettlebell Body — Shipaton 2026 winner"/></figure>
<p>Kettlebell Body gives kettlebell training the room it deserves as a discipline of its own. Built with coach Trevor Shan, it pairs an eight-week Foundation program with weekly workouts, coaching videos, timers, and progress tools. Different formats, from EMOM to ladders, get their own structure, helping users follow a program beyond picking an exercise from a general fitness library.</p>
<p><a href="https://apps.apple.com/app/id6757338021" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<p><a href="https://play.google.com/store/apps/details?id=com.rule25.instinct" target="_blank" rel="noopener noreferrer"><u>Google Play</u></a></p>
<h3>3rd Place: <a href="https://apps.shipaton.com/app/bravy" target="_blank" rel="noopener noreferrer"><u>Bravy — The work journal that teaches real conversations</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2b8c752f4cf6c17f513d9ba3c5f413a83e4bbb11-2400x1260.png" alt="Bravy — The work journal that teaches real conversations — Shipaton 2026 winner"/></figure>
<p>Bravy gives visitors something useful before asking them to subscribe. Its <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels"><u>web-to-app funnel</u></a> starts with a three-question quiz, then offers an opening line and a practical conversation tip tailored to the result. Monthly, three-month, and annual plans follow, with a Stripe-backed purchase and redemption link unlocking Pro in the app without an account. The first bit of coaching happens before download.</p>
<p><a href="https://apps.apple.com/us/app/bravy/id6811919028" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>Conflict of Interest Award</h2>
<p>Last year was the first time we allowed RevenueCat employees and Shipaton sponsor employees to participate in the hackathon. That year we only got two submissions so it was refreshing to see that this year we increased that number to 23 submissions. Most were RevenueCat employees (some of them even submitting more than one app). </p>
<p>This year we decided to open the judging of these apps to the other Shipaton participants. Seemed only fair that after judging the apps you built, you should judge the apps we built. Judging was done through a custom judging platform where you were shown two apps at a time, and had to pick the better one. After a week, we had a pretty clear winner:</p>
<h3>Winner: <a href="https://apps.shipaton.com/app/friendpire" target="_blank" rel="noopener noreferrer"><u>Friendpire</u></a></h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/8ec98eaafdacb6d7dd30ffeb8385ace83a3b406f-2400x1260.png" alt="Friendpire — Shipaton 2026 winner"/></figure>
<p>RevenueCat employee Erik MacInnis wanted to bring back Catan nights with friends who could rarely find time to play together.</p>
<p>Friendpire turns that idea into a shared hex-tile world where friends gather resources, build farms and docks, and expand their little empires. Erik used Fable in Cursor to work through the art direction, mechanics, and social features, shaping a familiar board-game ritual into something easier to fit around everyday life.</p>
<p><a href="https://apps.apple.com/app/id6804065119" target="_blank" rel="noopener noreferrer"><u>App Store</u></a></p>
<h2>See you next year</h2>
<p>Every year, Shipaton keeps growing and you’ve once again outdone yourself. We saw so many great apps, and it’s a shame we can’t award every great app. The work all of the Shipaton participants put in is nothing short of inspiring. </p>
<p>Finally, from all of Team RevenueCat: congratulations to all the winners! And thank you to everyone who contributed, mentored, or simply encouraged someone else to ship. While it might be the end of Shipaton 2026, it's just the beginning of your apps. We can't wait to see where they go.</p>
<p>How did you find Shipaton? Did you have fun? Were there things you wish we had done differently?</p>
<p>We'd love to hear your thoughts. Let us know on <a href="https://x.com/RevenueCat" target="_blank" rel="noopener noreferrer"><u>Twitter</u></a> or <a href="https://www.linkedin.com/company/revenuecat/" target="_blank" rel="noopener noreferrer"><u>LinkedIn</u></a>, and keep an eye out for a survey we'll be sending to all participants to gather your feedback. </p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Building apps with heart: what Noom, Numo, and Opal get right ]]></title>
      <link>https://www.revenuecat.com/blog/growth/apps-with-heart</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/apps-with-heart</guid>
      <pubDate>Mon, 19 Oct 2026 10:04:58 GMT</pubDate>
      <dc:creator><![CDATA[Olha Yohansen]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[A perfectly-optimized funnel gets users to the paywall, but affinity is what keeps them in the app]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/a18e82727663388f3a5a3ad152f4f609bef63c60-3200x1700.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>In my last article I <a href="https://www.revenuecat.com/blog/growth/optimized-app-forgettable">spoke about how many apps have now optimized to a point of being generic</a>, and frankly, forgettable. Builders borrow functionality and patterns from the winning apps, and eventually most apps look the same (just look at every weightloss or period-tracking app for examples). To stand out, you need <strong>heart</strong>: an authentic understanding of a specific user, expressed in their own language.</p>
<p>Now, my argument is easy to agree with, but it’s harder to act on. Heart alone won’t get you to the top of the App Store. The apps that win in this environment need three things working at the same time:</p>
<ul>
<li><strong>Heart: </strong>something that resonates — real problems, messaging that lands</li>
<li><strong>Distribution: </strong>well-tuned paid acquisition engine and product loop that earns organic growth</li>
<li><strong>Monetization: </strong>an onboarding flow, paywall, and pricing strategy that systematically converts attention into revenue</li>
</ul>
<p>Pick one and you are halfway there. Pick all three and you are dangerous. Here's what heart looks like when apps actually do it — and what happens when they don't.</p>
<h2><strong>What ‘heart’ actually means in practice</strong></h2>
<p>I want to be careful here, because ‘heart’ is the kind of word that sounds meaningful in a conference talk, and fuzzy in a product review. Here is what I mean when I’m talking about building with heart.</p>
<h3><strong>1. A sharp understanding of a specific user</strong></h3>
<p>Not &quot;women aged 25–40 interested in wellness&quot;. Something much more specific: a first-time mom who is three months postpartum, exhausted, and looking for something that fits into five-minute windows between feeds. Or someone with ADHD who has tried 17 productivity apps and abandoned all of them because the onboarding was too long.</p>
<p>The specificity shapes everything: the tone, the visuals, the questions you ask, the order you ask them in. The more specific the mental model of who you are building for, the more everything else follows naturally.</p>
<aside class="tip"><strong>Practical tip</strong><p>You can test this throughout your funnel in one iteration. I've seen such a re-do bring 5–7% conversion uplifts to paid onboarding paywall.</p></aside>
<h3><strong>2. Speaking their language</strong></h3>
<p>This sounds obvious, but is almost never done well. Speaking your user's language does not mean labelling them (&quot;for busy moms!&quot;). It means formulating onboarding questions the way the user would actually think about the problem: not the way a product manager would write a spec.</p>
<p>A good test: read your first onboarding screen out loud. Does it sound like something a person would say? Or does it sound like a SaaS landing page?</p>
<p>A personal example: when I was leading the onboarding team at Flo Health, I suggested adding an interrupting popup during the loader screen that runs right before the paywall. The popup asked: &quot;Do you drink more than two cups of coffee a day?&quot;</p>
<p>The question came directly from my own experience. I am a mother, and I was pregnant before. Pregnant women are told early and often: no more than two cups of caffeine per day. It is one of those rules that becomes part of how you think. When I was pregnant, I was counting. Every day.</p>
<p>So when we were testing copy for the interrupting popups on the web funnel, I threw this one into the mix. It stuck. From what I can tell, nothing has beaten it since.</p>
<p>Can you guess why the second popup asked about chocolate?</p>
<p>Chocolate also contains caffeine. Most people do not think about this. Pregnant women and women trying to conceive do, because they are already tracking everything. The moment a user sees that question, something clicks: <em>this app knows what I know</em>. It is the feeling of being seen by someone who has been in your exact situation — not a demographic guess.</p>
<p>That is what speaking the user's language actually means. Not clever copywriting, but a question that only makes sense if you genuinely understand the specific situation your user is in. And in this case, I understood it because I had lived it.</p>
<h3><strong>3. Small moments of delight</strong></h3>
<p>These are the things that die in the roadmap meeting because someone asks &quot;what is the point in this?&quot; and nobody had a good answer. A small animation that responds to user input. Copy that has a point of view. A screen that acknowledges something specific about what the user just told you: not generically, but in a way that shows you were paying attention.</p>
<p>These moments are often cheap to build. But their effect on retention is significant. I've seen 10% lift in onboarding paywall conversion from such <em>aha!</em> moments.</p>
<aside class="tip"><strong>Pro tip</strong><p>Test in blocks of logical sequences — small changes alone rarely deliver detectable impact.</p></aside>
<h3><strong>4. Early </strong><em><strong>aha!</strong></em><strong> moments</strong></h3>
<p>This is where I spend most of my time when working with apps, and it is where the ROI is highest.</p>
<p>An <em>aha!</em> moment is when the user thinks: “ah, this is exactly what I need”. It is the point where curiosity becomes commitment. The mistake most apps make is placing it too late: after signup, after onboarding, sometimes only after a purchase.</p>
<p>In B2C apps, <a href="https://www.revenuecat.com/blog/growth/paywall-placement">the first </a><em><a href="https://www.revenuecat.com/blog/growth/paywall-placement">aha!</a></em><a href="https://www.revenuecat.com/blog/growth/paywall-placement"> moment should happen before the paywall</a>. Often before signup, and ideally in the ad itself.</p>
<h2><strong>5 types of </strong><em><strong>aha! </strong></em><strong>moment to try</strong></h2>
<p>Before we move onto a framework for building apps with heart, let’s dig a little deeper into the forms that these heartfelt <em>aha! </em>moments can take.</p>
<h3><strong>Calculations and projections</strong></h3>
<p>The user inputs something and gets a meaningful, personalized result back. <a href="https://www.revenuecat.com/blog/growth/web-to-app-onboarding-funnel">Noom built much of its reputation on this</a>: <a href="https://www.revenuecat.com/blog/growth/why-your-onboarding-experience-might-be-too-short">after a long onboarding</a> asking about weight, goal date, and lifestyle, the user gets a loader followed by a graph showing exactly when they will reach their target weight. The user thinks &quot;Aha, I can lose this amount by that date. Where do I sign up?&quot; Dozens of apps have since copied this sequence, and it still works, because the graph <strong>transforms a generic product into something that feels built for this specific person.</strong></p>
<p>Another weightloss app takes the same principle further by building the <em>aha!</em> across several screens rather than one. When the user enters their weight, a BMI traffic-light scale appears in real time, making the concept easier to understand. The user thinks &quot;Aha, I am in the orange zone.&quot; </p>
<p>The next screen pre-fills a reasonable weight loss goal — not the aggressive one the user might have entered themselves — and gives feedback if the user insists on a dangerous weight loss goal: &quot;Aha, they’re not going to sell me an unrealistic target.&quot; Then the graph arrives. By the time the paywall appears, the user feels the product is already looking out for their best interest and working for them. Each screen adds a layer of trust before the ask.</p>
<h3><strong>Seamless transitions into the product itself</strong></h3>
<p>Numo, an ADHD management app, asks users during onboarding about the tasks they keep postponing — the exact struggle someone with ADHD relates to. How do they know? The users have told them.</p>
<p>At the end of the flow, it reveals that those answers have already been turned into tasks inside the app, with reminders pre-set. The user goes from just ‘answering questions’ to feeling that the app is already personalized to their life and working to help them. This <em>aha! </em>moment means they’re not being asked to pay for something they <em>might</em> like; they’re being asked to keep something they now already have.</p>
<h3><em><strong>Aha!</strong></em><strong> moments that start in the ad</strong></h3>
<p>Simply Sing does something rare: the <em>aha!</em> begins before the user even opens the app. Their ads show someone who cannot sing; another person tells them to do simple actions they need to do right now: say ‘hey, hey, hey’ in their loudest voice, hum etc; the song immediately adjusts to the user's voice.</p>
<p>By the time the user taps through to onboarding, they’ve already felt the product's core promise. The onboarding then confirms it, guiding them through vocal range detection, after which all available songs are adjusted to their range. They think: &quot;Aha, I am an alto and can sing these songs.” While most apps treat the ad as traffic and the app as the goal, Simply Sing treats the ad and the onboarding as one continuous experience.</p>
<h3><strong>The value proposition itself as the </strong><em><strong>aha! </strong></em><strong>moment</strong></h3>
<p>Opal is an app that blocks distracting apps from the user. To illustrate this, their onboarding skips the elaborate quiz format and shows users, in plain numbers, how much time they lose to their phone — and how much they could get back. No graph. No personalized projection. Just the number, framed as time: years of your life.</p>
<p>The user thinks: &quot;Aha, that’s how much time I’m getting back with this app.” It works because the <em>aha!</em> is not about product features; it’s about connecting emotionally, and making the cost of not using the product feel real.</p>
<h3><strong>Loaders and processing screens</strong></h3>
<p>Some people say they hate loaders, but the data says otherwise. A loader that implies the system is actively working on the user's behalf — building their plan, processing their answers — creates anticipation even when the calculation is instant.</p>
<p>What all of these have in common is that the user <em>feels</em> something before they are asked to pay. They are connecting with the value of the product, and feel the product understands them in return.</p>
<p><em>Aha!</em> moments are rarely a single screen. Flo Health's opening sequence spans four screens to route the user to the correct product mode, while simultaneously making them feel understood. It’s the same with the BMI screens mentioned earlier. The overall effect is one <em>aha!</em>, but it is built from a series of small confirmations. When mapping your own onboarding, look for the cluster of screens where something shifts for the user, not just the single moment.</p>
<h2><strong>Why most apps still feel empty (even when optimized)</strong></h2>
<p>Here comes the uncomfortable part. I’ve said this to clients in the past, and I’m sure I’ll say it again. Maybe you’re doing all of these things. That’s fine; awareness is a starting point.</p>
<p>A lot of apps are well-optimized. Clean funnel, tested copy, best-practice paywall, A/B tests run. Yet the app still feels like nothing. There’s content, there’s onboarding, but it just feels… <strong>empty</strong>.</p>
<p>Here’s what I think is happening:</p>
<p><strong>Optimized funnels with no emotional hook: </strong>the flow moves users efficiently from screen to screen, but there’s no moment where they feel understood. The questions are generic. The responses are generic. The user arrives at the paywall having been processed, not engaged.</p>
<p><strong>Surface-level personalization:</strong> the onboarding collects the user's name, age, and goal. Then shows them a paywall that is identical to every other user's paywall. The personalization collected data but did not use it. Users notice this mismatch — maybe not consciously, but it registers. It damages trust precisely because the app set the expectation of something tailored, then delivered something generic.</p>
<p><strong>Paywalls before value:</strong> a user downloads an app, answers four questions, and hits a paywall before they have experienced anything. Early paywalls assume the user arrived with enough intent to pay before seeing what they are paying for. In reality, most users aren’t ready to pay — they’re curious. The app needs to bridge that curiosity gap before asking for money.</p>
<p><strong>Same messaging everywhere:</strong> across the onboarding, the paywall, the <a href="https://www.revenuecat.com/blog/growth/post-purchase-screen">post-purchase screen</a>, and the first few in-app experiences, the copy is interchangeable. There is no voice. No point of view. It could belong to any app in the category.</p>
<p>The industry spent years (and a lot of money) optimizing for conversion rates. But in doing so, it has quietly stripped out the things that make users stay. We’ve optimized our way to being forgettable. A high paywall conversion rate is not the goal — high <strong>LTV</strong> is. And LTV is earned by making users feel, early and repeatedly, <strong>that the product is genuinely for them</strong>.</p>
<h2><strong>The bridge: heart + method framework</strong></h2>
<p>Heart without method is invisible. You can build the most thoughtful, specific, emotionally-resonant app in your category. But if no one finds it, or if the people who find it cannot convert, it doesn’t matter.</p>
<p>Likewise, method without heart is forgettable. You might run a clean acquisition funnel, have a well-tested paywall, a solid pricing strategy. If the product feels like every other product in the category, you are just another app in the same auction, competing on who spends more. Some apps do win that way — but only if the creatives are as strong as the funnel, and the budget can sustain it.</p>
<p>Don’t get me wrong, acquisition is a valid strategy. But it isn’t a moat. Users convert, then leave. The apps that will keep users long-term are the ones that have heart <em>and </em>method. Right now, most apps have one of the two. You probably know which it is.</p>
<p>So how do you get both? Here’s my framework for building apps that feel like something, <em>and </em>convert. It’s not a complete growth system, but it is a starting point for connecting the ‘heart’ work to the ‘method’ work.</p>
<h3><strong>1. Design early </strong><em><strong>aha!</strong></em><strong> moments before the paywall</strong></h3>
<p>Map your onboarding flow. Mark the point where a user first experiences genuine value. If that point is after the paywall, your job is to move it earlier.</p>
<p>Ask yourself: what is the smallest thing we could show or give the user (a result, a projection, a personalized output) that would make them feel like the product is already working for them? Then build it before the paywall.</p>
<p>The benchmark I suggest aiming for is to <strong>retain at least 75% of users from the first screen to the second</strong>. If you’re retaining less than that, the first screen is not confirming that users are in the right place fast enough. And if they don’t feel confirmed in the first two screens, they will not reach the <em>aha!</em> moment you built further in the flow.</p>
<h3><strong>2. Let onboarding feel human</strong></h3>
<p>Bring back the things that got killed in previous roadmap meetings because they were hard to measure: question phrasing. Tone of voice. A moment of warmth when a user shares something personal. A screen that doesn’t just collect information but acknowledges it.</p>
<p>These are the differences between a user who converts and stays, and one who converts and churns within the first billing cycle. The former tells their friends. The latter leaves a one-star review because they forgot they subscribed.</p>
<p>A practical starting point is to <a href="https://www.revenuecat.com/blog/growth/five-minute-journal-onboarding-redesign-arpu">rewrite every onboarding question in the voice of the user</a>, not the voice of the product. Then read the flow aloud. Fix anything that sounds like copy. Repeat until it sounds like a real user.</p>
<h3><strong>3. Use onboarding to tell the story — whatever got them there</strong></h3>
<p>Good onboarding does two things at the same time. It tells the user this is for them. And it sells them the better version of their life if they hire this product. When it does both, you get conversion and retention together, not one at the cost of the other.</p>
<p>This is also why your creatives might not always need to be perfectly on-brand to work. Some of the best-performing ads I have seen look almost random — images of discharge, self conscious mirror selfies, things you would never put on a brand moodboard. But they perform because they trigger a deep fear, a curiosity, a problem, or a life moment the user recognizes. The thing they clicked on at 11pm without fully knowing why. I’ll be honest: I have been almost embarrassed by how well some of these perform on CPA. But they do.</p>
<p>If the creative gets the click, then the onboarding tells the story. A user who arrived with low intent can become a paying subscriber — <em>if</em> the onboarding picks them up and walks them toward the better version of themselves.</p>
<p>The creative is the first sentence. The onboarding is the rest of the story.</p>
<h3><strong>4. Measure heart experiments on conversion and LTV</strong></h3>
<p>When testing heart-led changes to your onboarding, do not measure conversion rate alone.</p>
<p>Sometimes the conversion stays flat. Price plans redistribute, revenue looks the same on paper, you conclude that the experiment did nothing.</p>
<p>Then you look at LTV 60 days later and users are staying longer. The onboarding sold them on <em>committing</em>, not just paying. That’s a win — it just didn’t show up when you expected it to.</p>
<p>TL;DR: <a href="https://www.revenuecat.com/blog/growth/hard-paywall-vs-freemium">measure paywall conversion and cohort LTV together</a>. That is where the real answer lives.</p>
<h2><strong>Let’s build heart back into apps</strong></h2>
<p>Building has never been easier. Getting noticed has never been harder. The apps that will cut through are not necessarily the most technically sophisticated, or the ones with the largest acquisition budgets. They’ll be the ones that make users feel seen, and give them a reason to stay.</p>
<p>Delight is the one thing you can't A/B test your competitor into having. You can copy a paywall layout in an afternoon — you cannot copy a product that genuinely understands its users.</p>
<p>Start by building the heart in. Then build the method around it.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[How to retain users in your AI app: lessons from Celebs - Celebrity Look Alike]]></title>
      <link>https://www.revenuecat.com/blog/growth/celebs-ai-app-retention</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/celebs-ai-app-retention</guid>
      <pubDate>Mon, 19 Oct 2026 09:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Margarita Loktionova]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[How does an AI celebrity lookalike app keep users coming back? We spoke with the team behind Celebs about what they've learned from hundreds of experiments and how their approach to onboarding, free trials, and product quality helps them achieve strong retention.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/b978e659efe3d76efe70545844a9ed6f334c04fb-1408x736.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>AI apps earn more per payer than other subscription apps, but they churn faster, and retention varies a lot from one AI app to the next. Yet, <a href="https://www.revenuecat.com/blog/growth/ai-app-retention-study">our recent research</a> found that AI apps in the top 30% for retention keep as many subscribers as traditional apps, or even more.</p>
<p>You'd think that only applies to obvious habit-building use cases, like utilities, fitness, or diet apps. At least that's what I assumed.</p>
<p>But guess what? It's not that straightforward.</p>
<p>I spoke with Patrick Stuart-Constant, Sociaaal's CEO, and Pablo Sánchez, its founding head of product. Their team is behind <a href="https://apps.apple.com/us/app/celebs-celebrity-look-alike/id1488127870">Celebs - Celebrity Look Alike</a>, an app that tells you which celebrity you look like.</p>
<p>It's a classic novelty AI use case, the kind where many apps focus on monetizing fast. Sociaaal encourages repeat usage instead: it offers a free trial and lets users reach their first generation before they see any paywall.</p>
<p>The result? Celebs ranks in the 94th percentile for retention among the AI apps in our study. More than half of its weekly subscribers renew after the first week, compared with 37.7% for the median AI app on weekly plans. After a year, it keeps about four times as many weekly subscribers as that median app.</p>
<h2>Why Celebs users keep coming back</h2>
<p>I was curious about this. How does a celebrity lookalike app with millions of users get people to come back again and again?</p>
<p>Here are the most important bits the team highlighted during our chat:</p>
<ul>
<li><strong>Putting the algorithm's quality above everything else.</strong> Sociaaal built a strong matching algorithm and prioritized its quality over any growth hack. Because the result is fast and accurate (&quot;in one second you get someone that looks like you&quot;), people keep using it. A week later, they're out with friends and do a few new generations. A month later, it happens again.</li>
<li><strong>Giving people ideas for what to try next.</strong> Sociaaal has tested guiding converted users on what to try next, like taking a photo with their grandma or with friends at school. According to Pablo, many people don't know what else to use the app for, and prompts like these can work well.</li>
<li><strong>Removing everything between the ad and the result.</strong> The team behind Celebs refused to add a long onboarding with email collection, and the tests backed them up. Now people get their first generation right away, then see the paywall offering the free trial.</li>
</ul>
<p>Pablo sees the simplified experience as one of the big reasons Celebs retains so well:</p>
<blockquote><p>&quot;We simplified the funnel, and the core feature is super good. We really let the user go deeper and try the app before having any friction.&quot;</p><footer>Pablo Sánchez - Head of Product, Sociaaal</footer></blockquote>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/362e5358254f6ccae1e36666584c6f387d02f696-1800x1400.png" alt="The Celebs's AI app "/></figure>
<p>The app looks simple, but that took a lot of work.</p>
<blockquote><p>&quot;That simplicity is a result of years of testing with hundreds of tests, with millions of users going through these different flows.&quot;</p><footer>Pablo Sánchez - Head of Product, Sociaaal</footer></blockquote>
<h3>How Sociaaal runs experiments</h3>
<p>A big part of that work is iteration. The team tests continuously, knowing that most hypotheses won't win on the first attempt.</p>
<p>That's why many parts of the app are designed parametrically: instead of a fixed screen design, elements such as layout, imagery, wording, bullet points, CTAs, and trust elements are variables the team can change and test independently.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/7cccc27ba09a40f8bae52074b212ecf292c1874d-1536x955.png" alt="App growth experiments by Sociaaal"/></figure>
<p>New hypotheses are first tested on smaller cohorts in secondary geographies, and only promising variants go up against the existing baseline. It's also partly a volume game: more well-defined hypotheses create more opportunities to find something that works.</p>
<p>The first screen users see is a good example. It has gone through many transformations. For a long time, a white background with a squared CTA was the clear winner, and the team struggled to find anything that could beat it.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/446798e3b623814a05950224bd6264ffc7b1e353-2048x1387.png" alt="The Celebs AI app - onboarding experiments"/></figure>
<p>Later, as consumer trends shifted and the onboarding became more colorful, a blue background started showing stronger engagement in smaller test cohorts. Eventually, it produced up to a 10% lift in trial conversion.</p>
<p>The lesson for the team: a winning design is contextual and can stop winning as the rest of the product and user expectations change.</p>
<h2>How to apply this to your AI app</h2>
<p>Don't get me wrong. Not every AI app needs a free trial or has a use case that people come back to.</p>
<p>But most apps benefit from building for repeated use and testing until the setup works.</p>
<p>For Sociaaal, retention isn't a goal in itself. The studio is built around unit economics, so retention matters when it helps the studio earn more from users than it spends to acquire them.</p>
<p>So treat the ideas below as ideas to test, not steps to copy.</p>
<h3>1. Make the core result worth repeating</h3>
<p>Celebs users keep coming back because the main result is good enough to try again, and that took a lot of product improvements.</p>
<p>Sociaaal replaced the app's old matching algorithm with a new system that combines several algorithms. It matches people based on their facial features rather than their expressions, and compares them against Sociaaal's own library of celebrity photos.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/97da05c7c6809147a92ff6825a7e26b257ee18bc-1800x1400.png" alt="The Celebs app - celebrity look alikes"/></figure>
<p>Getting there took more than one algorithm swap. The team tested several approaches with controlled cohorts, from asking users for images from multiple angles to matching the user's pose to a celebrity photo. Most were discarded.</p>
<p>Even when a feature looked more sophisticated, users didn't engage or convert better. The team picked the most promising direction and refined it for several months until it beat the previous baseline.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2a6fe7adb72553fff6a6e3a25662fb4dc2418f16-1536x826.png" alt="Celebs look alike algorithms"/></figure>
<p>It paid off. With better matches, people kept generating matches with new photos and new people. Extra AI features, like transforming your photo, didn't move the needle as much.</p>
<p>Here's what that means for your own AI app:</p>
<ul>
<li><strong>Treat output quality as a retention lever.</strong> If your app's value comes from one AI result, a generic model with a prompt on top is easy for competitors to copy and may not be good enough to try twice. Celebs' edge is its combined algorithms and its own photo library.</li>
<li><strong>Judge quality by what users do.</strong> A more sophisticated matching approach lost at Celebs whenever users didn't engage or convert better. Ratings help, but repeat behavior tells you more. Check how many users generate again in their first session or come back within a few days (and why), and compare cohorts before and after product improvements.</li>
<li><strong>Give a big change time to win.</strong> A major product change doesn't have to be an immediate winner to be valuable. Sociaaal's new matching only beat the baseline after months of refining. Sometimes the question is whether it gives the team something promising to keep improving.</li>
<li><strong>Resist the urge to add features for their own sake.</strong> When retention is low, it's tempting to keep building new features. But if the main result isn't good enough, more features won't make up for it. At Celebs, improving the main match did more for retention than extra AI features.</li>
</ul>
<h3>2. Get users from the ad to the aha moment as fast as possible</h3>
<p>Once your core AI result is good enough that people want to use it again, getting them to it fast can pay off more than charging them on the first visit.</p>
<p>Yes, many AI apps do the opposite. They put users through a long onboarding, or hide the result behind a paywall. For an app people might use once, that looks like fast revenue.</p>
<p>But at Celebs, the path with the least friction won. Patrick described it as getting people from the ad to &quot;the core experience and the aha moment&quot; as quickly as possible, and said removing sign-up and email collection worked &quot;much, much better.&quot;</p>
<p>Keep the generation itself fast, too. Celebs used to play fancy animations while it processed the match. They might impress someone the first time, Pablo said, &quot;but when you generate seven times, it's like, why do you even want this animation?&quot;</p>
<p>The team tested removing them and cutting the generation time from five seconds to 1.4, and both changes moved the needle.</p>
<h3>3. Let the data pick your monetization</h3>
<p>The free trial at Celebs is a good reminder to test your monetization instead of assuming what an AI app should (or shouldn’t) do.</p>
<p>I expected Celebs to charge before showing any result. Sociaaal tested that against a three-day free trial, and the trial won. Patrick sees the trial as the icebreaker.</p>
<blockquote><p>&quot;Before using our app, quite a lot of people would have doubts as to whether this app really tells you who your celebrity lookalike is. We show them who they look like, who their friends and their partner look like, and then they keep on using the app and do more and more generations.&quot;</p><footer>Patrick Stuart-Constant - CEO, Sociaaal</footer></blockquote>
<p>That's how Sociaaal approaches everything. The team runs about 100 A/B tests a month and uses RevenueCat to read the results. Patrick describes it as &quot;a philosophy of constantly testing and iterating and running as many A/B tests as you can in a very rigorous way.&quot;</p>
<p>A few habits make that volume useful:</p>
<ul>
<li><strong>Re-run tests that lost.</strong> A result only holds for the version of the app it ran on. Patrick has seen failed tests win &quot;a few years later&quot; because &quot;the rest of the app has changed so much.&quot;</li>
<li><strong>Pick an early signal for retention.</strong> Renewal data takes weeks to come in, which is slow when you run 100 tests a month. At Celebs, users who open the app three to five times in their first two or three days usually stick around, so the team uses early sessions as another key signal.</li>
</ul>
<h3>4. Personalize only after you've cracked the baseline</h3>
<p>Personalization can add another layer of performance, but it comes with a cost: every personalized flow is one more variant to build and maintain, which slows a small team down. So Pablo’s advice is to get the default experience working first and only then personalize.</p>
<p>When it does work, the gains add up. At Celebs, after dozens of iterations, personalizing parts of the experience with user signals lifted conversion by around 15% in the segments where it was applied.</p>
<p>Geography is one of those signals. When testing how much of the doppelgänger to reveal before the trial, Sociaaal found a large blurred image won in some Asian markets, while a much smaller preview did better in the US.</p>
<p>The question becomes less “Which variant wins?” and more “Which variant wins for whom?”</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ab7da2384d97380b3078677b2cedbe43411a65cc-1536x740.png" alt="The Celebs app - experiments"/></figure>
<p>The team also works with more complex setups at scale, like systems that gradually send more users to whichever version performs best for each audience while a test is still running. But you need a lot of traffic, engineering work, and constant monitoring to make it work.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e3b47a2d8eded3ace4053cbab0dbd9bcba0006ef-1536x638.png" alt="A/B tests and experiments"/></figure>
<h2>The trade-offs to test for yourself</h2>
<p>The Celebs app keeps its users by making the core match good enough to repeat, getting people to it quickly, and testing everything around it, including a free trial most people would bet against.</p>
<p>Each of those choices has a cost, though. Dropping accounts and email collection shortened the path to the first match, but it also gave up the easiest way to bring users back. Celebs makes up for it with paid ads and retargeting.</p>
<p>Free usage costs money, too. Every free generation and every trial is compute you pay for, including for people who never subscribe. If your generations are expensive, you can cap trial generations or sell extra credits on top of the subscription. And a trial only pays off if enough people keep using the app after it ends. Celebs shows you can't always tell that from the use case alone.</p>
<p>There's no right answer to any of these trade-offs, only experiments that tell you what works for your app. After hundreds of them, Sociaaal keeps landing in the same place: the simpler version wins. Or, in Pablo's words, &quot;simplicity pays.&quot;</p>
<section><h3>Run your next test with RevenueCat Experiments</h3><p>Sociaaal uses RevenueCat to run and track dozens of tests every month. Test your pricing, trial lengths, and paywall designs to learn what gets more people to stick around.</p><p><a href="https://www.revenuecat.com/feature/experiments">Give it a try</a></p></section>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[How to run enhanced app campaigns on Google Ads]]></title>
      <link>https://www.revenuecat.com/blog/growth/enhanced-app-campaigns-google-ads</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/enhanced-app-campaigns-google-ads</guid>
      <pubDate>Fri, 16 Oct 2026 15:01:21 GMT</pubDate>
      <dc:creator><![CDATA[Lucas Moscon]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[A practical guide to unblocking Google Ads for your mobile app ]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/88dd47f3064d606397d2f2a5858ec22dc47d8a9c-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Google Ads is one of the most underused channels in subscription app user acquisition. Not because the inventory is bad, but because the default way to buy it gives advertisers almost nothing to work with.</p>
<p>That default is Google App Campaigns (GAC, formerly UAC), and it has been the status quo for app advertisers for years.</p>
<p><a href="https://www.revenuecat.com/blog/growth/a-practical-guide-to-google-app-campaigns/">Thomas Petit's practical guide to Google App Campaigns</a> documents the limitations better than anyone:</p>
<ul>
<li>Valuable search inventory you can't pick keywords for</li>
<li>No keyword reporting</li>
<li>No way to run search-only campaigns</li>
<li>Brand searches silently inflating your campaign performance</li>
</ul>
<p>Thomas’s guide is the best resource for surviving within Google’s current restraints. But it’s a tight box. This article is about <strong>enhanced app campaigns </strong>— a new way to get <em>out </em>of the box.</p>
<h2><strong>Why do enhanced app campaigns matter most on Google?</strong></h2>
<p>In my previous article, I covered <a href="https://www.revenuecat.com/blog/growth/enhanced-app-campaigns/">what enhanced app campaigns are</a> and why apps are shifting budgets to them. Quick refresher:<strong> enhanced app campaigns (EACs) are web campaigns whose final URL redirects users straight to the app store.</strong> No landing page, no <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels/">web funnel</a>, no SKAN dependency.</p>
<p>This article is the execution manual for EACs — specifically focused on Google, because Google is where EACs unlock the most. On Meta and TikTok, running EACs mainly gets you better measurement and enriched signals. They’re valuable, but the campaigns themselves look similar to what you were running before.</p>
<p>Google is different. On Google, EACs unlock entirely new campaign types that don't exist for app advertisers:</p>
<ul>
<li><strong>Search:</strong> bid on specific keywords and search terms, and see revenue and ROAS at the keyword level. GAC technically serves on search, but you don't choose the keywords, you don't see them properly, and you can't exclude them. It's by design.</li>
<li><strong>YouTube Shorts:</strong> get full control over placement types within YouTube. This use case is great for apps that mainly run vertical videos and want to access new inventory without creating new assets.</li>
<li><strong>Demand Gen:</strong> full control over how your budget is spent. If you have winning vertical video or static images from Meta or TikTok, this is a new home for it, same format.</li>
<li><strong>Display: </strong>the largest display network on the internet, with placement-level control.</li>
<li><strong>Performance Max</strong> (PMax)<strong>:</strong> Google's all-inventory automation product, once your conversion signals are strong enough to feed it.</li>
</ul>
<p>One important thing to note is that running enhanced app campaigns on Google Ads does not allow you to reach the Play Store inventory on Android devices. So this is a constraint you should keep in mind.</p>
<p>With GAC, Google's algorithm decides everything: placements, keywords, audiences, budget allocation. You upload assets, set a bid, and pray. With EACs, you're buying Google inventory the way web advertisers have for 20 years. With actual control.</p>
<p>Across the subscription apps I worked with, the numbers back it up:</p>
<ul>
<li>100% reduction in install cannibalization — because you can now control all brand search terms (more on this below)</li>
<li>12% lower CPMs on Demand Gen EACs compared to equivalent GAC placements</li>
<li>14% higher ROAS on Demand Gen versus GAC targeting similar placements</li>
</ul>
<p>Let's get into how it actually works.</p>
<h3><strong>Are enhanced app campaigns allowed on Google Ads?</strong></h3>
<p>I know this is the first thing on your mind. When we first started running enhanced app campaigns at Appstack, this concern came up a lot. The answer is a clear yes.</p>
<p>Enhanced app campaigns are web campaigns. When you run one, you're using Google Ads exactly as designed: a web campaign with a final URL, a click, and conversions reported back through Google's standard web measurement stack. The fact that your final URL redirects the user to the App Store or Play Store is not a loophole. Deep links and store redirects are a normal, documented part of the web. App stores are websites too.</p>
<p>There is no policy against it. You're not spoofing anything, hiding anything, or exploiting a bug. In my experience, thousands of apps run this structure at scale today, with Google account reps fully aware of it. Google gets paid either way. (And you could argue that they prefer it, because you feed their algorithm richer conversion data than SKAN ever could.)</p>
<p>So no, you won't get banned.</p>
<h2><strong>How it works under the hood</strong></h2>
<p>Before touching the Google Ads interface, understand the click journey — this is how app install attribution works without SKAN. It's five steps:</p>
<ol>
<li><strong>User clicks your ad:</strong> the ad's final URL is a tracking link (app store page), not your website.</li>
<li><strong>Instant redirect:</strong> the link captures the click (with the Google click ID, campaign, keyword, and ad parameters) and redirects the user to the App Store or Play Store in milliseconds — the user never sees a webpage.</li>
<li><strong>Install and first open:</strong> the user installs the app and opens it for the first time. On first launch, the SDK matches the install to the click using probabilistic matching. It doesn't depend on SKAN or ATT opt-in.</li>
<li><strong>Events flow in:</strong> trial starts, subscriptions, renewals, and every event is tied back to the exact campaign, ad, and keyword that drove the install.</li>
<li><strong>Signals go back to Google:</strong> Conversions are uploaded to Google Ads via API against the original click ID. Google's algorithm now optimizes using previously-matched conversion data rather than <a href="https://www.revenuecat.com/blog/growth/skadnetwork-server-to-server-events-attribution-revenuecat/">modeled SKAN postbacks</a>, which are aggregated and have long delays.</li>
</ol>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9d8465c4cc6e3c87805336892e0c824664cccd50-1999x1047.png" alt=""/></figure>
<p>That last step is what makes the whole system compound. Google's bidding is only as good as the signals you feed it, and EAC signals are cleaner, faster, and more granular than anything <a href="https://www.revenuecat.com/blog/growth/skadnetwork-guide-subscription-apps/">SKAN</a> can deliver.</p>
<p>For example, you can pass data back to Google Ads hourly and have total control over which signal is mapped to an offline conversion action. A lot of the apps I work with strategically prioritize qualified events, like ‘Trial qualified’, to improve performance.</p>
<h2><strong>Setting up your first enhanced app campaign on Google Ads</strong></h2>
<h3><strong>Step 1: Offline conversion actions</strong></h3>
<p>In Google Ads, create web conversion actions for the events you care about. For subscription apps, the standard set is:</p>
<ul>
<li>Install (secondary, for diagnostics, not optimization)</li>
<li>Trial started</li>
<li>Trial converted (or subscription started, if you don't <a href="https://www.revenuecat.com/blog/growth/7-day-trial-subscription-app/">offer trials</a>)</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/5b79efdcf512c3b22b228aa3ea521ae93f73fdc2-1435x913.png" alt=""/></figure>
<p>Set ‘trial started’ or ‘subscription started’ as primary, depending on your volume. Same trade-off logic as everywhere else: more signals vs. better signals.</p>
<p>If you want to know which event to optimize or how much to spend, my answer is to do the math and ensure you get at least 10 conversions per campaign per day. So if your subscription costs $100 and you reach breakeven status (which is great at the beginning), you will need to spend $1,000 per day to meet the threshold.</p>
<aside class="tip"><strong>Pro tip</strong><p>Don’t get frustrated during your first few days optimizing towards an offline conversion action in Google Ads. It’s normal to struggle: Google lacks sufficient context to understand what the offline conversion action actually means. After feeding at least 25 conversions, the performance starts to improve.</p></aside>
<p>A quick note on the numbers in this guide: ~10 primary conversions per campaign per day is the floor for learning to stabilize, around 25 conversions is when performance usually starts to improve, and 50 is the minimum before you judge a campaign or change its structure.</p>
<p>With the <a href="https://www.revenuecat.com/docs/integrations/attribution/appstack">RevenueCat and Appstack integration</a>, conversions are automatically uploaded to the Google click ID once the account is connected. You don't build any of this pipeline yourself. For a walkthrough, check out this <a href="https://www.revenuecat.com/blog/engineering/revenuecat-now-integrates-with-appstack/">integration guide</a><u>.</u></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3ed1115c7d4ccc70e74cb7103333466d243ed9f1-1982x1154.png" alt=""/></figure>
<h3><strong>Step 2: Create your tracking links</strong></h3>
<p>Running EACs is fully within Google's policies, but building your own tracking links requires Google certification which means a two/three month review of your infrastructure — that’s a long wait, so most teams use a certified partner instead (like us at Appstack).</p>
<p>The tracking link must include all query parameters to properly attribute the data and return the correct information to Google. This is an example of a link:</p>
<p><code>https://appstack.link/deeplink/resolve?deeplink_id=hPSr3MG7&amp;url={lpurl}&amp;placement={placement}&amp;campaignid={campaignid}&amp;adgroupid={adgroupid}&amp;targetid={targetid}&amp;creative={creative}&amp;network={network}&amp;device={device}&amp;gclid={gclid}&amp;gbraid={gbraid}&amp;wbraid={wbraid}&amp;channel={channel}&amp;adtype={adtype}&amp;keyword={keyword}&amp;keywordid={keywordid}&amp;redirection_url=https://apps.apple.com/us/app/</code></p>
<p>The link must be set at the campaign level only (no need to add it multiple times across ad groups or ads) and should be located under ‘Campaign URL options’. You don’t need to add any suffixes or custom parameters if the link structure is correct (use the example I provided).</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ea9d71fb479e3c073381fbca88f037594235536c-1021x530.png" alt=""/></figure>
<h3><strong>Step 3: Pick your first campaign type</strong></h3>
<p>Don't launch everything at once. Here's the order I recommend, based on dozens of subscription app accounts:</p>
<p>First, begin with search-to-app.</p>
<p>Search is the highest-intent inventory on the internet, and app advertisers have been locked out of it for years. With an EAC, you create a standard Search campaign, add your keywords, and set the tracking link. That's it. Users search, click, and land directly on your store page.</p>
<p>Two plays, in priority order:</p>
<ol>
<li><strong>Brand defense first: </strong>here's a dirty secret about GAC that Thomas already called out in his guide: a big chunk of its ‘performance’ is cannibalized brand traffic. Someone searches your app's name (a user you already won), GAC serves them an ad, they install, and Google takes credit and your money for an install that was coming anyway. You can't see it, and you can't stop it, because GAC doesn’t give you the keyword control.</li>
<li><strong>Then category and competitor keywords: </strong>this is where keyword-level ROAS reporting changes how you operate, as you'll know, per keyword, what a subscriber costs and what they're worth. No app advertiser running GAC has ever seen this data. Start with exact and phrase match — broad match plus a fresh conversion history is how you burn a week's budget on garbage queries.</li>
</ol>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/4984d19fabd3e97c063719e53e56211e9b8e7667-1999x996.png" alt=""/></figure>
<p>Starting with search-to-app helps you warm up offline conversions at low cost and start getting some ad spend (sometimes the issue is that the campaign doesn’t spend). So my recommendation is to set the campaign to maximize for clicks with a cap if needed. Once you collect some conversions, you can move to maximize conversions or value.</p>
<aside class="tip"><strong>One caution on competitor keywords</strong><p>Bidding on a competitor's brand name is generally allowed, but using their trademark in your ad copy can get your ads restricted, so keep competitor names out of your headlines and descriptions.</p></aside>
<h4><strong>Demand Gen (YouTube and Shorts)</strong></h4>
<p>If you have <a href="https://www.revenuecat.com/blog/growth/subscription-app-creative-testing/">winning vertical videos</a> (or even static images) on Meta or TikTok, Demand Gen is the cheapest expansion you'll ever run: same creative, same format, new audience.</p>
<p>I recommend you structure your Demand Gen into two different campaigns:</p>
<ol>
<li>Create a Demand Gen campaign, select YouTube Shorts as the only placement for a pure Shorts play. You will need to upload the assets to a YouTube account to select them at the ad level.</li>
<li>Have a second Demand Gen campaign for the remaining placements (excluding YouTube Shorts). This way, you control the vertical content and avoid placing the same ad in two different campaigns.</li>
</ol>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/18d321f03e7b74442446ad762cd4c37219d49e7e-1999x885.png" alt=""/></figure>
<p>Based on the data I can access, Demand Gen EACs delivered 12% lower CPMs and 14% higher ROAS than Google App Campaigns running on comparable placements. The reason is simple: you control the placement, and Google receives better conversion signals, so the algorithm wastes less.</p>
<aside class="tip"><strong>Pro tip</strong><p>Don't remake creatives for Google. Deploy your proven Meta/TikTok winners as-is first, and only iterate once you have baseline data. It’s easier and cheaper to do it.</p></aside>
<h3><strong>Step 4: Launch checklist</strong></h3>
<p>Once you’ve completed the above, here’s your final checks:</p>
<ul>
<li>Tracking link tested end-to-end on a real device (click → store → install → in-app event)</li>
<li>Offline conversion actions receiving data and marked primary/secondary correctly (you need to run a campaign to test that the offline conversion action is actually receiving the right signals)</li>
<li>Create custom columns using the offline conversion actions to see them in Google Ads</li>
<li>One campaign, few ad groups — maximum liquidity, faster learning phase</li>
<li>Budget floor: enough daily spend to generate ~10 primary conversions per day; below that, learning never stabilizes</li>
</ul>
<h2><strong>Feeding Google better signals</strong></h2>
<p>This is where subscription apps have an unfair advantage, and where most of them leave money on the table.</p>
<p>Google optimizes toward whatever conversion you tell it to optimize for. Most apps tell it ‘install’, which is like training a hunting dog to fetch anything that moves. Installs are cheap and mostly worthless; what you want is subscribers.</p>
<p>With EACs, the events you can optimize toward are only limited by what your backend knows:</p>
<ul>
<li><strong>Trial started: </strong>the volume play. A good starting point for most apps.</li>
<li><strong>Trial converted:</strong> the money event. It fires days after install, when the user isn't in the app. That makes it nearly impossible to send reliably through SKAN without a custom server-side setup, and trivial through an EAC, because the postback is an API call rather than a device-side postback. Although I don’t recommend optimizing for ‘Trial converted’ (due to the delay in the event), you can configure this event as a secondary action to improve performance.</li>
<li><strong>Trials qualified: </strong>trials filtered by early engagement signals that predict conversion. Best of both worlds: more volume than converted trials, more quality than started trials.</li>
</ul>
<p>If you're on RevenueCat, this entire layer is free work. The <a href="https://www.revenuecat.com/docs/integrations/attribution/appstack">RevenueCat + Appstack</a> integration forwards server-to-server events (trial conversions, renewals, cancellations) directly into the attribution pipeline. No SDK event mapping, no developer time, no risk of a codebase change silently breaking your events. RevenueCat is already the source of truth for your revenue; this just wires it into your ads.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/66240d13ec06cd9ded641ed987a290bb2cf60a1e-1999x703.png" alt=""/></figure>
<aside class="tip"><strong>Boost the signal</strong><p>Whatever you optimize for, send all your events to Google, including renewals. Even events you don't bid on improve the algorithm's picture of who your best users are.</p></aside>
<p>The compounding effect is real: better signals → better bidding → cheaper subscribers → more conversion volume → even better signals. Apps running GAC are stuck feeding Google delayed, aggregated, threshold-gated SKAN data. You're feeding it revenue events straight from your backend, tied to the original click, in near real-time. Same auction, different weapons.</p>
<h2><strong>When to judge, when to scale, when to kill</strong></h2>
<p>Most EAC setups don't die because the strategy is wrong. They die because someone <a href="https://www.revenuecat.com/blog/growth/when-to-give-up-a-paid-channel/">judged a campaign at the wrong time</a>, with the wrong metric. So instead of a generic ‘be patient’ speech, here are the rules I give every app I work with:</p>
<p><strong>Judge brand search in days, not weeks.</strong></p>
<ul>
<li>Brand campaigns have no learning curve worth respecting: intent is maximal, and volume is predictable.</li>
<li>If it isn't printing cheap installs within the first week, the setup is broken (link, match types, store redirect), not the strategy.</li>
<li>Debug, don't wait.</li>
</ul>
<p><strong>Judge category search and Demand Gen at the ROAS level, never at the install level.</strong></p>
<ul>
<li>Give them two to three weeks and at least 50 primary conversions before touching anything structural.</li>
<li>During that window, your only job is to cut obvious garbage: irrelevant search terms and placements that make no sense. Everything else stays untouched.</li>
<li>Use cohorts to actually see if more money is hitting the bank account than what is going out.</li>
</ul>
<p><strong>Kill keywords with data, not vibes.</strong></p>
<ul>
<li>This is the muscle GAC never let you build.</li>
<li>200 clicks, decent CTR, zero trials: Dead. Kill it without mercy.</li>
<li>Expensive installs but strong trial-to-paid conversion: Keeper, even if your CPA dashboard says otherwise.</li>
<li>You now have keyword-level ROAS. Use it as the judge, jury, and executioner.</li>
</ul>
<p><strong>Scale winners vertically before expanding horizontally.</strong></p>
<ul>
<li>Raise budgets and bids on proven keywords and placements before adding new campaign types.</li>
<li>Only once Search and Demand Gen are profitable do you open Display (isolated, audited weekly) and eventually PMax.</li>
<li>Expanding while your core campaigns are still learning splits your conversion volume and slows everything down.</li>
</ul>
<aside class="tip"><strong>Pro tip</strong><p>Create a custom column for cost per trial converted and make it your default view. CPA-per-install is a vanity metric in this setup; retire it.</p></aside>
<h2><strong>TL;DR: key takeaways</strong></h2>
<p>We covered a lot in this article, so let’s recap on the top points to remember:</p>
<ul>
<li>GAC is the legacy way; no keyword control, no keyword reporting, brand traffic silently inflating performance. EACs are the way out of the box, not a hack inside it.</li>
<li>This is 100% allowed. EACs are standard web campaigns with a store redirect. No policy issue, no gray area, no ban risk.</li>
<li>Start with search-to-app brand defense. Less install cannibalization is the fastest, most measurable win in this entire playbook.</li>
<li>Recycle your Meta/TikTok winners into Demand Gen. Same assets, new audiences and segments at a lower CPM.</li>
<li>Your signals are your moat. Optimize toward trial started or qualified trial, not installs, and send trial converted as a secondary signal.</li>
<li>Google Ads has always had the inventory. What app advertisers were missing was the control and the data to buy it intelligently. Now you have both.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[What's New in the RevenueCat AI Toolkit: The CLI, Reviewable Store Changes, and Paywalls with Codebase Context]]></title>
      <link>https://www.revenuecat.com/blog/engineering/ai-toolkit-new</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/ai-toolkit-new</guid>
      <pubDate>Fri, 16 Oct 2026 01:57:42 GMT</pubDate>
      <dc:creator><![CDATA[Jaewoong Eum]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[In this article, you'll explore what's new in the AI Toolkit since launch: the RevenueCat CLI, reviewable plans for store products, and paywalls designed.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/f19d40dcf219065b4192dc58f866b33a71e89a98-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>In May, RevenueCat released the <a href="https://www.revenuecat.com/blog/company/ai-toolkit">AI Toolkit</a>, a plugin that connects coding agents such as Claude Code, Codex, and Gemini CLI to your RevenueCat projects through the MCP server, along with 13 skills that teach the agent how to create a project, integrate the SDK, show a paywall, and debug purchases.</p>
<p>The main plugin now ships 24 skills, a second plugin adds 20 skills for Google Play Billing, and the new RevenueCat CLI gives agents another way to reach your account. Most of the new skills take your agent past the integration step, into work you used to leave your editor for. The difference from <a href="https://www.revenuecat.com/docs/tools/rico">Rico</a>, RevenueCat's own agent, is whose agent does that work: the toolkit equips the coding agent you already use, with your codebase as context.</p>
<p>In this article, you'll explore what's new in the AI Toolkit since launch: the RevenueCat CLI, reviewable plans for store products, paywalls designed with your codebase as context, experiments, SDK compatibility checks, the new analytics skills, the Play Billing plugin, and how to install or update the toolkit.</p>
<h2><strong>The RevenueCat CLI: Beyond the MCP server</strong></h2>
<p>Until late August, every skill assumed that the MCP server was the agent's only connection to RevenueCat. The <a href="https://www.revenuecat.com/docs/tools/cli">RevenueCat CLI</a> (<code>rc</code>), in public beta since August 25, adds another one. It manages projects, apps, store credentials, the product catalog, customers, and paywalls from the terminal, and it behaves the same way whether a person, a script, or an agent runs it.</p>
<pre><code>brew install RevenueCat/tap/rc
rc auth login
rc projects use</code></pre>
<p>For CI and agent sandboxes, the <code>@revenuecat/cli</code> npm package ships the same native binary, and <code>npx @revenuecat/cli &lt;command&gt;</code> runs any command without installing anything. Scripts can skip the browser login by passing a RevenueCat API v2 secret key through the <code>RC_API_KEY</code> environment variable.</p>
<p>Three details make the CLI practical for an agent. Every command describes itself at runtime through <code>rc commands --json</code> and <code>rc schema</code>, so the agent discovers flags from the binary instead of guessing them from its training data. Every command also supports <code>--json</code>, and in that mode errors come back as JSON too, with stable error types and exit codes. And <code>--no-input</code> makes a command fail instead of waiting on a prompt, which keeps an agent or a CI job from hanging.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/17a461a79a24aee5730dca8aeef5c54c52c8a724-2232x1170.png" alt=""/></figure>
<p>For CI and agent sandboxes, the <code>@revenuecat/cli</code> npm package ships the same native binary, and <code>npx @revenuecat/cli &lt;command&gt;</code> runs any command without installing anything. Scripts can skip the browser login by passing a RevenueCat API v2 secret key through the <code>RC_API_KEY</code> environment variable.</p>
<p>Three details make the CLI practical for an agent. Every command describes itself at runtime through <code>rc commands --json</code> and <code>rc schema</code>, so the agent discovers flags from the binary instead of guessing them from its training data. Every command also supports <code>--json</code>, and in that mode errors come back as JSON too, with stable error types and exit codes. And <code>--no-input</code> makes a command fail instead of waiting on a prompt, which keeps an agent or a CI job from hanging.</p>
<pre><code>rc products store plan &lt;app-id&gt; --file prices.csv
rc products store show &lt;plan-id&gt;
rc products store apply &lt;plan-id&gt;</code></pre>
<p>You can ask for the change and the file in one request:</p>
<p>&quot;Raise the monthly Pro subscription to $12.99 in the US on both stores. Write the desired prices to <code>store/prices.csv</code> so I can commit them, then walk me through the plan's diff.&quot;</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/128d6f4a4aef82fed43c51f3f8dbdf7e118b7d6e-1430x256.png" alt=""/></figure>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ca1a7763a483bcdffb8b0349df9e29dfedece1f5-1702x537.png" alt=""/></figure>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/388ebe4627b2d0601bbe5bfdb3475429fc4b3e93-1495x508.png" alt=""/></figure>
<h2><strong>Paywalls: Designing with your codebase as context</strong></h2>
<p>The <code>revenuecat-paywall</code> skill from launch covers presenting a paywall in your app with RevenueCatUI. The new <code>revenuecat-paywall-design</code> skill works on the paywall that RevenueCat serves to that code: creating, duplicating, editing, evaluating, and auditing it.</p>
<p>This is where a coding agent has an advantage: it can read your app. When you ask for a new paywall, the skill has the agent gather context from two places. From your RevenueCat project, it reads offerings, prices, and existing paywalls. From your codebase, it picks up premium features, brand colors, visual direction, and tone. The agent passes what it found to the <code>create-paywall-ai</code> MCP tool as <code>codebase_context</code>, the part of the request that needs an agent inside your repository, and RevenueCat's Paywalls AI Editor generates the draft from it. The result is always an unpublished draft, and generation can take several minutes when it creates images.</p>
<p>Other requests use a different tool on purpose. When you ask for a copy of an existing paywall, the agent uses <code>duplicate-paywall</code>, which copies the original instead of regenerating it from a description and losing its images and structure. Changes to an existing paywall go through <code>edit-paywall-ai</code>.</p>
<p>To evaluate a paywall, the agent renders it with <code>render-paywall-screenshot</code> and reads its exact configuration with <code>get-paywall</code>. The audit follows an order of impact: it starts with the call to action, moves through how clearly the recommended package stands out, price anchoring, content above the fold, and trust signals, and leaves copy for last.</p>
<p>&quot;Create a paywall for my new <code>premium</code> offering that matches the app's colors and highlights the features behind the Pro entitlement, then audit the draft.&quot;</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0f1119435aa17f317514a8767d4017ce18f98369-1432x430.png" alt=""/></figure>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ed2af0f217eed697437a1986ed989aa51dafe11b-854x1020.png" alt=""/></figure>
<h2><strong>Experiments: From setup to results</strong></h2>
<p>The new <code>revenuecat-experiments</code> skill teaches your agent to prepare an experiment with the MCP server's experiment management tools. A RevenueCat experiment compares two to four offerings, and the skill keeps the variants identical except for the one thing under test:</p>
<ul>
<li><strong>Control</strong>: when the control is what production serves today, the agent uses the current offering directly instead of copying it.</li>
<li><strong>Price and trial tests</strong>: prices live on store products, so the treatment needs its own products. The agent reuses products that already exist, creates the missing ones through the store state flow above, and swaps them into a copy of the control offering.</li>
<li><strong>Paywall tests</strong>: the agent duplicates the offering together with its paywall and edits only the part under test, using the paywall tools above. This needs a RevenueCat paywall on the offering. If your app draws its own paywall, there's nothing on the offering for the skill to edit, so the variants have to come from your app's code instead, for example a paywall that reads each offering's metadata, and that's code your coding agent can write.</li>
</ul>
<p>The experiment is created as a draft, and the agent starts it only after you confirm, because starting it enrolls real customers. Pausing stops enrollment but keeps serving enrolled customers their variant. Stopping is final: enrolled customers fall back to the default offering, and the experiment can't be restarted.</p>
<p>Experiments also depend on your app. They only split customers evenly if the app displays the current offering it gets from the SDK rather than a hardcoded one. Checking that means reading your code, which your coding agent already has open, and the experiments skill doesn't check it on its own, so ask for it:</p>
<p>&quot;Set up a price test on the annual subscription: keep the current offering as the control and try $49.99 instead of $59.99 in the treatment. Before you create it, check that the app displays the current offering instead of a hardcoded one.&quot;</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/314e41b200d881ed2f95df78ce6cfe2927adecc4-1389x314.png" alt=""/></figure>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ac39c4042761266e93494c6cfcc3e106f480c459-1488x975.png" alt=""/></figure>
<p>Once an experiment is running, the experiment analysis skill reads it in a fixed order:</p>
<ol>
<li>The experiment's setup.</li>
<li>A screenshot of each variant's RevenueCat paywall.</li>
<li>Chart data from the same period, such as revenue, trial volume, and conversion.</li>
<li>The results, which the agent can break down by platform or country.</li>
</ol>
<p>The chart step gives the agent context that the results alone don't have, such as whether trial volume or revenue shifted across the whole app while the test ran.</p>
<h2><strong>SDK compatibility: Checking before you ship</strong></h2>
<p>Some RevenueCat features need a minimum SDK version, and your subscribers don't all update the app at the same pace, so before you rely on a feature you need to know how many of them can use it. The <code>revenuecat-sdk-compatibility</code> skill answers that with your own data by combining two MCP tools. <code>list-sdk-feature-gates</code> returns the minimum version of each SDK for every gated feature, and <code>list-sdk-versions</code> returns the SDK versions observed among your subscribers in the last 30 days, including wrapper SDKs such as React Native, Flutter, and Kotlin Multiplatform. The agent compares the two and tells you what share of your recent subscribers is below the requirement.</p>
<p>&quot;Can I enroll existing customers in an experiment? What share of my subscribers are on an SDK version that doesn't support it?&quot;</p>
<h2><strong>Analytics: Answers without leaving your agent</strong></h2>
<p>Several new skills answer questions about your customers and metrics while you work on the app. <code>revenuecat-audiences</code> filters your customers by product, spend, country, attribution, or the app and SDK version they run, and links to the filtered list. <code>revenuecat-charts</code> now tells Initial Conversion (a trial or subscription started) apart from Conversion to Paying (a payment made), and it looks up your experiments, offerings, and store state before it names a cause for a change. <code>revenuecat-dashboard-links</code> teaches the agent to link you to the exact dashboard page it's talking about. Three skills step outside the app itself: <code>revenuecat-forecasting</code> projects MRR or ARR month by month from the MRR that comes up for renewal, its renewal rate, and recent new subscriptions, <code>revenuecat-billing</code> covers what you pay RevenueCat, and <code>revenuecat-app-valuation</code> estimates what the app might be worth.</p>
<h2><strong>Google Play Billing: A second plugin</strong></h2>
<p>If you ship on Android, the <code>revenuecat-play-billing</code> plugin adds 20 skills for the Google Play subscription lifecycle with the RevenueCat Android SDK, covering the purchase flow, plan and price changes, payment recovery, subscription states, webhooks, and security. Each skill is derived from a chapter or appendix of the RevenueCat Android SDK Handbook, and the plugin is synced from the <a href="https://github.com/RevenueCat/play-billing-skills">Play Billing Skills</a> repository. [The handbooks article](TODO: link to the handbooks post) covers that handbook, its companion Google Play Billing Handbook, and the skills in more detail.</p>
<p>In Claude Code, install it next to the main plugin from the RevenueCat marketplace:</p>
<p><code>claude plugin marketplace add RevenueCat/ai-toolkit
claude plugin install revenuecat@RevenueCat
claude plugin install revenuecat-play-billing@RevenueCat
</code></p>
<p>The plugin is also available in Cursor, Codex, and VS Code. Gemini CLI installs one extension per GitHub repository, so from <code>RevenueCat/ai-toolkit</code> it gets the main plugin only, and the Play Billing Skills README shows how to add those skills to Gemini directly.</p>
<h2><strong>Installation: Updating and installing the toolkit</strong></h2>
<p>If you already use the toolkit in Claude Code, update it first. Claude Code doesn't refresh plugins from third party marketplaces on its own by default, and installs from before August 19 still use the plugin's old name, <code>RevenueCat</code>, so run both of these commands:</p>
<p><code>claude plugin install revenuecat@RevenueCat
claude plugin update revenuecat@RevenueCat
</code></p>
<p>The first command replaces an install made under the old name, and the second updates one made after the rename. In Codex, which has no rename mechanism, uninstall the old <code>RevenueCat</code> plugin and install <code>revenuecat</code> to keep receiving updates. In Gemini CLI, run <code>gemini extensions update revenuecat</code>.</p>
<p>For new installs, here is where each client stands now:</p>
<ul>
<li><strong>Claude Code</strong>: installing from Anthropic's official plugin marketplace now delivers the full plugin. Claude Code adds that marketplace the first time you start an interactive terminal session, so <code>/plugin install revenuecat@claude-plugins-official</code> works without adding one. The RevenueCat marketplace, <code>RevenueCat/ai-toolkit</code>, also carries the Play Billing plugin.</li>
<li><strong>Cursor</strong>: the plugin is listed in the <a href="https://cursor.com/marketplace/revenuecat/revenuecat">Cursor Marketplace</a>, and <code>/add-plugin revenuecat</code> installs it.</li>
<li><strong>Codex</strong>: add the marketplace with <code>codex plugin marketplace add RevenueCat/ai-toolkit</code>, install the plugin with <code>codex plugin add revenuecat@RevenueCat</code>, then sign in with <code>codex mcp login RevenueCat</code>, because installing doesn't start authentication.</li>
<li><strong>Agent Plugins clients</strong>: both plugins ship a manifest that follows the <a href="https://agent-plugins.org/">Agent Plugins 1.0.0</a> specification, so any client that implements it can load them.</li>
<li><strong>Skills only</strong>: <code>npx skills add RevenueCat/ai-toolkit</code> or <code>rc skills install --all</code> installs the skills without the MCP server, for environments that don't support plugins.</li>
</ul>
<h2><strong>Conclusion</strong></h2>
<p>The common thread in these additions is that your agent now reaches past your code into the systems your code depends on, and it brings the code along as context. A good first task is one where that context changes the result, such as a paywall generated from your app's own colors and the features behind your entitlement, followed by a careful read of the draft and the agent's audit before you publish.</p>
<p>A skill is a written answer to the question of what should happen first. With that order written down, you can let an agent with access to your store catalog prepare changes to it.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[5 Models for combining subscription apps with physical products]]></title>
      <link>https://www.revenuecat.com/blog/growth/combining-subscriptions-physical-products</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/combining-subscriptions-physical-products</guid>
      <pubDate>Thu, 15 Oct 2026 12:44:00 GMT</pubDate>
      <dc:creator><![CDATA[Daphne Tideman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[How to bring offline and online products together (without users feeling ripped off)]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/e88456be1fc284647d214ffed0ed563dfe5fe81f-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>My husband, probably fed up with our plants dying (or with me constantly uploading photos to Claude to figure out what was wrong with them), bought me some <a href="https://plantwithwillow.co.uk/">Willow plant sensors</a>. I downloaded the app, set everything up, and was pleasantly surprised to discover I could not only give each plant a fun name, but also have them sassily ‘talk’ back to me.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/776177af5480a5bd5315b2a194abad55f0c98057-923x1999.png" alt=""/></figure>
<p>While reviewing all the data, excited to find out what my plants needed (and slightly stressed about providing enough humidity during a London heatwave), I noticed that Willow had a paid subscription. Usually, when a physical product pushes you to also pay and subscribe for an online product, it feels cheeky — you’ve already paid for the thing, haven’t you? But for once, it felt great.</p>
<p>The combination we're used to is <strong>physical products where you need a subscription to really make use of them.</strong> They’re especially popular in the health category, like the Oura Ring, Whoop, and Peloton. But Willow is different. I got enough value from the physical product alone; so the subscription felt like a genuinely valuable add-on, rather than paying for something twice.</p>
<p>It made me realize how rare that is: a physical product and app coexisting perfectly.</p>
<p>In <a href="https://www.revenuecat.com/blog/growth/2025-app-monetization-trends/">my 2025 monetization trends piece</a>, I flagged the rise of <a href="https://www.revenuecat.com/blog/growth/ai-hybrid-monetization">hybrid monetization</a> through combining online and offline products. Physical goods are one of the most overlooked <a href="https://www.revenuecat.com/blog/growth/hybrid-monetization-techniques">hybrid monetization techniques</a>, because they're the opposite of what we usually picture when we think of apps.</p>
<p>I've also seen this from the other side — I used to be Head of Growth at an e-commerce brand where we looked into building digital experiences — and have observed five models for combining apps going physical, and physical brands going digital. When done right, I genuinely believe it isn't a trend, but a powerful way to improve your <a href="https://www.revenuecat.com/blog/growth/what-is-lifetime-value-ltv-apps">lifetime value</a>.</p>
<p>But before we even get to that, we’ve got two questions to answer.</p>
<h2>Why physical, why now?</h2>
<p>I touched on physical goods being on the rise last year due to the popularity of hybrid monetization, but there's more to why this is an important and growing trend.</p>
<p>Yes, <a href="https://www.revenuecat.com/state-of-subscription-apps">most apps are still subscription-only</a>: 63.5% of apps offer nothing but a subscription. However, as the number of apps grows, the median realized LTV per payer is lower: $23 per year.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ffdae52f2354f021c1fc080596c650843291f105-1069x763.png" alt=""/><figcaption>State of Subscription Apps report 2026: realized lifetime value (RLTV) per payer after Year 1, by developer HQ</figcaption></figure>
<p>This makes the economics tricky, but physical goods add a way to build on that. Here’s how that looks in practice for my plant app:</p>
<ul>
<li>The subscription: £25 per year (about $34 per year)</li>
<li>The sensors: about £75 ($101) for three sensors (Yes, I looked up how much the gift was worth for this article)</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/267f6af5aec2b92f84654afe37549bba62d00876-923x1999.png" alt=""/></figure>
<p>So physical products can extend your LTV hugely. Even as we’ve gotten used to paying more and more for apps these days, <strong>physical products still have higher value</strong>.</p>
<p>The other reason this is more relevant than ever: <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels/">web revenue completely exploded in 2025</a>, with <a href="https://www.revenuecat.com/state-of-subscription-apps">41% of top-tier apps</a> now generating revenue on the web. This makes integrating some form of store or physical-goods upsell easier.</p>
<p>Physical goods can't be natively and easily offered inside your app either: Apple's <a href="https://developer.apple.com/app-store/review/guidelines/">guidelines (3.1.3(e))</a> state that physical goods must be <a href="https://www.revenuecat.com/feature/web">sold </a><em><a href="https://www.revenuecat.com/feature/web">outside</a></em><a href="https://www.revenuecat.com/feature/web"> in-app purchases</a>. The upside: they also carry no Apple commission. The downside: if you aren’t used to selling via <a href="https://www.revenuecat.com/blog/company/apple-in-app-web-checkouts-eu">web checkout</a>, it makes physical products harder to set up and more hidden from users — which is where most physical goods attempts fall flat.</p>
<p>If it's not an integral part of your monetization system, it feels like a bad add-on: a gift shop bolted onto your app, an extra shakedown, or a cheeky ask. So how do you know if this <em>is </em>the right route for you?</p>
<h2>Why fair expectations are key to success</h2>
<p>I'm not being dramatic when I say there's one question that matters more than any other: <strong>what do customers believe they've already paid for?</strong></p>
<p>I wasn't upset by Willow's subscription. Not because I work in subscription apps and love a good subscription, but because I believe I already paid (well, <em>my husband</em> paid) for what my plants needed to thrive. I got that. I learned when they needed water. I learned when they needed humidity, more or less light. Heck, I was even so dedicated to keeping them alive that I took them into the shower, in the hope of helping them through the summer’s dry heatwave.</p>
<p>So before even knowing about a subscription, I had gotten what I paid for.</p>
<p><strong>The subscription was all extra:</strong> the data, the history, and the tracking. And I'll be honest, as a data and plant nerd, I am very happy to geek out on that, especially when it comes at a fair price. And as I hope to receive more sensors (they’re on my birthday list), the subscription becomes even more worth having.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/267f6af5aec2b92f84654afe37549bba62d00876-923x1999.png" alt=""/></figure>
<p>Contrast that with BMW, who <a href="https://www.forbes.com/sites/alistaircharlton/2023/09/07/bmw-drops-controversial-heated-seats-subscription-to-refocus-on-software-services/">dropped a subscription for their heated seats</a> after 15 months… Unsurprisingly, the subscription had been controversial from day one, because people buying a car with heated seats do not expect to pay separately to use them.</p>
<p>This is the golden rule for adding physical products to your subscription: <strong>fairness is key.</strong></p>
<p>Now, fairness doesn't just come down to whether customers <em>can</em> use your product without paying again. It comes down to whether they <em>expect</em> to.</p>
<p>Take me, for example. <a href="https://www.revenuecat.com/blog/growth/peloton-retention-takeaways">I bought a secondhand Peloton last year and eventually canceled my subscription</a> because the classes were becoming too repetitive for my needs and no longer fit my workout routine.</p>
<p>While it's annoying that I now can't use the device to its full extent without the subscription, it still functions as a basic stationary bike. And when I bought it secondhand, I knew up front I would need a subscription to get the most out of it, so it didn't feel unfair. Perception is a key part of whether your physical + digital partnership works.</p>
<p>From there, it's working out where physical fits into your strategy. There are five models I see subscription apps following, split across two approaches.</p>
<p>Approach 1: App first — you started with the app</p>
<ul>
<li><strong>Model 1. Brand merch:</strong> swag for superfans, built for brand and community</li>
<li><strong>Model 2. Companion goods:</strong> physical items that make the digital product work better</li>
<li><strong>Model 3. Physical as part of the core offer:</strong> the box is part of the subscription</li>
</ul>
<p>Approach 2: Hardware first — the device came first</p>
<ul>
<li><strong>Model 4. Hardware first, subscription attached:</strong> the device is the entry point; software is the profit</li>
<li><strong>Model 5. Membership-native hardware:</strong> the hardware disappears into the subscription</li>
</ul>
<p>Let’s break down each model.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3c87adcf03af25200eee11f0472b0c28a3db748f-1999x1125.png" alt=""/></figure>
<h2>Approach 1: App first — you started with the app</h2>
<p>This approach is for app-first companies: you started with the app and then decided to add in physical, for whatever reason.</p>
<h3>Model 1: Brand merch</h3>
<p>A great example of this is <a href="https://store.lingokids.com/">Lingokids</a>, a learning app for children. In 2022, they <a href="https://lingokids.com/press/lingokids-store-now-available">created an off-screen brand presence through a character store</a>:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/da2c8c9dfb4a71df65d7cb2ec55dc7353449b400-1344x895.png" alt=""/></figure>
<p>Self-care app Finch also did this, selling stickers, clothing, and plushies of their signature bird.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d522f6bad66c46d2a52e8e4b58c95f0d04515529-1182x1336.png" alt=""/></figure>
<p>Duolingo <a href="https://www.modernretail.co/marketing/duolingo-wants-to-make-its-green-owl-mascot-as-famous-as-pikachu-with-its-first-pop-up-store/">has a brand store</a> where the products mostly don't relate to using the app, except that if you're learning chess through Duolingo, you could also buy the Duolingo chess set (which, by the way, is ridiculously adorable).</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/cec5e405632bb435baa6c1084e672ce679454540-1306x1170.png" alt=""/></figure>
<p><strong>The key thing about this model is that it's more about marketing than monetization. </strong>Duolingo said it on the record: yes, merchandise sales doubled two years running, yet <a href="https://www.modernretail.co/marketing/duolingo-wants-to-make-its-green-owl-mascot-as-famous-as-pikachu-with-its-first-pop-up-store/">&quot;sales is secondary to spreading brand love&quot;</a><strong>. </strong>Not all apps build a strong brand connection, and not all apps need one. But for those that do, related merch can be a value add or, in some cases, even the basis of a referral scheme.</p>
<p>For example, <a href="https://www.joinladder.com/">Ladder</a>, an app for personalized workout plans, has a merchandise store where you can buy merch with credits earned from referring other users.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/6f4b14183a3d1e9409d1f10caa1540571c9720a7-923x1999.png" alt=""/></figure>
<h3>Model 2: Companion goods</h3>
<p>The next model is where added products create value but, again, aren't a must. It's a sweet spot: <a href="https://www.revenuecat.com/blog/growth/subscription-app-expand-value/">loyal users can get extra value</a> from your app by buying your products, but they don’t need the physical product for the app to function or deliver its core value. Companion goods can also often be bought elsewhere.</p>
<p><a href="http://reading.com">Reading.com</a>, a reading app for young children, created printables that you can buy as an extra. COO Tim Dikun <a href="https://subclub.com/episode/what-readingcom-learned-testing-prices-and-funnels-tim-dikun-teachingcom">shared on the Sub Club podcast</a> that about 20% of people starting a trial or purchasing on the web take the $9.99 upsell. But their biggest physical good, in my opinion, is their Amazon books:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/cc71e4c1959cdbf0a7305b461727e21297f6af17-1999x1297.png" alt=""/></figure>
<p>These had two purposes:</p>
<ol>
<li><strong>People were asking for them:</strong> parents often re-read the books with their children, and they liked the idea of a physical book to hold rather than doing everything on a screen</li>
<li><strong>Amazon is additional exposure:</strong> a new platform, new reach, and a potential way to get discovered by people who never would have found the app</li>
</ol>
<p>Two birds, one book!</p>
<p>Another great example is <a href="https://betterme.world/">BetterMe</a>, a personalized wellness app. As you go through their onboarding flow on the web, you get offered a <a href="https://store.betterme.world/products/pilates-essential-kit-raspberrypink">Pilates kit</a> with all the things you need to use their app. If you're new to Pilates, or at least Pilates at home, you probably don't have much of this equipment, so this add-on can be really valuable. And if you do, it’s an optional extra rather than a must-buy.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/66bd38a03abb6ba50ed72cebd3ac0166b1b8cc1a-1999x1135.png" alt=""/></figure>
<h3>Model 3: Physical as part of the core offer</h3>
<p>Here is where the lines start to blur. The app still often has value by itself, but the physical is a huge part of the offering. <a href="https://www.arya.fyi/">Arya</a>, an intimacy concierge app, is a great example: you get a box of products delivered, and then the app provides guidance to help you through it all, with instructions on how to use them.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f514375d1eb74e069da642ca93e8a3914a453d3a-1999x869.png" alt=""/></figure>
<p>Another example is <a href="https://chatbooks.com/">Chatbooks</a>, which turns your camera roll into monthly photo books. The lines are blurry here: is it a subscription app or a photo book subscription? I’d argue the app is the key part because their value proposition is the ease of turning your camera roll into books and the app is the key way they do that:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/fd307412fbf89e5e8d9d22c5f14563611e0b7d92-1567x730.png" alt=""/></figure>
<p>Yet their subscription in the app is for a number of books per year, making the distinction between app and physical product very confusing:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/5d27bc5290b63c7eaca36e255753e8b2e80a35f8-991x1999.jpg" alt=""/></figure>
<p>In this model, physical goods are no longer an add-on but are becoming a bigger part of your business. Arya’s app value comes from the box that is delivered and from guiding you through it. Chatbooks' value is turning your photo roll into a photobook. Neither works without the physical goods. You'll need more of an operational system in place as a result because it will be near 100% of your subscribers ordering/receiving physical goods, vs. what could be a few percent for model 1 or 5–20% for Model 2.</p>
<p>Then there are the apps where the hardware comes first.</p>
<h2>Approach 2: Hardware first — the device came first</h2>
<p>One of the toughest things about physical goods is their margins. I've seen the margins of e-commerce and of apps, and they are night and day.</p>
<p>Peloton shares this openly on the hardware side: even though we could all agree their equipment is expensive, they only made a 13.4% gross margin on hardware in <a href="https://www.globenewswire.com/news-release/2026/08/06/3340106/0/en/Peloton-Announces-Q4-FY2026-Financial-Results.html">Q4 FY26</a>, compared to 73.6% on their subscription. Hugely different. This is where expensive-to-make hardware boosts its value with a hardware subscription.</p>
<h3>Model 4: Hardware-first with subscription attached</h3>
<p>In this model, you buy the hardware first, then add a subscription. My plant care app and sensors from Willow follow this model. Likewise, Peloton fits into this bucket. Another popular hardware-first product is the Oura ring, where most revenue comes from the hardware (roughly an <a href="https://ringingthebell.substack.com/p/whoop-vs-oura-the-10-billion-question">80/20 hardware-to-subscription split</a>), but the subscription is a growing part of the model.</p>
<p>Another good example is <a href="https://uk.myskylight.com/">Skylight</a>, a digital photo frame. The irony is that on their landing page, they promote that no app or subscription is needed.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a909ddcf5948f50c3ce53e58ae8793fd3032b936-1352x746.png" alt=""/></figure>
<p>And it's true, you can use the frame without a subscription. But the app allows you to do so much more, such as:</p>
<ul>
<li>Sharing videos and photos</li>
<li>Managing remote settings via the app</li>
<li>Creating photo albums and mirroring slideshows on any screen</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ad66bc8ddce6b2933f5e90c39327ae04008bddb2-1380x704.png" alt=""/></figure>
<p>Their digital calendar product goes further, with additional app-based tools such as meal planning and chore tracking. You buy the frame first, but the app provides the extra value. And according to the numbers their founders <a href="https://subclub.com/episode/how-skylight-balances-growth-and-profit-for-sustainable-success-michael-segal-mark-ungerer-skylight">shared on the Sub Club podcast</a>, while the app is only 10% to 20% of their revenue, it's about half their profit.</p>
<p>They keep their fairness principle in mind, though: <strong>what the customers bought the device for — displaying digital photos — stays free.</strong></p>
<h3>Model 5: Membership-native hardware</h3>
<p>The most integrated model for combining offline and online products is membership-native hardware: you aren't paying for the actual hardware, just for the subscription.</p>
<p>This is the Whoop example, where <a href="https://sacra.com/research/whoop-at-1b-year-growing-103-yoy/">85% of revenue is the subscription</a>. You get the device for free <em>as long as you have a subscription</em>. About 10% of their revenue is from selling accessories, like paying for a specific band: physical goods layered on top of a membership core.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/89d20e26cb84332eb7bacc3f58c1b28cd6e849f4-1342x697.png" alt=""/></figure>
<p>For this model, if you stop paying, the device either stops working or has to be returned, but that's the deal you knowingly signed up for. Nothing you were originally promised is taken away.</p>
<p>This model is arguably the hardest to get right, because hardware typically runs on slim margins. Look at Mirror: the interactive fitness mirror cost around $1,500 upfront and still required a monthly subscription. Once the pandemic’s at-home fitness fad faded, demand dropped off. Lululemon, which had <a href="https://www.cnbc.com/2020/06/29/lululemon-to-acquire-at-home-fitness-company-mirror-for-500-million.html">acquired the company for $500 million in 2020</a>, <a href="https://athletechnews.com/peloton-lululemon-partner-on-content-as-mirror-gets-the-ax/">shut it down in 2023</a>.</p>
<p>That's a simplification of the story, of course. The pandemic-fueled boom in at-home fitness inevitably faded — we saw the same thing happen to Peloton — but it also highlighted just how difficult it is to build a sustainable business around both hardware and subscriptions. Tellingly, Lululemon then partnered with Peloton to offer digital fitness content instead. It kept the subscription and dropped the hardware.</p>
<h2>Where do hardware subscriptions go wrong?</h2>
<p>It's not just the high cost of hardware that can make this tricky to get right. It's the fairness question we started with. A hardware subscription feels fair when customers can keep using what they bought without paying again, unless ongoing payment was clear from day one. But there are a few cautionary tales to consider when planning your approach.</p>
<h3>1. Grandfather with double care</h3>
<p>Take the <a href="https://www.happiestbaby.co.uk/products/snoo-smart-bassinet">Snoo</a>, a $1,700 smart bassinet. It's an expensive purchase for something babies typically use for only six months, so many parents justify the cost knowing they can resell it afterward. When Happiest Baby <a href="https://www.fortune.com/2024/08/22/snoo-sleeper-bassinet-added-monthly-subscription-parents-mad">added a $20-per-month premium subscription in July 2024</a>, it tried to handle the change fairly: anyone who had already bought a Snoo kept those features for free.</p>
<p>Except that's where things fell apart. The grandfathering applied to the original owner's account, not the device itself. Overnight, a used Snoo no longer came with the same features it had the week before. Secondhand buyers suddenly had to pay extra for functionality the product was advertised to have, while original owners saw the resale value of their bassinets fall. Technically, nothing had been taken away from existing customers. In practice, though, the promise of ownership had been broken — just one transaction downstream.</p>
<p><strong>The lesson: </strong><a href="https://www.revenuecat.com/blog/growth/peloton-retention-takeaways/">if you change the deal</a>, let existing customers keep the old one for as long as possible. And don't just think about your direct customers either. If your product has a healthy secondhand market, price or rule changes may have an additional negative backlash.</p>
<h3>2. Never break a core function</h3>
<p>Another cautionary tale is <a href="https://www.whoop.com/">Whoop</a>. For years, the company advertised free hardware upgrades after six months of membership. But when it launched the Whoop 5.0, members suddenly found that upgrading <a href="https://techcrunch.com/2025/05/11/fitness-tracker-whoop-faces-unhappy-customers-over-upgrade-policy">required either a 12-month membership extension or a $49–$79 fee</a>. After widespread backlash, Whoop reversed course. The episode showed that fairness isn't just about the fine print; it becomes anchored to whatever promise customers have come to expect, even in a subscription-first business.</p>
<p><strong>The lesson: </strong>if you're selling a physical product, customers should always be able to keep using it without paying another penny, unless ongoing payments are clearly part of the deal from day one (like in Model 5).</p>
<h3>3. Price under the disgust line</h3>
<p><a href="https://uk.myskylight.com/">Skylight</a> lived this one. They doubled their Calendar subscription from $39 to $79 with minimal impact on attach or retention. $99 penciled even better on paper, but their customer research put it firmly in &quot;not worth it&quot; territory, so they stopped at $79 and left the money on the table, <a href="https://subclub.com/episode/how-skylight-balances-growth-and-profit-for-sustainable-success-michael-segal-mark-ungerer-skylight">as they shared on Sub Club</a>.</p>
<p>There's a difference between what will maximize revenue and what will build lasting trust. I've always said that high prices come with high expectations, but they also raise the stakes: when someone has already paid a premium for a physical product, they're far more likely to feel cheated if they're asked to keep paying to unlock or maintain its core value.</p>
<p><strong>The lesson</strong>: In theory, you can often charge more by treating the hardware and the subscription as separate products. In practice, bundling them too aggressively can damage the relationship with your customers. Sometimes it's worth leaving a little money on the table in exchange for stronger retention, better word of mouth, and the reputation that comes from treating customers fairly.</p>
<h3>4. Communicate what the money funds</h3>
<p>Oura learned this one the expensive way. In 2021, they <a href="https://techcrunch.com/2021/12/02/oura-ring-3-review">put previously-free metrics behind a new membership</a> and asked people to trust that the fee would fund ongoing development, before showing any of it. Even with free <a href="https://www.revenuecat.com/blog/growth/lifetime-subscriptions/">lifetime memberships</a> for Gen2 upgraders (which some rumors say were the ‘cost’ of the change), the move drew backlash. Five years later, the same subscription barely raises an eyebrow, because now <a href="https://finance.yahoo.com/news/ceo-11-billion-oura-explains-171720178.html">their CEO can point at receipts</a>: 14 new features and two integrations shipped in a year, framed explicitly as what the membership pays for.</p>
<p><strong>The lesson:</strong> A subscription for a hardware product feels fair if the money visibly maps to something being made or added (e.g. storage, data, calculations) or the improvements it enables. If you can't point to what shipped or is improving, users may question it.</p>
<p>Note that with all of this,<strong> the takeaway is not ‘don't do physical goods’. </strong>It's really ‘think through all the levels of fairness and what the expectations of your customers are’. And keep these four rules in mind.</p>
<h2>The e-commerce economics app people underestimate</h2>
<p>A final consideration before you go down this route: keep in mind what it's really like to run an e-commerce business. I've worked closely with so many e-commerce founders and know the horror stories of running out of stock, high returns because of issues, couriers letting you down, and more.</p>
<p>There's a reason I love apps — and <em>only</em> work with apps these days: none of that, and all of the data.</p>
<p>In exploring this world, keep in mind what the margins look like. They will often be far lower than digital subscriptions. The risk and work around returns, inventory, and stock management is huge, and so are the cost and effort of building up fulfillment.</p>
<p>We haven't even touched on the technical setup of it all, with many apps going down the route of either building a Shopify store or working with a third-party distributor. (You can use tools like Stripe on this side too, but it's worth exploring the setup upfront.)</p>
<p><strong>I believe the most successful apps win with a physical product when it fits the experience, gets your most valuable customers to spend even more with you, and builds retention loops.</strong></p>
<h2>Five questions to get you started</h2>
<p>So, are physical products right for you? Which approach should you consider? These are the five questions to ask yourself:</p>
<ol>
<li>Do your superfans already ask for merch? (Model 1 is permission, not strategy)</li>
<li>Is there a physical object that would make your digital product work better? (Start at Model 2)</li>
<li>Would your subscription survive shipping delays? (Go to Model 3)</li>
<li>If you sell hardware: what did the customer buy, and does it work forever without you? (Model 4)</li>
<li>Could you afford to give the hardware away? (Model 5 is a financing model, not a product decision)</li>
</ol>
<p>I never expected to be attached to a plant sensor. To be curious what Downstairs Denise had to say about the lighting situation, to experiment with placement, or even to shower with a plant. I also never expected to be happy to pay a subscription on something that’s already doing its job. But I am, because my little sassy plants keep showing me that the subscription adds extra value: I get to track them over time and see the impact of learning and taking better care of them.</p>
<p>I don't feel obliged to subscribe. But as I buy more sensors, it adds value. It all comes down to fairness and thinking through (from your customer’s perspective) what they should or shouldn’t ‘need’ to pay for. I shouldn't have to pay for an app when receiving a gift, and I don’t. But I want to pay to learn, to nerd out on all my plants' data, so I’ll happily pay. The offer was worth it for me as a customer. This perspective is where you need to start.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Cal AI paid UFC creators thousands of dollars to post, and got no download spike at all]]></title>
      <link>https://www.revenuecat.com/blog/growth/jake-castillo-cal-ai-sub-club-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/jake-castillo-cal-ai-sub-club-podcast-2026</guid>
      <pubDate>Wed, 14 Oct 2026 13:41:27 GMT</pubDate>
      <dc:creator><![CDATA[David Barnard]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Jake Castillo kept betting anyway: a creator he signed at 10,000 views a video hit 7 million in month three.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/d81a54c515d808c6664fa23ccf0df01ad6437206-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>The logic was sound. UFC fighters track their food meticulously when they cut weight, so a calorie tracker that works from a photo saves them weighing everything, and the fight audience is enormous. Cal AI signed a couple of UFC creators and waited. &quot;The UFC creators we signed posted and crickets,&quot; says Jake Castillo, Cal AI's co-founder and COO. &quot;No download spike. Nothing.&quot; Asked whether these were big creators, he answers with the bill: &quot;thousands and thousands of dollars spent.&quot;</p>
<p><a href="https://www.youtube.com/watch?v=22wFjjVyXUI">Watch on YouTube</a></p>
<p>The explanation came from the audience data. &quot;Is the average person watching? UFC is doing so on their couch as they're drinking beer and eating pizza with their friends,&quot; Jake says. &quot;The last thing they want to think about is tracking their calories, because that's just like going to give them bad news.&quot; Mukbang creators, who eat 10,000 calories in a sitting, were the same mistake from the other side: viewers watching someone else's cheat day are planning their own, not counting it.</p>
<p>What replaced the on-paper logic was a method anyone can run from their phone. &quot;Views can lie. Followers can lie, but comments cannot,&quot; Jake says. For a niche, read the comment sections of its biggest creators and look for people asking for a solution your app solves. For an individual creator, the comments separate the one in ten who converts from the nine who don't. Two women post the same leg-day routine; under one it is fire emojis from men, under the other it is &quot;thank you so much for posting this video. I was really unsure of how to work out my hamstrings or quads or glutes.&quot; Jake's line on which one converts: &quot;I think it's pretty obvious which one will.&quot;</p>
<h2>The magic moment test</h2>
<p>Before any of that, Cal AI had to pass a test of its own. &quot;What we called it was the magic moment,&quot; Jake says. &quot;It was like a one, two, three punch. You scan, analyse results.&quot; When a fitness creator making a what-I-eat-in-a-day video scanned a plate on camera, viewers did a double take, and the double take is what sold the app. &quot;If your app doesn't have a magic moment, it doesn't lend itself well to a seamless integration because it's not going to cause that double take.&quot; An app that needs a paragraph of explanation should run paid ads with a hard sell instead, because &quot;if it feels like an ad, it's not going to go viral. If it's not going to go viral, it's not going to be profitable.&quot;</p>
<p>The test shaped details most people copying the playbook never saw. A streaks pop-up fired after the first scan of the day and broke the three-beat rhythm, so &quot;we would make sure our influencers didn't show their first scan of the day.&quot; Creators with subtle integrations got an on-screen caption naming the app; creators whose integration was already obvious didn't, because the caption cost views. A dedicated creator build put the logo on every screen. &quot;No marketing program should ever feel like a finished product,&quot; Jake says.</p>
<h2>The one number that caps what you can pay</h2>
<p>The deal math starts with revenue per thousand views, which Jake says is easy to find early on: with nothing else running, a creator's post shows up as a spike in a flat download chart. &quot;Once you get that number, it's like you can't pay like a CPM that is higher than that RPM. Otherwise you are guaranteed to not be profitable.&quot; Creators will push back. &quot;They may say no, like that's not I want to get paid more. But it's like you need to understand there is no other no other alternative.&quot;</p>
<p>From there the structure follows the shape of the returns. Break-even is set by average views; the profit is in the outlier. &quot;Where you make your real money is when they have like one video go mega viral. And like all that is profit,&quot; Jake says. &quot;So whenever possible, I would try to do flat rate deals because it allows you to capture the asymmetry that is algorithmic content.&quot; The creator in the byline is the case for it, and for the bets Jake says the UFC miss never stopped them taking: signed somewhere around the 12th or 15th deal on the strength of his content, doing &quot;like 10,000, 12,000 views&quot; for two months, then &quot;month three, he had a video get 7 million views and then like for like from that point forward, it was like never under 500,000 views.&quot; Jake's framing: &quot;A cult like following is a lagging indicator. If you're a great creator. The cult like following will come.&quot; Sign before it does, and the price is lower.</p>
<p>At scale the same discipline became a system. Cal AI had &quot;50 to 70 influencers posting on a day,&quot; which makes attribution murky, so Jake built what he calls a renewal score that weighs the secondary signals before anyone gets re-signed. He ran it from spreadsheets for a long time, and is releasing the software he eventually built in its place at the end of October.</p>
<h2>Jake Castillo on why the Cal AI clones were never going to make it</h2>
<p>Cal AI posted its wins and losses on Twitter as they happened, and clones followed. Jake wasn't worried. &quot;We knew that if people were building a Carly clone just because they heard about our success, they were never going to make it,&quot; he says, Carly being the transcript's rendering of Cal AI. &quot;The reason why we were successful was like, we were obsessed.&quot; He was dreaming about influencer deals, and the obsession produced &quot;a bunch of little, small, incremental improvements in those small incremental improvements compound over time to be an insurmountable mountain that competition can't climb.&quot; The Twitter threads about clone revenue sent traffic to Cal AI anyway.</p>
<p>He is equally direct about people copying him. &quot;The easiest way for an app founder to blow a ton of money is to copy what I did or copy what you saw on a podcast because like, my app is not your app.&quot; The creator thesis worked because health and fitness has more creators than any other niche and almost no brands for them to work with, and those creators were already filming the food Cal AI scans. An astrology app, he points out, has a few hundred creators and the rival astrology apps have probably already signed them to exclusivity. The price is the other thing not to copy. Cal AI sat among the cheapest calorie trackers at roughly $30 a year because, in co-founder Blake Anderson's framing, &quot;we're not optimising for profit. We're optimising for mindshare,&quot; and because a young audience that hasn't lived with the problem for long won't pay much to solve it. Different audience, different answer.</p>
<p>In <a href="https://www.youtube.com/watch?v=22wFjjVyXUI" target="_blank" rel="noopener noreferrer">the full episode</a>, Jake also explains why Cal AI's viral creator posts underperformed as paid ads until the team made dedicated creative, how a five-star hotel lobby with a rickety elevator explains funnel congruence, and what he can and can't say about the MyFitnessPal acquisition.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[8 Updates to help you build better paywalls in RevenueCat]]></title>
      <link>https://www.revenuecat.com/blog/company/8-updates-to-help-you-build-better-paywalls-in-revenuecat</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/8-updates-to-help-you-build-better-paywalls-in-revenuecat</guid>
      <pubDate>Tue, 13 Oct 2026 21:56:17 GMT</pubDate>
      <dc:creator><![CDATA[Courtney Touchstone]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[A new Paywall AI Editor, smarter custom components, localized images, foldable previews, and better paywall measurement]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/93220a92f605deb81deb65441054bf7089d7254c-1774x887.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Led by the third version of the Paywall AI Editor — now available to everyone — September was a big month for RevenueCat Paywalls: with updates that make it faster to build a paywall, easier to tailor it to each individual user, and clearer to see how they respond.</p>
<p>Here's a quick breakdown of what's new:</p>
<ul>
<li><strong>Paywall AI Editor v3</strong>: get from idea to working design faster with new starter screens, better image generation, and custom variables created right from the conversation</li>
<li><strong>Multi-step awareness</strong>: the AI Editor now sees your whole multi-step paywall while you edit any single screen</li>
<li><strong>Custom components</strong>: build your own components that read your paywall's Offering, Packages, and variables, and react when they change</li>
<li><strong>Localized images</strong>: assign a different image to each locale, including light and dark variants</li>
<li><strong>Foldable previews</strong>: preview folded and unfolded layouts for the upcoming iPhone Duo and the Galaxy Z Fold7</li>
<li><strong>Paywall interaction callbacks</strong>: send interaction events from native and hybrid SDKs to your own analytics tools</li>
<li><strong>Custom-paywall views in charts</strong>: see inferred views of your custom paywalls in the Conversion, LTV, and Abandonment charts</li>
<li><strong>Faster housekeeping</strong>: duplicate published paywalls and batch-delete assets in the media gallery</li>
</ul>
<p>To try the updates, open your project's 'Paywalls' page. Keep reading for the details on each change.</p>
<h2><strong>Build faster with the new Paywall AI Editor</strong></h2>
<p>The third version of the Paywall AI Editor is out now for all users, with new starter screens and onboarding to help you go from an idea to a working design in less time.</p>
<p><strong>What's new</strong></p>
<ul>
<li><strong>Better image generation</strong>: generate visuals that fit your design — now including images with transparent backgrounds, and refine them as you work</li>
<li><strong>Custom variables from the conversation</strong>: ask the editor to create the variable your dynamic content needs without leaving the chat; your app still supplies the runtime values, such as customer-specific information</li>
<li><strong>Awareness of multi-step paywalls</strong>: when you edit one step, the editor can see the rest of the workflow, so its suggestions keep layouts and messaging consistent across screens</li>
</ul>
<p><a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls">Explore the Paywall AI Editor</a> and <a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/variables">learn about custom variables</a>.</p>
<h2><strong>Design for every customer, language, and screen</strong></h2>
<p>A paywall that converts in one market on one device can fall flat in another. These updates give you more control over how your paywall adapts to each customer.</p>
<h3>More design flexibility with custom components</h3>
<p>Go beyond the standard paywall components with your own HTML, CSS, and JavaScript. Build custom comparison tables, animations, and visuals that fit your app’s design.</p>
<p><strong>What's new</strong></p>
<ul>
<li><strong>Use your paywall’s data</strong>: custom components can now access your Offering, Packages, custom variables, and device context</li>
<li><strong>Update as customers choose a plan</strong>: change the content or appearance of your component when a customer selects a different Package</li>
</ul>
<p>Build a comparison table using your available Packages, or an animation that highlights the selected plan, with control over how it looks and behaves.</p>
<p><a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/components#custom-component">Learn how custom components work</a>.</p>
<h3><strong>Localize your images, not just your copy</strong></h3>
<p>A translated headline next to an English-language screenshot only gets you halfway. You can now assign a different image to each locale — including light and dark variants — all within one paywall, instead of a separate design per language.</p>
<p>Set localized images in the Paywall Editor, on the 'Paywall Localization' page, or by asking the Paywall AI Editor. <a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/localization">See how to localize images</a>.</p>
<h3><strong>Preview foldable layouts ahead of the iPhone Duo launch</strong></h3>
<p>A layout that works on a folded display may need another look once the screen opens up. The editor now includes folded and unfolded previews for the upcoming iPhone Duo, plus previews for the Galaxy Z Fold7, so you can catch layout issues before customers do.</p>
<p>Read more about <a href="https://www.revenuecat.com/blog/engineering/paywalls-on-iphone-duo">preparing your paywalls for iPhone Duo</a>.</p>
<h2><strong>See how customers respond</strong></h2>
<p>Purchase results tell you whether someone converted. The new updates help you understand what happened along the way.</p>
<h3><strong>Send paywall interactions to your own analytics</strong></h3>
<p>Paywall interaction callbacks are now available across native and hybrid SDKs, so your app can receive interaction events and forward them to your analytics tools. Study paywall engagement alongside the rest of the customer journey and decide what to test next.</p>
<p><strong>What's new</strong></p>
<ul>
<li><strong>Interaction callbacks</strong> across native and hybrid SDKs</li>
<li><strong>Real-time listeners</strong> in React Native and Flutter for the <code>presentPaywall</code> and <code>presentPaywallIfNeeded</code> APIs</li>
</ul>
<p><a href="https://www.revenuecat.com/docs/tools/paywalls/displaying-paywalls">Explore paywall integration options</a>.</p>
<h3><strong>See custom-paywall views in your charts</strong></h3>
<p>If you build your own purchase screens, the Paywall Conversion, LTV, and Abandonment charts now include inferred views of your custom paywalls. That gives you more context on conversion, customer value, and abandonment without leaving RevenueCat. Keep in mind that inferred views are inferred, rather than directly recorded impressions.</p>
<h2><strong>Spend less time on housekeeping</strong></h2>
<p>Two smaller updates to cut the busywork between one paywall version and the next.</p>
<ul>
<li><strong>Duplicate published paywalls</strong>: duplication now works for published paywalls, so a new headline, layout, or offer test can start from a design you know works</li>
<li><strong>Batch-delete assets</strong>: select several assets in the paywall media gallery and remove them at once, making clean-up quicker after a round of design exploration</li>
</ul>
<h2><strong>Ready to get started?</strong></h2>
<p>Open your project's 'Paywalls' page to try the new tools. A good first step: Duplicate a paywall you already have, pick one improvement (like a localized screenshot or a custom component built on your Packages), and test that version.</p>
<p>For the SDK features, update your RevenueCat SDK and follow the platform-specific instructions in the <a href="https://www.revenuecat.com/docs/tools/paywalls/displaying-paywalls">paywall integration docs</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[What's New: Paddle discounts, custom checkouts, and cleaner data come to RevenueCat Funnels]]></title>
      <link>https://www.revenuecat.com/blog/company/what-s-new-paddle-discounts-custom-checkouts-and-cleaner-data-come-to-revenuecat-funnels</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/what-s-new-paddle-discounts-custom-checkouts-and-cleaner-data-come-to-revenuecat-funnels</guid>
      <pubDate>Tue, 13 Oct 2026 21:51:52 GMT</pubDate>
      <dc:creator><![CDATA[Courtney Touchstone]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Automatic Paddle discounts, custom checkout links, and clearer data on which visitors become customers]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/ad151391549a650d3e21de6615353d720f4630e8-1774x887.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>September 2026 brought a round of RevenueCat Funnels updates built to help you get more value from your acquisition traffic.</p>
<p>Turning visitors into customers takes two things: a clear path to purchase, and reliable data about what happens along the way. This release improves both, with automatic Paddle discounts, custom checkout URLs, and better visibility into which visitors become customers.</p>
<p>Here's a quick breakdown of what's new:</p>
<ul>
<li><strong>Automatic Paddle discounts</strong>: apply Paddle discounts from URL parameters and target them to specific Packages, with no code entry at checkout</li>
<li><strong>Custom checkout URLs</strong>: point purchase buttons at your own checkout and use RevenueCat Funnels to build the journey leading up to it</li>
<li><strong>Hashed checkout email in Google Tag Manager (GTM) events</strong>: give your downstream tags an extra signal for conversion matching</li>
<li><strong>Bot filtering</strong>: funnel analytics and ad pixels now skip bot traffic, and we've cleaned up affected historical reports</li>
<li><strong>PostHog funnel events</strong>: send web funnel and web purchase redemption events to PostHog to see where visitors drop off</li>
<li><strong>Workflow awareness in the Paywall AI Editor</strong>: the editor now sees every screen in your funnel while you edit one</li>
<li><strong>Language detection across regional locales</strong>: match more visitors to a translation they can read</li>
<li><strong>Numeric input validation</strong>: set minimum and maximum values for numeric answers such as age, height, or weight</li>
</ul>
<p>All updates are available in RevenueCat Funnels now. Here's a closer look at each one and how to put it to work.</p>
<h2><strong>Make checkout work the way you want</strong></h2>
<p>Every extra step between a visitor's decision and a completed purchase is a chance to lose them. These updates remove friction at checkout and give you more control over where the purchase happens.</p>
<h3><strong>Carry your promotion through to checkout with automatic Paddle discounts</strong></h3>
<p>A customer who clicks a promotional link shouldn't have to remember a code and enter it again at checkout.</p>
<p><strong>What's new</strong></p>
<ul>
<li><strong>Discounts from URL parameters</strong>: RevenueCat Funnels now applies Paddle discounts automatically from URL parameters</li>
<li><strong>Package-level targeting</strong>: select a Paddle discount in the dashboard and apply it to specific Packages, so you control which plans get the offer</li>
</ul>
<p>For example, you could promote a discount on an annual subscription while leaving the monthly plan at its regular price. Customers who pick the eligible Package get the discount with no extra code-entry step, and your campaign's offer carries all the way through to purchase.</p>
<h3><strong>Connect your funnel to the checkout that works for your business</strong></h3>
<p>You can now configure purchase buttons in <a href="https://www.revenuecat.com/guides/web-to-app-funnels">web-to-app funnels</a> to use a custom checkout URL. If you already have a checkout experience you want to keep, use RevenueCat Funnels to build the journey leading up to it, then send customers there when they're ready to buy.</p>
<p>For teams whose checkout requirements previously blocked a funnel launch, this opens a path forward. Work on the questions, messaging, and offer presentations that lead to purchase while keeping the checkout you've chosen.</p>
<h2><strong>Make acquisition decisions with better data</strong></h2>
<p>Knowing which campaigns bring paying customers is essential when you decide where to spend your next acquisition dollar. These updates improve purchase matching, remove misleading traffic, and show you where visitors drop off.</p>
<h3><strong>Match more purchases with hashed checkout email in Google Tag Manager</strong></h3>
<p>Funnel purchase events sent to GTM now include the hashed checkout email – an additional signal for conversion matching. With the right downstream tags configured, it can help connect purchases to acquisition activity and give you a clearer basis for judging campaign performance.</p>
<p>RevenueCat loads your configured GTM container on published funnels and sends events into its data layer. You control how those events map to your analytics and advertising tools. <a href="https://www.revenuecat.com/docs/integrations/attribution/google-tag-manager">See the GTM setup guide</a> for more info.</p>
<h3><strong>Only see real prospects with bot filtering</strong></h3>
<p>Crawlers inflate visitor counts and make conversion rates look worse than they are. Now, you can evaluate the behavior of real prospects and avoid changing a funnel, or cutting a campaign, because automated traffic distorted the results.</p>
<p><strong>What's new</strong></p>
<ul>
<li><strong>Bot filtering</strong>: funnel analytics and ad pixels now skip bot traffic</li>
<li><strong>Historical cleanup</strong>: we've cleaned up affected historical reports</li>
</ul>
<h3><strong>Find where visitors drop off with PostHog events</strong></h3>
<p>If your team uses PostHog, you can now analyze web-to-app funnel behavior in the same place as the rest of your customer journey. The PostHog integration now receives web funnel events and web purchase redemption events, alongside the purchase and paywall events it already gets.</p>
<p>See how visitors progress through your funnel, find where they leave, and connect more of the journey from web purchase to redemption. That gives your next experiment a sharper focus.</p>
<p><strong>To turn it on</strong>: Open 'Project settings → Integrations → PostHog', enable 'Send funnel events to PostHog', and set the event names to match your reporting conventions. Web purchase redemption has its own event-name setting.</p>
<h2><strong>Build funnels that are easier to complete</strong></h2>
<p>A few smaller updates help your funnel speak the right language, collect better information, and stay consistent as you edit.</p>
<ul>
<li><strong>Workflow awareness in the Paywall AI Editor</strong>: when you edit one funnel screen, the editor can now see the other screens in the workflow. Its edits fit the surrounding experience, so you spend less time keeping messaging and design consistent across steps.</li>
<li><strong>Language detection across regional locales</strong>: RevenueCat Funnels now matches supported localizations using the language part of a visitor's locale, even when the region differs. More visitors see a translation they can read instead of missing it on an inexact regional match.</li>
<li><strong>Numeric input validation</strong>: set minimum and maximum values for numeric answers such as age, height, or weight. Customers catch invalid entries as they go, and you get more reliable data for the experience you're building.</li>
</ul>
<h2><strong>Ready to get started?</strong></h2>
<p>Pick the update that matches where revenue is getting stuck:</p>
<ul>
<li><strong>Visitors reach your offer but don't buy?</strong> Start with automatic Paddle discounts and your checkout setup.</li>
<li><strong>Need to decide which campaigns or steps deserve attention?</strong> Start with conversion matching, cleaner traffic data, and PostHog events.</li>
</ul>
<p>Not sure which to tackle first? Ask <a href="https://www.revenuecat.com/docs/tools/rico">Rico</a>, RevenueCat's AI-powered app growth advisor, in your dashboard or in Slack. Rico can benchmark metrics like trial conversion against apps in your category, flag RevenueCat features you aren't using yet, and answer setup questions from the RevenueCat docs.</p>
<p>Open RevenueCat Funnels in your project to get started, or follow the RevenueCat <a href="https://www.revenuecat.com/docs/tools/funnels">Funnels guide</a> to build your first web-to-app journey.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[It’s not you, it’s me: when to break up with a paid UA channel]]></title>
      <link>https://www.revenuecat.com/blog/growth/when-to-give-up-a-paid-channel</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/when-to-give-up-a-paid-channel</guid>
      <pubDate>Mon, 12 Oct 2026 10:00:00 GMT</pubDate>
      <dc:creator><![CDATA[David Vargas]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Seven checks to test your own setup, five signals the channel is broken, and a fix, pause, or kill framework]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/2fd47b7a00679eb6d152b91014c10f560128c721-3200x1700.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>I’ve been running app campaigns for long enough that I’ve seen one problem arise time and time again — and nobody seems to have a solid answer for it. <strong>When should you give up on a channel?</strong></p>
<p>We’ve all had it happen: an app is running <a href="https://www.revenuecat.com/guides/web-to-app-funnels/paid-ads">paid user acquisition campaigns</a> on one specific ad network and getting really bad results. There’s multiple reasons that could explain the performance, but after weeks (or even months) trying to address these issues, the performance hasn’t improved. Eventually, the Founder or CEO asks the questions:</p>
<ul>
<li>When should we give up on this channel?</li>
<li>How far should we go with different testing and experimentation?</li>
<li>Should we explore cheaper channels?</li>
</ul>
<p>In my <a href="https://www.revenuecat.com/blog/growth/how-to-build-a-ua-system-when-youre-a-one-person-team">UA playbook for one-person teams</a>, I said to pick one channel and make it a success before even thinking<em> </em>about a second one. But a few months ago I also said on LinkedIn that you should explore cheaper channels if you’ve been trying one channel for three weeks with no results.</p>
<p>So which one is it? Should you persist, or should you move on?</p>
<p>The honest answer is: both, depending on the situation. And that’s exactly the problem. I’ve seen founders killing a channel after five days because “TikTok doesn’t work for us”, and others continuing to burn money on a channel for months because the modeled conversions in the dashboard still look nice.</p>
<p>This decision is only getting harder with new privacy limitations, AI deleting barriers, CPMs increasing, and a whole new generation of app builders shipping apps at an unimaginable speed.</p>
<p>But there is a clear way to decide when to hold onto or let go of a channel — keep reading for my <strong>fix, pause, kill framework.</strong></p>
<h2>Quitting too early vs. holding on too long</h2>
<p>When this challenges comes up, you typically see the same two profiles again and again:</p>
<p><strong>The tourists</strong>: They launch Meta, <a href="https://www.revenuecat.com/blog/growth/tik-tok-ads-guide-mobile-apps">TikTok</a>, and Apple Ads at the same time with $100–200/day budgets while they check hourly delivery. After five days, they discard most of them and only stick with one channel, concluding that the other channels “don’t work for our app”.</p>
<p>When you look at the channels, you’ll see that none of them got a proper setup (campaign optimization goal or creative strategy are often the main mistake) or none of them got enough signals. As Lucas Moscon explained in his article on <a href="https://www.revenuecat.com/blog/growth/ad-channel-diversification">ad channel diversification</a>, launching more channels <em>doesn’t</em> mean lower risk if your budget, audiences, or team capacity can’t afford multiple learning phases at once.</p>
<p><strong>The loyalists:</strong> This is the opposite case — and in my experience, the most expensive one. These are marketers that used to have a working channel bringing consistent growth. For some reason, it stopped working and yet, they keep burning money on it, even when <a href="https://www.revenuecat.com/glossary#customer-acquisition-cost-cac">customer acquisition cost (CAC)</a> has been going up for weeks. Sometimes it’s an emotional tie (“it worked last year”), sometimes it’s down to knowledge gaps (the UA responsible “owns” that channel) and very often it’s a data problem: they’re either cannibalizing organic or trusting modeled conversions too much.</p>
<p>To support these people and guide your decisions, you need a clear framework for when a channel deserves more time, and when it doesn’t.</p>
<h2>7 Checks to see if it’s you: the pre-kill channel checklist</h2>
<p>Let’s be super honest here: most of the time when someone says “this channel doesn’t work for us”, the problem is not the channel, it’s something on the company’s side that is being <em>exposed</em> by the channel. So before blaming the channel or cutting it off, go through this checklist. If you answer ‘no’ to any of these questions, the problem might be on your end.</p>
<h3><strong>1</strong>. <strong>Does your product convert within benchmarks?</strong></h3>
<p>First off, is your product converting as it should be. Use the <a href="https://www.revenuecat.com/healthscore/">RevenueCat App Health calculator</a> or the <a href="https://www.revenuecat.com/state-of-subscription-apps">State of Subscription Apps report</a> to find the relevant benchmarks for your vertical, then examine:</p>
<ul>
<li>Install rate</li>
<li>Install-to-trial rate or install-to-paid rate (if you don’t have a free trial)</li>
<li>Trial-to-paid rate</li>
</ul>
<p>Obviously, if your <a href="https://www.revenuecat.com/glossary#conversion-rate-cvr">conversion rates (CVR)</a> are far below your category, <strong>no channel will save you.</strong> You will just see the same numbers but with different CPMs.</p>
<p>I personally lived this scenario with one of my clients: we tested Google, TikTok, <a href="https://www.revenuecat.com/blog/growth/7-meta-ad-testing-frameworks-for-subscription-apps">Meta</a>, <a href="https://www.revenuecat.com/blog/growth/apple-search-ads-guide/">Apple Ads</a>, even influencers, and we struggled for months, testing thousands of creative angles. Ultimately, our install-to-paid rate was simply too low for the CAC that we needed to achieve in order to sustain business margins. Until we stopped trying to fix that with an <a href="https://www.revenuecat.com/blog/growth/introductory-offers-apps/">introductory offer</a>, and focused on creating a <a href="https://www.revenuecat.com/blog/growth/hard-paywall-activation-journey">solid activation journey</a>, we couldn’t run paid UA consistently.</p>
<h3><strong>2</strong>. <strong>Does your optimization event have enough volume?</strong></h3>
<p>The theory is that you need around 10 events per day (or 50 per week) to exit learning phases. According to my experience, the reality is that after 30 per week, you can judge the quality you get on that channel. However, if you’re running campaigns with $50/day optimizing towards subscriptions, you should expect your performance to fluctuate constantly — because you’re not feeding the algorithm enough signals.</p>
<p><a href="https://www.revenuecat.com/blog/growth/thomas-petit-podcast-paid-ua/">Thomas Petit</a> has said this multiple times: <strong>focus on actions that happen within the first 24 hours. </strong><a href="https://www.revenuecat.com/blog/growth/post-purchase-screen">55% of 3-day trial cancellations happen on </a><strong><a href="https://www.revenuecat.com/blog/growth/post-purchase-screen">Day 0</a></strong>, so first-24 hours actions are critical. On top of that, I recommend moving to a proxy event if you can’t afford optimizing directly towards your subscription event.</p>
<h3><strong>3. Did you test enough creatives?</strong></h3>
<p>We all know that targeting is now done through creatives, not audience settings (with some exceptions) so having a proper <a href="https://www.revenuecat.com/blog/growth/overanalyze-creative-analysis-paid-ads/">creative mix</a> with enough volume is key to really see if your CTRs, hook rates, and hold rates are according to the benchmarks for every user persona you think you can target.</p>
<p>If you tested a channel with three creatives and never rotated them, you didn’t test the channel. You tested three creatives. As a reference, <a href="https://www.revenuecat.com/blog/growth/subscription-app-creative-testing">in my $0–500 framework</a>, I recommend starting with 8–10 creatives and rotating the ones that don’t spend every 48 hours in Meta and every 24 hours on TikTok (<a href="https://www.revenuecat.com/blog/growth/detect-ad-fatigue-mobile-apps">creative fatigue</a> is much faster there).</p>
<h3><strong>4. Is your budget enough for that specific channel?</strong></h3>
<p>There are some specific channels where small budgets won’t work. For example in Moloco, you will never see the reality of the channel if you spend $100 a day because the algorithm needs a much longer time and a greater amount of signals to exit the learning phase (minimum is $500/day).</p>
<p>Another example is Apple Ads, where you can’t have a $50/day budget if you want to test 15 generic keywords and 20 competitor keywords. You would need weeks to really figure out the performance. So before cutting a channel off, consider whether your budget is feeding it enough to see any real impact.</p>
<h3><strong>5. Are you measuring correctly?</strong></h3>
<p>This one is especially critical on iOS. <a href="https://www.revenuecat.com/blog/growth/skadnetwork-guide-subscription-apps">Probabilistic attribution</a> estimates which ad led to an install by modeling aggregated signals (like timing, IP address, and device type) rather than matching a confirmed user ID — so the numbers are educated guesses, not exact counts. This technique and modeled attribution (AEM, ICM, ADC, etc.) can under-report or over-report what a channel is really bringing. I've seen channels that looked unprofitable in the dashboard but were bringing dozens of unreported trials per week when checking the baseline uplift. And I've seen the opposite with DSPs and 24 hour view-through windows. If you haven't checked the baseline before vs. after launching the channel, you don't really know how it performs.</p>
<h3><strong>6. Is the delivery going to the right audience?</strong></h3>
<p>A cheap CPA can hide a terrible channel, and an expensive CPA can hide a great one. Check the age and placement distribution (when you can) and try to ask basic demographic questions on your onboarding so you can use first-party data to analyze before and after launching a channel.</p>
<p>If the algorithm is sending most of your budget to 18–24-year-olds who start trials but never pay, your cost per trial will look amazing while your <a href="https://www.revenuecat.com/glossary#return-on-ad-spend-roas">return-on-ad-spend (ROAS)</a> goes down. In one of my Meta cases, the core audience was men over 45, and the biggest win was shifting the delivery to the placements where they actually spent time.</p>
<h3><strong>7. Are you testing all the possible setups?</strong></h3>
<p>This one is critical in some cases: if your subscription app is struggling to convert with app campaigns but your metrics are within benchmarks or close to them, <strong>you should test other setups</strong> before discarding the channel. In these cases, I always start with app campaigns but in some channels these campaigns don’t use view attribution for example so switching to a <a href="https://www.revenuecat.com/blog/growth/web-to-app-paid-user-acquisition">web-to-app setup</a> with web sales campaign can really help you to make the numbers work. If this doesn’t work, I always try a <a href="https://www.revenuecat.com/guides/web-to-app-funnels">web-to-app funnel</a> as the last option since the campaigns for web funnels behave completely different from the CVR perspective.</p>
<p>Your honesty when answering these seven questions makes the difference between profit and losing out. So if, after going through this checklist, you can answer ‘yes’ to all of the questions, then yes: it’s time to look at the channel itself.</p>
<h2>5 Signals that indicate that it’s actually the channel</h2>
<p>Sometimes the channel is just more expensive for your audience at the moment you’re testing due to multiple reasons (higher competition, seasonality, user behavior within that placement, etc.) and no optimization will fix that. Here’s how to tell if that’s the case:</p>
<h3>1. There's a structural cost gap</h3>
<p>A few months ago, Meta was struggling on some of my accounts for two to three weeks despite having a correct targeting and setup so I moved part of the budget to a <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels">web-to-app campaign</a> in YouTube Shorts through Google DemandGen campaigns. I used the same creatives and the same conversion event but I got a much different result: YouTube Shorts got a CPM of $2–3 while Meta was giving me $7–8.</p>
<p>That’s an almost 3x cheaper (!) inventory without compromising quality (obviously I checked this after doing the switch) which brought my CAC down consistently.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/7544ca6a6e7cfc61b4327fd0428f928c40ca009f-1260x394.png" alt=""/></figure>
<h3>2. The platform broke (and it's not your fault)</h3>
<p>Sh*t happens, and sometimes we can’t do anything about it. While this is the least common case, it does happen. <a href="https://lnkd.in/p/eBCBj_Hu">One recent case I had</a> was with TikTok after they merged Manual and Smart+ campaigns in one single flow. I compared TikTok and Meta using the exact same creatives during 30 days:</p>
<ul>
<li>Travel app: Meta CPI $3–5 vs. TikTok CPI $27.50</li>
<li>Fitness app: Meta CPI $4.77 vs. TikTok CPI $30.50</li>
<li>A third app: +$40 CPI on TikTok</li>
</ul>
<p>CPMs and CTRs were almost the same on both channels so the gap wasn't coming from the audience or the creatives, but most likely from the real-time attribution for iOS after the merge. On top of that, TikTok was reporting 80–100% fewer installs than App Store Connect (it used to be 25–35%), and all the spend was going to one single ad.</p>
<p>Another example: a social casino app with a purchase-optimized Google campaign that had been running since November 2023. We tried to launch web-to-app on iOS, and the whole account got rejected due to a domain issue. After four days without activity the campaign never recovered its performance. Nothing changed internally, but with Google App campaigns, you literally control nothing over placements or creative combinations. So sometimes relaunching and praying is basically the only option.</p>
<p>In these cases, the answer is not ‘kill’, but ‘pause’: stop the bleeding, monitor the platform, and test again when the issue is fixed or you feel ready to have a new headache.</p>
<h3>3. There's no incrementality</h3>
<p>This is one that really fools many teams. The channel reports conversions and the MMP even confirms them but the business doesn’t move.</p>
<p>If the blended baseline doesn't move after adding extra spend, the channel is not bringing new users — it's taking credit for users you would have got anyway. It doesn’t matter how nice the CPA looks in the dashboard, that's a channel you have to stop (or at least re-test with a much more restrictive setup).</p>
<p>I have seen this with DSPs like AppLovin working with 24 hour VTA windows. I normally go with a six-hour view attribution window, and even then I sometimes see cases like this: when I test static banners on any DSP, they eat my organic conversions. You can clearly see how the organic baseline starts to decrease the exact same day I upload the banners (and the CPA of the campaign decreased by +50%!).</p>
<p>Some of the formats that DSPs recommend are tricky: 320x50, 300x250, and other tiny formats that are normally integrated into the UI of the apps where ads are served. The user in this case is just using that app and the ads are <em>part</em> of the interface... so you end up counting a ‘view’ that is <em>not</em> an effective view.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/c747712ff0caa78dfae62c9a13021f0479597910-1208x710.png" alt=""/></figure>
<h3>4. The conversion improves, but the unit economics don't close</h3>
<p>Sometimes a channel or campaign type does exactly what it promises, and it still doesn't make sense for your business because of the <a href="https://www.revenuecat.com/blog/growth/ai-feature-cost-subscription-app-margins">unit economics</a>.</p>
<p>When I tested Meta ROAS campaigns with AEM for a casino app, the results were really interesting: better age distribution (less spend on 18–24), better CVR, but CPMs were 100% higher than the mid-funnel CPA campaign. We were clearly accessing a more expensive tier of inventory but the CVR improvement couldn't compensate for it. Better users, better conversion, but still worse business overall.</p>
<h3>5. The opportunity cost is eating your main channel</h3>
<p>This is the <strong>silent killer</strong>. The reason that most teams overlook when they decide to start a new channel.</p>
<p>Every new channel (and every new ad group) puts pressure on your creative production. A new channel needs new formats, new creatives rotated, new attribution logic, and more time checking data to see the incrementality.</p>
<p>If that pressure comes at the cost of your main cash generator getting fewer fresh creatives or less attention, you're not diversifying, <strong>you're weakening what already works.</strong></p>
<h2>The decision framework: fix, pause or kill</h2>
<p>Once you go through the checklist and the five signals, your channel decision becomes much more straightforward. Broadly, there are three possible ways to move forward:</p>
<ul>
<li><strong>Fix:</strong> the checklist shows a problem on your side (not enough creatives, events with low volume, budget below the channel minimum, wrong audience distribution etc.). You can fix it and give the channel another full cycle before judging it.</li>
<li><strong>Pause:</strong> the problem is temporary or external: a platform bug, a product change on the ad network side, seasonality, an account issue. Pause, monitor what other advertisers are reporting (you can freely ask about that on <a href="http://growthsociety.co">growthsociety.co</a>), and re-test once waters are calmer.</li>
<li><strong>Kill:</strong> you have a structural cost gap, zero incrementality after a clean test (following the list of questions from the first section), or a CAC that your LTV doesn't cover even in the most optimistic scenario. You need to kill the channel and move on.</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0359de3c99d0bfdc12847ccaba9d02ff8d2c194c-3200x2320.png" alt=""/></figure>
<p>One more thing: killing a channel doesn't mean killing it forever. Channels change a lot; they change the algorithms’ functioning, introduce new campaign optimization goals, new creative formats, and more, so failing doesn’t mean forgetting that channel forever.</p>
<p>I always say that bad performance is never random. There’s always a driver: setups, signals, unit economics… Sometimes channels drive the bad news. Your job is to know which of those you're dealing with before you move the money.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Box Box's first message from Formula One was a legal notice, and its two founders were happy to get it]]></title>
      <link>https://www.revenuecat.com/blog/growth/ranjith-ramanan-kamal-kumar-box-box-club-launched-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/ranjith-ramanan-kamal-kumar-box-box-club-launched-podcast-2026</guid>
      <pubDate>Wed, 07 Oct 2026 15:47:32 GMT</pubDate>
      
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Ranjith Ramanan's co-founder check was a scroll through Kamal Kumar's Instagram, which turned up a Formula One car tattoo.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/6892bf6ba306f70c2e16e1594794a9a8cde188e4-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Box Box is the F1 widget app that Charles Leclerc has been spotted using and that the Mercedes, Haas, Aston Martin, and Alpine teams have all pointed their fans at. It was built by two strangers from Bangalore who met over a tweet, shipped it free in a month, and didn't quit their jobs until a Formula 2 driver asked to join the company. On this episode of Launched, Charlie Chapman talks to designer Ranjith Ramanan and engineer Kamal Kumar about how a side project for a sport they loved ended up with its logo on a race car.</p>
<p><a href="https://www.youtube.com/watch?v=2Lu4MDwtC54">Watch on YouTube</a></p>
<h2><strong>Formula One noticed Box Box, and sent lawyers</strong></h2>
<p>Kamal is clear that there is no formal relationship with Formula One yet. The first interaction came from the other direction. &quot;I think the first interaction that we had from Formula One was a legal notice that they sent us,&quot; he says. The app was listed as &quot;box, box, club, F1 widgets,&quot; as Kamal says it, and F1 is a trademark. It also used the sport's official font, and photos of drivers and cars.</p>
<p>They read the rules, renamed the app, rewrote the App Store subtitle and description, dropped the font, and replaced the driver photos with their own 2D and 3D representations made with outside artists. None of which dented the mood. &quot;We were quite happy about it, that it was not a huge deal,&quot; Kamal says. &quot;We knew that, okay, formula one is actually noticing us.&quot; Ranjith puts it more bluntly: &quot;Even if it was getting the legal notice. We were happy that they are using the product.&quot;</p>
<p>The relationship that did come was with Formula 2. Juan Manuel Correa, a former F2 driver, messaged Kamal on LinkedIn to ask what they were building. The founders had already turned down investors who talked only about money; after two or three calls they decided Juan shared the vision, and he joined the registered company's board as a strategic partner and brand ambassador rather than an investor. At the Abu Dhabi race he showed them his helmet, covered in Box Box logos, and the logo was on his car too. Charlie asks whether they paid for the sponsorship. They did not. &quot;You imagine your logo on the products,&quot; Kamal says, &quot;but then again, we saw it on a formula two car.&quot;</p>
<h2><strong>A Mexican fan's TikTok and a Mercedes tweet did the marketing</strong></h2>
<p>Neither founder had a marketing background, and their first guess about the market was India. Then the download spikes showed up in Brazil and the US. Kamal: &quot;We were wondering, you know, why is this happening? How could this happen?&quot; The answer was a fan in Mexico who posted the app on TikTok, a platform the founders couldn't even open from India. The video went to a million views and the downloads followed it across Mexico, Brazil, and the US. The first week brought close to 5,000.</p>
<p>The bigger wave came with iOS 16. Box Box already had home-screen widgets, so when Apple added lock-screen widgets the team converted its concepts and was live on day one. A share-your-lock-screen trend started the same week, and the Mercedes F1 account joined it, asking fans to post lock screens with Mercedes wallpapers. The screenshots carried Box Box's widget with no name on it. &quot;The other Mercedes fans, they were like intrigued, like, what is this?&quot; Kamal says, and the fans who knew answered them. &quot;I think in that one week after the tweet, we had like 250,000 downloads.&quot; Haas and Aston Martin followed on TikTok, and the second viral moment arrived when a user spotted the lock-screen widget on Charles Leclerc's phone in one of his own vlogs.</p>
<p>Charlie's read on the TikTok moment is the lesson for anyone building something visual: luck was involved, but the app had to look good enough to catch an eye in a video before any of the rest could happen. Ranjith has a name for that. He designs for the first-open &quot;wow&quot; and for what he calls the multiplayer effect, where &quot;you see the app and you become the UGC&quot; and show it to the next person.</p>
<h2><strong>Users asked for a way to pay, so Ranjith designed things worth paying for</strong></h2>
<p>The first version had no paywall at all, on purpose. The founders added a WhatsApp support line, bought a SIM card for it, and answered every message, expecting feature requests. What they got was different. &quot;The users were like, you guys are doing such a good job, I would like to pay you,&quot; Kamal says. &quot;And you're like slightly taken aback.&quot; Charlie's response: it's always a good sign when your users are asking to give you money.</p>
<p>Charging came with a rule. So much had been given away that the founders refused to take any of it back from the community that had supported them, which meant the paid tier had to be built from new things: widget themes, an Apple Watch app, complications. The first goal was small, earning back the $100 developer account fee. The subscription was yearly and timed to the racing season, so a fan pays for a season and decides again the next year.</p>
<p>They priced it, as Kamal remembers it, at $5.99 a year, and users told them it was too little. &quot;You are doing so much, but then you are asking so little,&quot; he recalls them saying. &quot;Why don't you add like a place in your app where I can give you more money?&quot; The answer was tiers through RevenueCat, including a higher-priced option, with lower-tier subscribers still receiving some of the new features. The end-of-season survey that settled the pricing drew around 5,000 to 6,000 responses, which Ranjith admits he pads with eight or nine questions when he only wants two or three answers.</p>
<h2><strong>Ranjith hides new designs from Kamal, because Kamal builds them overnight</strong></h2>
<p>The two met because Ranjith, a designer who doesn't code, posted an F1 widget concept on Twitter after every developer he knew turned him down. Kamal replied: &quot;bro, I'm interested.&quot; They had, it turned out, been sharing a wallpaper album with a mutual friend for a year without ever speaking. Ranjith's due diligence was Instagram, where he found a photo of an F1 car tattoo and decided, in his words, &quot;he is the person.&quot;</p>
<p>They built the first version in about a month, working, as Ranjith puts it, like Batman: after his US work calls ended at 10 p.m., messages from 11 until 3 or 4 in the morning. The working relationship has one quirk. Kamal is so keen on the sport that if Ranjith shows him a half-finished concept, there's a build in his inbox the next morning. &quot;So I need always to hide from him any new future,&quot; Ranjith says. &quot;I'll hide him. And until I finally myself. Oh, this is fine to show him. Then I'll put it there.&quot;</p>
<p>Charlie's takeaway is one he gives often on the show: a designer-and-engineer pair forces the design to happen before the code, and the engineering problem of making a beautiful design work is more fun than designing around what's easy to build. Kamal agrees from the other side. The healthcare and fintech apps he'd built before were, in his word, boring; Ranjith's designs were what kept him up at night.</p>
<p>There's more<a href="https://www.youtube.com/watch?v=2Lu4MDwtC54" target="_blank" rel="noopener noreferrer"> in the episode</a>: the Apple developer centre email that looked so much like spam that Ranjith half expected a kidnapping when he drove there, the day both founders quit their jobs, the end-of-season beta trick that gives fans a cliffhanger, and why the team's second app, Go Club, is blue.</p>
<h2>Guest links</h2>
<ul>
<li>Ranjith Ramanan on <a href="https://www.linkedin.com/in/ranjithalingal/">LinkedIn</a></li>
<li>Kamal Kumar on <a href="https://www.linkedin.com/in/lkamalkumar/">LinkedIn</a></li>
<li>Learn more about <a href="https://www.linkedin.com/company/box-box-club/?originalSubdomain=in">Box Box Club</a></li>
<li>Learn more about <a href="https://apps.apple.com/ph/app/go-club-steps-water/id6739782103">GO Club</a></li>
<li>Learn more about <a href="https://www.linkedin.com/company/arkade-club/?originalSubdomain=in">Arkade Club</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Apple’s new EU App Store terms: is adding web purchases worth it?]]></title>
      <link>https://www.revenuecat.com/blog/growth/apple-eu-app-store-fees</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/apple-eu-app-store-fees</guid>
      <pubDate>Wed, 07 Oct 2026 10:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Margarita Loktionova]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Should you add web purchases under Apple’s new EU terms? We look at how conversion losses change the fee savings, why scale matters, and what could make testing worthwhile beyond a lower commission.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/e48d17f08e01823f0bc7d0763cebc5112febfb1b-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Apple’s October 1 terms lower the standard EU commission and let you combine IAP with alternative payments for the first time. </p>
<p>But is that reason enough to send some users to the web?</p>
<p>If fees are your only reason, and you’re not among the highest-revenue apps, the answer is probably no. You still pay Apple, your payment provider charges on top, and even a modest drop in conversion can eat up the savings. Not to mention the extra work of handling refunds, disputes, and customers who don’t know how to cancel.</p>
<p>You’d also be committing to those payment options for 12 months, even if they turn out to cost you more than they save.</p>
<p>For larger apps with brand recognition and enough sales in the EU, lower fees might still save enough to cover the extra billing and support work, especially if in-app checkout converts better than sending customers to the web.</p>
<p>Anyhow. Let’s unpack what we already know.</p>
<h2>New commission rates, and IAP is no longer an either/or choice</h2>
<p>The new terms change both what Apple charges and how you can combine payment options, applying to sales on EU storefronts (wherever your company is based).</p>
<p>First, developers can now offer alternative checkout inside their app or link to web checkout without removing Apple IAP. The in-app option lets customers pay without switching to a browser, which <em>could </em>mean fewer lost purchases.</p>
<p>But if you offer it alongside IAP, Apple's option must appear at least as prominently, though nothing stops you from pricing the two differently.</p>
<p>Customers still have to choose how to pay, so you’ve removed one source of friction without necessarily removing the others.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b9b8e1ec710702564ab5697c4dbe4582e89aa5a5-1672x941.png" alt="EU App Store payment options"/></figure>
<p>With the standard EU IAP commission down from 30% to 26%, there’s also less to save by moving purchases to an alternative checkout.</p>
<p>Apple charges 20% for alternative payments inside the app, just six percentage points below IAP, or 15% for web link-out. That’s a very different deal from the US, where Apple currently takes no commission on linked <a href="https://www.revenuecat.com/blog/engineering/app-to-web-purchase-guidelines">web purchases</a>, although that remains subject to ongoing litigation.</p>
<p>Here’s what the full fee breakdown looks like for the EU:</p>
<table>
<thead><tr>
<th><p><strong>Payment option</strong></p></th>
<th><p><strong>Standard commission</strong></p></th>
<th><p><strong>Reduced commission</strong></p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Apple In-App Purchase</p></td>
<td><p>26%</p></td>
<td><p>15%</p></td>
</tr>
<tr>
<td><p>Alternative payment provider inside your app</p></td>
<td><p>20%</p></td>
<td><p>10%</p></td>
</tr>
<tr>
<td><p>Web checkout via link-out</p></td>
<td><p>15%</p></td>
<td><p>10%</p></td>
</tr>
<tr>
<td><p>Alternative marketplace or web distribution</p></td>
<td><p>5%</p></td>
<td><p>5%</p></td>
</tr>
</tbody>
</table>
<h2>What lower commissions really mean for your take-home revenue</h2>
<p>At standard EU rates, web link-outs offer the biggest potential fee savings. That gap narrows fast, though.</p>
<p>Your provider takes a share on top, and it adds up quicker than the headline processing rate: on a €9.99 subscription in the EU, Stripe charges around 1.5% + €0.25 to process, 0.7% for Billing, and 0.5% for Tax, so roughly 5% in total. A merchant of record adds about 3.5 points more. And the smaller your transactions, the worse that fixed €0.25 looks.</p>
<p>Suppose you're making <strong>€100,000 </strong>in EU sales, paying your provider<strong> 5%</strong>, and losing <strong>5% </strong>of purchases because customers have to go through a checkout they don't recognize.</p>
<p>Here’s what you’d be walking out with:</p>
<table>
<thead><tr>
<th><p><strong>Where the customer pays</strong></p></th>
<th><p><strong>Sales</strong></p></th>
<th><p><strong>Apple's cut</strong></p></th>
<th><p><strong>Provider fee</strong></p></th>
<th><p><strong>You keep</strong></p></th>
<th><p><strong>vs IAP</strong></p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Apple IAP (standard)</p></td>
<td><p>€100,000</p></td>
<td><p>26%</p></td>
<td><p>—</p></td>
<td><p>€74,000</p></td>
<td><p>—</p></td>
</tr>
<tr>
<td><p>Web link-out (standard)</p></td>
<td><p>€95,000</p></td>
<td><p>15%</p></td>
<td><p>5%</p></td>
<td><p>€76,000</p></td>
<td><p>+€2,000</p></td>
</tr>
<tr>
<td><p>In-app checkout (standard)</p></td>
<td><p>€95,000</p></td>
<td><p>20%</p></td>
<td><p>5%</p></td>
<td><p>€71,250</p></td>
<td><p>−€2,750</p></td>
</tr>
<tr>
<td><p>Apple IAP (reduced)</p></td>
<td><p>€100,000</p></td>
<td><p>15%</p></td>
<td><p>—</p></td>
<td><p>€85,000</p></td>
<td><p>—</p></td>
</tr>
<tr>
<td><p>Alternative checkout (reduced)</p></td>
<td><p>€95,000</p></td>
<td><p>10%</p></td>
<td><p>5%</p></td>
<td><p>€80,750</p></td>
<td><p>−€4,250</p></td>
</tr>
</tbody>
</table>
<p>In this scenario, IAP wins in three of the four comparisons. The only path that beats it is a web link-out at standard rates, and only by €2,000 on €100,000 in sales, which a slightly worse conversion rate would wipe out entirely.</p>
<p>Finally, you’d also need to cover maintenance and support. If you use RevenueCat Billing, payment retries, billing emails, a customer portal, and dashboard refunds are handled for you. However, refund requests, chargebacks, and support tickets still take your team’s time.</p>
<h2>The 12-month commitment is the biggest catch, but it’s not clear how far it goes</h2>
<p><a href="https://developer.apple.com/support/terms/apple-developer-program-license-agreement/#A14">Apple’s developer terms</a> say you elect which payment options you offer, and that election holds for twelve months across all EU storefronts:</p>
<p>“Each election you make applies across all the EU storefronts, and subsequent elections also remain in effect for twelve (12) months.”</p>
<p>So you can’t really plan on simply removing web checkout a month later if the numbers don’t look great. And you need to account for the billing, support, and reporting work even if the savings disappoint.</p>
<p>Rolling it out to a limited audience<em> looks </em>acceptable, as long as the experience stays consistent for the customers who do see it, but Apple’s wording leaves room for interpretation.</p>
<p>Apple’s published terms also don’t spell out how targeting or A/B tests fit into the commitment. This might leave room to test the waters with a smaller group through <a href="https://www.revenuecat.com/docs/tools/experiments-v1/configuring-experiments-v1">RevenueCat Experiments</a>, comparing revenue and LTV across IAP, in-app alternative checkout, and web link-out.</p>
<h2>So, should you add alternative checkout in the EU?</h2>
<p>At the end of the day, there’s no definite rule on whether web billing is something you should try. </p>
<p>On the one hand, it’s not as straightforward as “paying less and saving on <a href="https://www.revenuecat.com/blog/engineering/app-store-fees">App Store fees</a>,” because the reality is more nuanced. On the other hand, the actual impact on conversion, your existing resources, sales volume, and openness to experimentation can make this path more appealing for some developers.</p>
<p>While we’re running new experiments to estimate the impact on conversion, here are some directional tips on how to approach this based on where you are now.</p>
<h3>For smaller apps, it may not be worth the trouble</h3>
<p>Alternative checkout may not save enough to justify the effort, especially if you’re already paying Apple’s reduced 15% rate. The five-percentage-point difference gets smaller once you pay your provider, and lower conversion rates can wipe it out altogether.</p>
<p>Even if the calculation comes out somewhat positive, consider what that saving is worth against the time you’d spend maintaining another purchase flow. The 12-month lock-in also makes that decision more serious.</p>
<p>Where it might make sense is when the reason isn't revenue at all:</p>
<ul>
<li>A pricing model Apple doesn't support, like consumption-based or per-seat billing</li>
<li>A custom payment setup your use case specifically requires</li>
<li>Long-term benefits from managing the subscription lifecycle on web, like the pricing and promotion control we cover below</li>
</ul>
<p>If you have limited time and budget, testing pricing and packaging or improving engagement may do more for revenue at this stage. That doesn’t rule out web purchases, but it gives you something to compare it with before deciding where to put your effort.</p>
<h3>For larger apps, there may be more to gain</h3>
<p>Web monetization may be worth testing if you have substantial EU sales and the resources to support another billing flow. Especially now that it's no longer all or nothing: under the previous terms, selling outside IAP meant removing Apple's checkout from that storefront entirely. Now you can add web checkout alongside IAP and leave your existing flow running.</p>
<p>The maths also improves with scale, since setup costs don't grow proportionally with sales. And conversion is less of a gamble when customers already know your brand, which is part of why this works for the likes of Duolingo and Spotify. </p>
<p>You don't have to show web checkout to everyone either. Apple's rules govern what appears on each paywall, not who sees it, so you can start with your most loyal customers and keep IAP as the only option elsewhere.</p>
<p>Finally, the value of web monetization goes beyond fee savings if you've already built a growth engine that can use the extra levers:</p>
<ul>
<li><strong>Pricing and promotions</strong>: Web-specific prices, discounts, and win-back offers applied directly at checkout, without going through App Store Connect</li>
<li><strong>Checkout experience</strong>: Control over the purchase page and the payment methods you offer, with room to test both</li>
<li><strong>Lifecycle marketing:</strong> RevenueCat already sends billing events to your email and CRM tools on IAP. What alternative checkout adds is the ability to act on them — like a win-back email that lands on a checkout with the offer already applied, or a cancellation flow that counters with a different price and takes the payment there</li>
</ul>
<h2>Lower fees come with more reporting. RevenueCat handles that part.</h2>
<p>Say you've done the math and decided to enable web purchases in the EU. Under Apple's October 1 terms, your purchase flow has to pass App Review, and every transaction has to be reported back to Apple.</p>
<p>What’s more, that reporting doesn’t end with the first payment. Renewals, refunds, product changes, and cancellations all need to be accounted for. </p>
<p>Even if a customer opens checkout and leaves without buying, the token Apple issued still needs a report.</p>
<p>With RevenueCat, <a href="https://www.revenuecat.com/blog/company/apple-in-app-web-checkouts-eu">you don’t have</a> <a href="https://www.revenuecat.com/blog/company/apple-in-app-web-checkouts-eu">to build all of this yourself</a>:</p>
<ul>
<li><strong>Reporting</strong>: Purchases through the Stripe Billing, Paddle Billing, and RevenueCat Billing integrations are matched to Apple’s tokens and reported automatically, along with subsequent subscription activity and unused tokens.</li>
<li><strong>Purchase buttons</strong>: Apple-styled IAP buttons are built into Paywalls for when you offer IAP alongside alternative checkout.</li>
<li><strong>Targeting</strong>: You can show the alternative-payment paywall on EU storefronts and keep your existing IAP paywall elsewhere.</li>
<li><strong>Checkout</strong>: Your customers can follow a link to pay in their browser, or pay in a checkout sheet inside your app (currently in private beta).</li>
</ul>
<h2>The bottom line</h2>
<p>If you have the purchase volume and resources to experiment, web monetization is worth considering. Especially if you also want to test different offers, payment methods, or lifecycle campaigns.</p>
<p>Start with a clear hypothesis about what you expect to improve, then measure conversion, revenue after fees, renewals, and the support work that comes with it.</p>
<p>A lower commission isn’t a reason to rush on its own. In the EU, you still pay Apple and your provider, and you’re committing to your chosen payment options for 12 months. The experiment needs to show whether the savings and additional control justify that commitment for your app.</p>
<p>We’re running our own experiments to understand how the new EU options affect conversion and longer-term revenue. Stay tuned for updates.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Your app's growth has stalled: what now?]]></title>
      <link>https://www.revenuecat.com/blog/growth/app-growth-stalled</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/app-growth-stalled</guid>
      <pubDate>Tue, 06 Oct 2026 13:07:23 GMT</pubDate>
      <dc:creator><![CDATA[Daphne Tideman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[A four-step diagnostic to track the root cause and address it]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/56f27848b00b0a55514c1f15f7739d726dcbb000-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>We've all had it: that week when the numbers aren't quite what you hoped. Fewer new subscribers than the week before. Maybe churn creeps up. You tell yourself it's just a blip, that next week will be better.</p>
<p>Then one week becomes two, two becomes four, and as the graph keeps drifting down, a low-level panic starts to set in.</p>
<p>What’s happening? What are we doing wrong? Whose fault is it?</p>
<p>Every great growth story I've heard has a stall somewhere in it: a plateau, a setback, a period when nothing seemed to work. It's almost never the clean hockey-stick graph we love to post on LinkedIn. #GrowingInPublic — but not <em>that</em> publicly.</p>
<p>Growth stalls are normal, but finding the answers for what causes them is tough. I've experienced it both as Head of Growth at Heights and while advising clients, telling myself the same reassuring things I'm about to tell you. So I know how it feels, not just how to fix it.</p>
<h2>What <em>not </em>to do when your app’s growth stalls</h2>
<p>It's tempting to play the blame game. Meta is often the favorite culprit, or perhaps a teammate who isn't pulling their weight (or refilling the coffee). But when you dig into the data, most stalls are actually within your control. <a href="https://hbr.org/2008/03/when-growth-stalls">Research into why companies stop growing</a> found that <strong>only around 13% of growth stalls are driven by external factors</strong>. The rest come down to internal decisions — and most companies experience at least one major stall.</p>
<p>The worst response is to become a headless chicken: changing everything at once, chasing every new idea, and hoping something sticks.</p>
<p>Instead, I want to help you work through a growth stall methodically, from diagnosing what's actually causing it to getting growth moving again.</p>
<h2>The growth stall diagnostic</h2>
<p>Think of this as growth therapy. My goal is to reassure you, reduce the panic, and leave you with a clear plan rather than a long list of random experiments.</p>
<p>Over the years, I've developed a simple framework for working through these moments. It comes down to four questions:</p>
<ol>
<li>Is it a blip or a stall?</li>
<li>Is it within your control?</li>
<li>Where exactly is growth breaking down?</li>
<li>What should you do about it?</li>
</ol>
<p>Most people panic and jump straight to question four. But the real value lies in taking the time to answer the first three questions — that’s what tells you which action will actually work.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0de6c8624016e359150c000db2ed884e6172aa9a-1486x1490.png" alt=""/></figure>
<h2>Step 1: Is it a blip or a stall?</h2>
<p>If we chased every number that dipped for a week, we'd be very fit from all the running around — and completely exhausted.</p>
<p>There’s a lot of noise in growth, especially early on when numbers can swing wildly. So before you panic, <strong>define your thresholds</strong>: what counts as a real miss vs. normal variation?</p>
<p>If you typically grow 5–10% week-on-week and occasionally dip, is a 20% drop the point where you investigate? The answer will be different for every business, but having those guardrails is how you separate real signals from seasonality and one-off blips.</p>
<p>Seasonality is especially easy to miss. Once you have enough data, patterns start to emerge, and you can look back at a slowdown with more context. I have a client now in their eighth year who told me: “We’ve grown a bit this quarter — not loads — but these aren’t the months we usually grow in, so we’re not worried. We’re using the time to fix things and build our infrastructure.”</p>
<p>That perspective is much harder to have when you’re smaller, which is why you need to look for trends over several weeks, and resist reacting to a single bad one. <strong>Don’t obsess over daily swings; focus on week-on-week movement</strong>.</p>
<p>And when you catch yourself thinking, “This doesn’t look like a blip anymore”, treat it as a stall. Bring the team together and approach it like an experiment. Start with a <a href="https://www.revenuecat.com/blog/growth/subscription-app-pre-mortem">pre-mortem</a>: imagine it’s three months from now and growth is still flat. What went wrong?</p>
<h2>Step 2: Is the blocker within your control?</h2>
<p>Your circle of control is everything you can <em>actually</em> influence: the internal factors that shape your growth.</p>
<p>Things like:</p>
<ul>
<li>What you’re focusing on</li>
<li>What your processes look like</li>
<li>Which channels you’re investing in</li>
<li>How you’re positioning yourself</li>
</ul>
<p>There’s usually more within your control than you think. Then there’s everything outside it:</p>
<ul>
<li>Seasonality</li>
<li>New competitors</li>
<li>Policy shifts</li>
<li>Platform changes</li>
<li>Market saturation</li>
</ul>
<p>The first job is to figure out which bucket your stall belongs in.</p>
<p>Even when something external is clearly playing a role, the next question is: <strong>how do you bring it back into your circle of control?</strong> You can’t delete a new competitor, but you can decide how you respond, through your pricing, positioning, product, or messaging.</p>
<p>As the research suggests, external factors are rarely the whole story. Once you’ve ruled them in or out, focus your energy where you have the most leverage: the things you can actually change.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/15b47bccce04c1e30b3f473db369cc084f374359-1078x1102.png" alt=""/></figure>
<p>I spoke to a wellness app last year at <a href="http://appgrowthannual.com">App Growth Annual</a> that was facing this exact challenge. They told me AI had become an easy replacement with just a few smart prompts. And it wasn’t only a fear; their growth was slowing.</p>
<p>That can feel completely out of your control. But the more useful question is: what parts of the problem <em>are</em> within your control?</p>
<p>The advice I gave them was to go back to their core value proposition and ask:</p>
<ul>
<li>What value can we offer that AI cannot?</li>
<li>What other ways can we help users achieve their <a href="https://www.revenuecat.com/blog/growth/what-drives-users-to-pay-jobs-to-be-done">Job to Be Done</a>?</li>
<li>What parts of the experience become more valuable when technology gets better, rather than less?</li>
</ul>
<p>For example, yes, AI can generate a similar wellness plan. But it’s much harder for it to keep users accountable, automatically collect behavioral data and insights, and continuously adjust the plan based on what’s actually happening in someone's life.</p>
<p>Competing with AI would be like David vs. Goliath, but they <em>can</em> <a href="https://www.revenuecat.com/blog/growth/differentiator-against-ai">differentiate their app from AI</a> and double down on the parts of the experience that create deeper, longer-term value.</p>
<h2>Step 3: Where exactly is growth breaking down?</h2>
<p>This is the step people skip, and it’s the one that matters most. Instead of guessing at a cause, audit your growth the way an outside consultant would: look at every part of your funnel and (just as importantly) at how you’re working. It can feel like you’re slowing down when every instinct is telling you to move faster. I promise you, this is the thing that speeds you up.</p>
<p><strong>Start by getting clear on your </strong><strong><a href="https://www.revenuecat.com/blog/growth/north-star-metrics-subscription-growth/">North Star metric</a></strong>: the one metric that best defines success for your business. What is it, and what inputs actually drive it?</p>
<p>Then evaluate each area separately, as if you were looking at someone else’s company:</p>
<ul>
<li>Acquisition</li>
<li><a href="https://www.revenuecat.com/blog/growth/activation-metrics/">Activation and engagement</a></li>
<li>Retention and revenue</li>
<li>Monetization</li>
<li><a href="https://www.revenuecat.com/blog/growth/build-app-growth-team">Team structure</a> and approach</li>
</ul>
<p>For each area, ask:</p>
<ol>
<li>What’s working?</li>
<li>What isn’t?</li>
<li>How are these pieces influencing each other?</li>
</ol>
<aside class="tip"><strong>FYI</strong><p>I include team structure deliberately, because many stalls <em>aren’t</em> caused by a single broken metric. They come from how a team operates: where decisions are made, what gets prioritized, and where a founder is spending their attention.</p></aside>
<p>The trap here is auditing each area in isolation and declaring victory the moment one number improves. <a href="https://www.linkedin.com/in/ekaterina-shpadareva-gamsriegler/">Ekaterina Gamsriegler</a>, who led growth at coding app Mimo, has a great example of this: <a href="https://www.revenuecat.com/blog/growth/what-is-lifetime-value-ltv-apps">LTV</a> had plateaued while acquisition costs kept climbing, the classic growth-stage stall.</p>
<p>The solution came from understanding the funnel as a connected system. A <a href="https://www.revenuecat.com/blog/growth/paywalls-study-guide/">paywall</a> change shifted which plans users chose, which changed LTV, which then changed how much the company could afford to spend on acquisition. The result: <a href="https://www.revenuecat.com/blog/growth/optimize-funnel-metrics-mimo/">Mimo increased customer LTV by 65% and reduced paid CAC by 20%</a> in under a year.</p>
<p>Ekaterina’s rule applies here too: before any change, ask three questions:</p>
<ol>
<li>What is the first-order effect?</li>
<li>What are the downstream effects?</li>
<li>What could quietly get worse as this gets better?</li>
</ol>
<p>The third is about accepting tradeoffs. I've seen price increases give ARPU a big lift, while the number of new customers dropped. Is that a tradeoff you are willing to make?</p>
<h3>10 reasons for your growth stall</h3>
<p>As you go through each area, it helps to have a list of the usual suspects to compare against what you’re seeing. These aren’t every possible cause, and they’re not a diagnosis on their own, but they are the internal patterns I see behind stalls again and again. And luckily, nine of the 10 are firmly within your control.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/87477b1b0deee178ddd1b25163d8013708db0bcc-1592x944.png" alt=""/></figure>
<table>
<thead><tr>
<th><p>Reason for growth stall</p></th>
<th><p>Explanation</p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Lack of product-market fit</p></td>
<td><p>You <strong>never fully locked in product-market fit</strong> and have now reached a scale where you can’t paper over the cracks anymore. Or perhaps your fit has quietly shifted underneath you. You’ll often see this through declining retention, weaker referrals, and new customers becoming harder to convert.</p></td>
</tr>
<tr>
<td><p>You haven’t got messaging right</p></td>
<td><p>Your messaging is too broad and doesn’t speak directly to a <a href="https://www.revenuecat.com/blog/growth/solve-app-problems-emotionally/">specific customer need</a>. As a result, channels become harder to scale, your website converts less effectively, and your cost of acquisition starts creeping up.</p></td>
</tr>
<tr>
<td><p>ver-reliance on one customer source</p></td>
<td><p>More than half your customers come from a single channel or one piece of social proof. Then the algorithm changes or that proof disappears, and everything drops at once, because Meta giveth, and Meta taketh away.</p></td>
</tr>
<tr>
<td><p>Focusing on the ‘wrong’ channels</p></td>
<td><p>You’re investing in the channels everyone else is using rather than the ones that actually fit your business. You might still acquire customers, but at a high cost, with lots of effort and diminishing returns.</p></td>
</tr>
<tr>
<td><p>Getting caught up in optimizing</p></td>
<td><p>You’ve already captured the obvious quick wins, and your experiments have become increasingly low impact. You’re polishing the edges instead of taking the bigger bets that could genuinely change the trajectory.</p></td>
</tr>
<tr>
<td><p>Equating marketing with growth</p></td>
<td><p>You’re treating growth and marketing as the same thing, which can trap your marketing team into chasing short-term conversions instead of building the brand, awareness, and demand that create long-term growth.</p></td>
</tr>
<tr>
<td><p>Lack of recent customer research</p></td>
<td><p>It’s been too long since you <a href="https://www.revenuecat.com/blog/growth/review-mining-for-subscription-apps/">spoke to customers</a>, so you’ve lost touch with what they actually need. The tell is that you keep running experiments, but none of them translate into meaningful growth.</p></td>
</tr>
<tr>
<td><p>Team silos</p></td>
<td><p>Your teams aren’t operating as one growth team. Each function is chasing its own goals and projects rather than aligning around what actually moves your North Star metric. Don’t race through this as a checklist. Use each one as a prompt: is this showing up in acquisition, retention, monetization, or how the team operates? Most stalls I see are a combination of two or three of them, not a single culprit.</p></td>
</tr>
<tr>
<td><p>Team silos</p></td>
<td><p>Sometimes the cause genuinely is outside your control: the economy, seasonality, a market shift, a worldwide pandemic, etc. But even then, the question becomes: how do you make the business more resilient? Can you reduce price sensitivity? Create alternative revenue streams? Adjust your positioning? External factors can explain a stall, but they don’t have to define it.</p></td>
</tr>
</tbody>
</table>
<h3>Running a growth audit: early-stage startups</h3>
<p>If you’re early, keep it simple and go straight to the foundation:</p>
<ul>
<li>Do you have <a href="https://www.revenuecat.com/blog/growth/product-market-fit-subscription-apps/">product-market fit?</a></li>
<li>Do you have an app that <a href="https://www.koji.so/docs/sean-ellis-test-product-market-fit">at least 40% of users</a> would be very disappointed to lose?</li>
<li>Are they willing to pay for it?</li>
</ul>
<p>If that foundation isn’t there, more channels won’t fix your growth. They might create some initial buzz, but they won’t solve the underlying problem.</p>
<p>One place you’ll often see this is in early drop-off. Some churn in the first few days is normal; in State of Subscription Apps 2026, <strong><a href="https://www.revenuecat.com/blog/growth/first-renewal-churn">55% of three-day trial cancellations happen on day zero</a></strong>. But if your drop-off is significantly worse than benchmarks, or stays high even after improving onboarding and communicating your value more clearly, the issue is probably deeper. It’s likely a mismatch between what you’re offering and what people actually need.</p>
<p><a href="https://www.mob.co.uk/">Mob</a>, the meal-planning app, is a great example of what fixing that foundation actually looks like. Founder Ben Lebus has spoken openly about how they went from £1.2 million in debt to becoming the number one app in the App Store (<a href="https://growthwaves.substack.com/p/mob-from-12m-in-debt-to-number-1">I shared their story here</a>). It started with focus: they cut the food truck, the spice mixes, the side apps, everything that wasn’t working, so they could go all in on one thing. Then they spoke to users every week and <a href="https://www.revenuecat.com/blog/growth/how-subscription-apps-can-become-painkillers/">realized they were a “vitamin”</a> — something nice to have, rather than something people truly needed.</p>
<p>They kept narrowing until they found a sharper Job to Be Done: “When weekday meals keep coming, I want a reliable, low-effort way to choose what to cook, so I can eat well without stress.” From there, they built around that need, and growth followed. Two of the 10 reasons were at play: a lack of focus and overly-broad messaging. Speaking to users is what helped them uncover both.</p>
<h3>Running a growth audit: scaling and mature</h3>
<p>If you’re further along, you’ve probably got some level of product-market fit, so that’s less likely to be the core issue. The problem is more likely elsewhere: you haven’t found the right channels to scale, your monetization model has room to improve, or early <a href="https://www.revenuecat.com/blog/growth/subscription-app-churn-reasons-how-to-fix/">retention starts declining</a> as you expand to a broader audience and need to create more ongoing value.</p>
<p>It can also be that your pricing and packaging were never properly pressure-tested. Reading.com, the kids' learn-to-read app, is a good example. As <a href="https://subclub.com/episode/what-readingcom-learned-testing-prices-and-funnels-tim-dikun-teachingcom">Tim Dikun, COO of Teaching.com, shared on Sub Club</a>, they tested prices from $4.99 all the way up to $19.99 before landing on $12.49. They also replaced the free trial on their <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels/">web funnel</a> with a <a href="https://www.revenuecat.com/blog/growth/money-back-guarantee?utm_campaign=Sub+Club+newsletter+2026+%28Edition+%2394%29&amp;utm_content=Sub+Club+newsletter+2026+%28Edition+%2394%29&amp;utm_medium=email_action&amp;utm_source=customer.io">30-day money-back guarantee</a>. It converted fewer people, but those users sent a stronger, higher-intent signal to the ad networks. They found the answer through testing, not guessing. These are common causes — not the only ones — which is exactly why you audit rather than assume.</p>
<p>And when you look at the numbers, don’t just focus on what happened last week. Zoom out and<strong> look for trends over time</strong>. Find the moments where something suddenly dropped or jumped, then ask what changed. Compare against benchmarks where you can, but always account for what’s genuinely different about your app, audience, and business model. For quick checks, I like using the <a href="https://www.revenuecat.com/healthscore/">RevenueCat App Health Calculator</a>. When I want to go deeper, I use the <a href="https://www.revenuecat.com/state-of-subscription-apps/">State of Subscription Apps report</a> for broader subscription benchmarks and trends.</p>
<h2>Step 4: Scale, fix, or kill</h2>
<p>By now, you should have a shortlist of hypotheses rather than one panicked guess. Step four is working through them, and being honest about what each one needs: scale it, fix it, or kill it. The only way forward is to <strong>test them one at a time</strong>: does this actually get us back on track or not? Sometimes that takes a few weeks; sometimes it takes months.</p>
<p>Be careful about fixing the wrong thing though — at Mimo, the team initially focused on reducing churn and improving retention before realizing most of their revenue came from new subscribers, and the churning cohort was too small to materially move the needle. They were solving a problem they didn’t actually have yet. The audit is what helps you avoid that trap.</p>
<p>It’s also worth turning the audit on yourself. One of my favorite parts of the Mob story (<a href="https://growthwaves.substack.com/p/part-2-of-mob-from-12m-in-debt-to">part two here</a>) is their fourth lesson: at an offsite, the team told Ben — a founder who had poured everything into the business — “let me do my job.” He stepped back, even leaving Slack channels where he wasn’t needed, and the team moved faster. I promise he took it well!</p>
<p>Part of diagnosing a stall is asking honestly whether your own involvement is helping or quietly getting in the way.</p>
<h3>When to kill a channel</h3>
<p>One rule I give clients for the kill decision: if a channel is sitting at four to five times your target cost of acquisition, and it’s still there after you’ve tried genuinely different approaches – not just new ad copy, but new formats and angles – it’s probably time to make a bigger change or kill the channel. <strong>Optimizing the edges of something fundamentally broken just burns time you don’t have</strong>.</p>
<p>I’ve seen this happen with Meta Ads all too often. In some cases a major shift broke them out of the endless high CACs: invested in brand awareness, really narrowed down and defined their positioning, or completely revamped their creative or landing page. In others, after trying major shifts they turned to other channels, or focused instead on <a href="https://www.revenuecat.com/blog/growth/subscription-app-expand-value/">improving their ARPPU</a> to break out of their growth stall.</p>
<p>A related exercise I love is re-costing your struggling channels to include the team hours they consume. Founders often keep a channel because the media spend looks acceptable, then realize that once they account for everyone’s time, the true cost of acquisition is much higher. Meanwhile, the “boring” channel quietly working in the background was the better investment all along.</p>
<p>And occasionally the honest answer is bigger than a fix. Slack began life inside a games company whose game didn’t work out; the internal chat tool the team had built for themselves turned out to be the real business. Most stalls don’t require a pivot that dramatic, but it’s worth remembering: <strong>sometimes the thing you’re trying to save isn’t the thing worth saving.</strong></p>
<h2>A growth stall is a gift</h2>
<p>They say the hardest moments in a relationship are often the ones that bring you closest — the same is true for growth, and for teams.</p>
<p>A stall is the ultimate test.</p>
<ul>
<li>It tests founders: how they react, whether they stay calm or start pointing fingers, whether they create clarity or chaos.</li>
<li>And it tests the team: can they come together, focus on the few things that matter, and work towards the changes that will actually move the needle?</li>
</ul>
<p>I recently learned that the best Formula One drivers are said to respond the same way whether they win or lose. They don’t get swept away by the emotion of the result. They step back, review the data, and work out what’s working and what isn’t.</p>
<p>We should approach growth stalls the same way. Don’t panic. Don’t spiral. Look at the data with the same curiosity you would bring when things are going well. You might not find the answer immediately, but finger-pointing, stressing, and trying to fix everything at once won’t get you there. I can promise you that.</p>
<p>Mob is proof of what can be on the other side. The same company that was £1.2 million in debt now has 280,000 premium subscribers and $12.5 million in ARR, not because of one clever hack, but because they worked through the fundamentals: cutting distractions, finding their real Job to Be Done, testing their way to a scalable acquisition channel, and learning when a founder needs to step back.</p>
<p>You come out of a stall with a stronger organization. Your processes improve. Your understanding and control of growth improves. And you usually come away with a few hard lessons too, whether that’s letting one channel dictate your growth, getting so focused on acquiring new users that you neglected retention, or being slow to recognize how much AI has changed your landscape.</p>
<p>It might not feel like it in the moment, but a stall can be a gift. It forces you to understand your business more deeply, build more resilience, and ultimately grow faster and more sustainably.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Early Apple Retention Messaging benchmarks from 22M messages]]></title>
      <link>https://www.revenuecat.com/blog/growth/apple-retention-messaging-benchmarks</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/apple-retention-messaging-benchmarks</guid>
      <pubDate>Tue, 06 Oct 2026 12:19:00 GMT</pubDate>
      <dc:creator><![CDATA[Margarita Loktionova]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[How 309 apps with early access use Apple's Retention Messaging, what it does to save rates and cancellations, and what to test first.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/9b0b0bf8bed770f743cad055f0c81f8cb1a04dfb-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Apple's Retention Messaging lets apps show a message or offer when subscribers go to cancel through App Store settings. Apps can use it to offer a discount, suggest a different plan, or remind subscribers what their subscription includes.</p>
<p>The biggest apps are moving first. Nearly half of apps tracking $1 million or more in monthly App Store revenue through RevenueCat have tried it, even ahead of the wider rollout.</p>
<p>We analyzed 22 million recorded messages across 309 apps to see how they set up their messages, how often subscribers leave without canceling, and how much those results vary.</p>
<aside class="tip"><strong>Tip</strong><p>The apps in our study have early access to the real-time <a href="https://www.revenuecat.com/blog/engineering/apple-retention-messaging-api">Retention Messaging API</a> and use it through RevenueCat. Developers can currently <a href="https://developer.apple.com/contact/request/retention-messaging-api/">request access from Apple</a>, with a simpler App Store Connect version coming this fall.</p></aside>
<h2>Larger apps are getting an early start with Retention Messaging</h2>
<p>Among App Store apps using RevenueCat, 44% of those making $1 million or more a month have tried Retention Messaging through RevenueCat. That falls to 17% for apps making $100,000–$1 million a month, and below 5% for smaller apps.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/d5cd1d803d7411d0e144e1b00e73358ac84a3bcf-1920x1080.png" alt="Bar chart showing Apple Retention Messaging adoption by monthly App Store revenue: 44.4% of apps earning $1 million or more, 16.9% earning $100,000–under $1 million, 4.7% earning $10,000–under $100,000, 1.3% earning $1,000–under $10,000, and 0.06% earning under $1,000. Adoption means recording at least one retention message"/></figure>
<p>For a large subscription business, even a small improvement at cancellation could affect thousands of subscribers. But these figures also reflect an early rollout: apps currently need Apple's approval to use the real-time Retention Messaging API.</p>
<p>Smaller apps can <a href="https://developer.apple.com/contact/request/retention-messaging-api/">request access</a>, too. Once Apple approves the request, RevenueCat handles the server side. You'll still need to pass Apple's performance test and get each message approved before it goes live, so build that into your timeline.</p>
<h2>What a typical save rate looks like</h2>
<p>The save rate shows how many subscribers who see the retention message don’t cancel within 24 hours, or take the offer instead. If you use RevenueCat, this is the same metric as the App Store Save Rate in Charts. </p>
<p>At the median app in our analysis, it was 17%, or 17 in 100 subscribers. That's based on 309 apps that showed at least 100 messages between March and September 2026.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3a0c34755cb5c9974857f5e10b477ffb4ec33a12-1920x1080.png" alt="Bar chart of Apple Retention Messaging save rates across 309 apps. The median is 16.7%, with the largest group of apps recording save rates of 15–20%."/></figure>
<p>The range between apps is wide. At a quarter of apps, fewer than 10 in 100 subscribers didn't cancel. At another quarter, more than 27 in 100 didn't. And at the top 10% of apps, it was 42 or more.</p>
<p>The save rate is a useful benchmark, but a limited one. It doesn’t show how many subscribers stayed because of the message, since some would have stayed anyway. Someone who cancels a few days later still counts as saved, and the metric doesn’t track whether they renew. (The cancellation data below points the same way.)</p>
<h2>Most apps offer a discount or plan switch, but few subscribers accept</h2>
<p>Almost nine in 10 apps (87%) in our analysis regularly show subscribers a discount or plan-switch offer when they try to cancel. Yet subscribers accept only about one in 100 of those offers.</p>
<p>Apps combine offers with text messages in several ways. The most common setup pairs text messages with promotional offers (27% of all 309 apps), followed by primarily promotional offers (22%). Another 38% use setups that include plan switches.</p>
<p>Together, these groups account for 87% of apps; the remaining 13% primarily use text.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/403b8b7aba792b0e9bae07c5c7ef4e0d62b3c290-1920x1080.png" alt="Bar chart of Apple Retention Messaging setups across 309 apps. Text paired with promotional offers is most common at 27%, followed by primarily promotional offers at 22%. Only 13% primarily use text."/></figure>
<p><em>We count a message type for an app only if it makes up at least 15% of the messages the app sends. So an app labeled 'promotional offer only' may still send a few other types. Text messages may include images. Percentages are rounded.</em></p>
<p>Between March and September, subscribers saw about 4.3 million promotional offers and 1.3 million plan-switch offers. Based on June–September data, when offer tracking was complete, they accepted only about 1.1% of promotional offers and 0.6% of plan-switch offers.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ea2b75afb2b8ffd67e7786ee83f447688f4cf1af-3296x1736.png" alt="Bar chart showing Apple Retention Messaging offer acceptance rates"/></figure>
<p>In other words, subscribers rarely take the discount or switch plans, even though most apps offer those options. Most recorded saves happen without either: the subscriber doesn’t accept an offer, but also doesn’t cancel within the next 24 hours.</p>
<p>One possible explanation is that subscribers are still deciding when they reach this screen. Some may reconsider or postpone canceling without taking an offer. For developers, it’s also worth testing messages that explain which features or benefits subscribers will lose if they cancel, alongside messages offering a discount.</p>
<h2>Early data shows no clear effect on cancellation rates</h2>
<p>In our analysis, cancellation rates fell at many apps after they introduced Retention Messaging. But comparable apps not using the feature saw even larger declines.</p>
<p>We looked at 64 apps with at least 500 paying customers and compared paid cancellations (excluding trials) in the three months before launch with the launch month and the two months after.</p>
<p>Of these apps, 34% saw their cancellation rate fall by at least 10%, compared with 46% of comparable apps without Retention Messaging.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3a2db223966db55ffd6034562d569d33806f44dc-1920x1080.png" alt="Bar chart comparing apps with cancellation-rate declines of at least 10%: 34.4% of apps using Apple Retention Messaging versus 46% without it, with results broken down by monthly App Store revenue."/></figure>
<p>Besides, both groups were <em>already</em> on a similar downward trend before launch.</p>
<p>Afterward, cancellation rates at apps using the feature fell less than at comparable apps without it: 3.6% versus 7.3%. With 64 apps, a gap this size could easily be chance, so the data doesn’t show a measurable effect either way.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/aaedf8c7451acde9a01432df0254a67a0b59aad4-1920x1080.png" alt=" Bar chart showing median cancellation-rate declines before and around Apple Retention Messaging’s introduction: 10.1% and 3.6% for adopting apps, versus 8.9% and 7.3% for matched apps without it."/></figure>
<h2>The next step is testing Apple Retention Messaging in practice</h2>
<p>Our early data doesn't show a clear, immediate reduction in cancellations from Retention Messaging. We're already working on experiments to understand its impact more directly, and we'll share those findings as they come in.</p>
<p>For developers preparing to try it, the best strategy is to compare different approaches before making discounts the default. Low offer acceptance doesn't necessarily mean the discount needs to be bigger. Price may not even be the issue.</p>
<p>Start with what you know about why subscribers cancel, drawing on cancellation surveys, support conversations, and product usage. Then test messages that address those reasons: a discount for affordability concerns, a different plan for subscribers whose needs have changed, or a reminder of useful features they may have overlooked.</p>
<p>If you do offer discounts, be selective about who sees them. When every subscriber who opens the cancel screen gets one, some may learn that starting to cancel is an easy way to get a lower price. With RevenueCat, you can <a href="https://www.revenuecat.com/docs/platform-resources/apple-platform-resources/apple-retention-messaging-api">control who sees an offer</a>, for example by excluding subscribers still in a free trial or those who've already redeemed an offer. Setting up Retention Messaging directly in App Store Connect doesn't offer this kind of targeting.</p>
<aside class="tip"><strong>RevenueCat’s Retention Messaging Experiments, now available in the RevenueCat dashboard, lets you test up to four messages against each other to see which works best for your app.</strong><p>Track renewals and revenue alongside the save rate to see which message keeps subscribers through their next payment. If you're still waiting on Apple's approval, you can prepare your messages in the sandbox environment. <a href="https://www.revenuecat.com/docs/tools/experiments-v1/experimenting-with-retention-messaging">Learn more here</a>.</p></aside>
<h2>About the data</h2>
<p>This analysis covers Apple Retention Messaging activity recorded through RevenueCat from March 22 to September 20, 2026, with data compiled on September 24.</p>
<p>We identified 406 apps with at least one message. Save-rate and message-setup analyses include the 309 apps with at least 100 messages, totaling 21,960,020 recorded messages. Messages are counted once per subscription per day, so the total does not represent unique subscribers.</p>
<p>The median and percentile benchmarks give each app equal weight. Message-type setup groups use a 15% threshold. Adoption shares compare apps that tried the feature with App Store apps tracking revenue through RevenueCat in the last 90 days.</p>
<p>These are early participants in Apple’s access program, not a representative sample of all subscription apps. The analysis describes recorded outcomes; it does not establish that messages caused subscribers to stay or renew.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[RevenueCat Charts v3: fresher data, better decisions]]></title>
      <link>https://www.revenuecat.com/blog/engineering/revenuecat-charts-v3-fresher-data-better-decisions</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/revenuecat-charts-v3-fresher-data-better-decisions</guid>
      <pubDate>Tue, 06 Oct 2026 10:16:22 GMT</pubDate>
      <dc:creator><![CDATA[Charlie Chapman, Rhys Kentish]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[We’ve rebuilt RevenueCat’s data infrastructure from the ground up.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/92c9d4e26d6bc7b057712c46f4f18b5639162ac3-2048x1024.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>We’ve rebuilt RevenueCat’s data infrastructure from the ground up.</p>
<p>That rewrite powers a big upgrade we’re excited to share today: <strong>Charts v3</strong> — a new Charts experience that gives you fresh data in seconds, plus a bunch of new ways to slice, segment, and understand your subscription business.</p>
<p>At a high level, this new infrastructure unlocks two big things:</p>
<ul>
<li><strong>Real-time updates across almost every chart:</strong> instead of waiting hours or days for metrics from App Store Connect or Google Play Console, our new real-time events now flow into your charts as soon as they happen, so you can actually watch launches, experiments, and promos play out in real time.</li>
<li><strong>A much faster path to new charts:</strong> because every chart now runs on the same rebuilt data model, it’s now <em>way</em> easier for us to ship new charts and dimensions — which is why, just in the last year of testing and refining this system, we’ve already added a ton of new charts and charting features.</li>
</ul>
<p>Charts v3 is now live in the RevenueCat dashboard.</p>
<p><a href="https://www.youtube.com/watch?v=hWE5hM7-UZ8">Watch on YouTube</a></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/fb9fb24773f84b3639ed68110ccb1ddd4b7d0aef-900x514.png" alt=""/></figure>
<h2>What ‘real-time’ actually means</h2>
<p>Previously, charts updated in batches every 2–12 hours depending on the dataset. Now, almost all charts refresh in <strong>real time</strong> as events come in, giving you:</p>
<ul>
<li>Immediate feedback on launches, paywall tests, and promos</li>
<li>Better intra-day monitoring for spikes, dips, or anomalies</li>
<li>More consistent behavior across charts, since they’re all driven by the same live pipeline</li>
</ul>
<p>Charts v3 currently supports <strong>App Store, Play Store, Stripe, and RevenueCat Web Billing</strong> apps, with support for our other supported stores coming soon.</p>
<h2>Why some numbers look different</h2>
<p>We’ve also made some improvements to our data definitions, so some numbers in Charts v3 can differ from those in legacy Charts. Here are the three biggest changes:</p>
<h3>Subscriptions from every store, treated consistently</h3>
<p>We’ve used this opportunity to treat subscriptions from different stores more consistently. Instead of handling each store’s behavior slightly differently, Charts v3 applies the same rules everywhere.</p>
<p>This new model lets us:</p>
<ul>
<li>Distinguish product changes (e.g. switching from plan A to B) from simple renewals</li>
<li>Treat resubscriptions (coming back after a lapse) differently from continuous renewals</li>
<li>In our <strong>Active Subscriptions Movement</strong> chart, resubscriptions after a lapsed subscription are now called out as a separate positive line item. In the past, resubscriptions on the Apple App Store were instead shown as negative churn.</li>
</ul>
<p>Other charts, like <strong>Subscription Retention</strong> and <strong>Trial Conversion</strong>, now align more closely with how you actually think about subscriber behavior (e.g. “this user changed products” vs. “this user renewed as normal”).</p>
<p>The change you’re most likely to notice is in <strong>Trial Conversion Rate</strong>, which will probably go down slightly. It now only counts direct trial-to-paid conversions. Previously, someone who resubscribed after completing a trial would also have counted as a trial conversion.</p>
<h3>Refunds no longer rewrite history</h3>
<p>Previously, when a payment was refunded, it could change metrics in already-completed periods. That made it harder to trust historic reports, and could cause numbers to ‘wiggle’ days or weeks after the fact.</p>
<p>With Charts v3, historical data stability is crucial:</p>
<ul>
<li>Revenue is added on the day the purchase happens</li>
<li>If that payment is later refunded, the revenue is subtracted on the day of the refund, not retroactively</li>
</ul>
<p>This means historical revenue for completed periods stays stable, and you’ll see negative revenue entries on refund dates.</p>
<p>The same behavior applies to conversions and LTV. The original purchase contributes to all relevant timeframes, and if it’s refunded, those contributions are removed only in the periods where the refund falls. Completed periods don’t keep shifting under your feet.</p>
<h3>Changes to cohorting</h3>
<p>We’ve also updated the cohorting methodology for <strong>Cohort Explorer</strong> and <strong>Prediction Explorer</strong>. Instead of defining big cohorts (like a whole month) using just the first and last calendar days, we now calculate each customer’s lifecycle relative to when they actually started, then aggregate the data.</p>
<p>In practice, this means:</p>
<ul>
<li>Late-joining customers no longer have their early revenue ‘pushed’ into the next period</li>
<li>Cohort-based metrics like 0–30 day LTV are more consistent and easier to compare across time</li>
</ul>
<h3>Quick Reference: expected metric changes</h3>
<table>
<thead><tr>
<th><p><strong>Metric</strong></p></th>
<th><p><strong>Expected change</strong></p></th>
<th><p><strong>Reason for change</strong></p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Trial Conversion Rate</p></td>
<td><p>Likely decrease</p></td>
<td><p>Only direct trial-to-paid conversions counted</p></td>
</tr>
<tr>
<td><p>Historical Revenue</p></td>
<td><p>May shift between periods</p></td>
<td><p>Refunds impact the refund date, not purchase date</p></td>
</tr>
<tr>
<td><p>Active Subscriptions</p></td>
<td><p>Minimal change</p></td>
<td><p>Counting method changed, not actual subscribers</p></td>
</tr>
<tr>
<td><p>MRR/ARR</p></td>
<td><p>Minimal change</p></td>
<td><p>Based on same active subscription count</p></td>
</tr>
</tbody>
</table>
<h2>New charts and capabilities unlocked</h2>
<p>Because the data model is now more flexible, we’ve been able to ship a bunch of new charts and features on top of Charts v3 already — with more on the way.</p>
<h3>Period-over-period comparisons</h3>
<p>A big upgrade built on Charts v3 is <strong>period-over-period comparisons</strong>. You can now compare your selected date range against the previous period with a single toggle in the <strong>Compare</strong> dropdown.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ae6304cae2435782993a33082ab5b64bcda7b682-2048x1525.png" alt=""/></figure>
<p><em>Churn rate over the last 12 months compared to the previous 12 months.</em></p>
<p>When enabled, charts will:</p>
<ul>
<li>Plot both the <strong>current period</strong> and <strong>comparison period</strong> as separate lines</li>
<li>Show total values and percentage change in the summary</li>
<li>Let you hover over any point to see the percentage change vs. the previous period at that moment in time</li>
</ul>
<p>Period-over-period is available on key charts like Active Subscriptions, MRR, ARR, Churn, New Customers, New Trials, Conversion to Paying, Refund Rate, and more.</p>
<h3>New and improved dimensions</h3>
<p>Charts v3 introduces new dimensions and tightens some existing ones, so segmentation matches how you actually reason about your business.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b567e57f5ae95ceddd0575797beba3b2eb8bbbaf-2048x1179.png" alt=""/></figure>
<p><em>The segment picker has new grouping and now includes search to make it easier to find the right dimension.</em></p>
<p>New dimensions include:</p>
<ul>
<li><strong>Custom attributes</strong>: segment by the custom attributes you set on customers</li>
<li><strong>App version</strong>: analyze metrics by the first app version a customer used</li>
<li><strong>Attribution</strong>: filter/segment using Source, Campaign, Keyword, etc.</li>
</ul>
<p>We’ve also improved existing dimensions:</p>
<ul>
<li><strong>Platform</strong> now refers to the customer’s <strong>first seen</strong> platform (where they originated), not the last platform they touched — so segmenting by platform is more stable over time</li>
<li><strong>Country</strong> now prioritizes the app store storefront over IP-based location, aligning charts more closely with how revenue and subscribers are actually distributed across markets</li>
</ul>
<p>One thing to note: customers you create through the RevenueCat API don’t have a platform, country, or SDK version attached, so they can’t be segmented or filtered by those dimensions.</p>
<p>For full definitions and edge cases, see the <a href="https://www.revenuecat.com/docs/dashboard-and-metrics/charts/real-time-charts">Charts v3 docs</a>.</p>
<h3>New Active Customers Chart</h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/1516da33d56c3d0a1168568f0542b738aafe7aed-2048x1168.png" alt=""/></figure>
<p>The new <strong>Active Customers</strong> chart plots all users that were seen in the period being measured, regardless of whether they’ve made a purchase. This works well to plot out your daily active users (DAU). An important note is that this chart does not yet correct for aliased customers, so in cases where the same customer appears under multiple linked app user IDs, active customer counts may be overstated.</p>
<h3>Trial Conversion Rate</h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/7fedc5f43700d2465839fbf2d37ecb47ccfcd641-2048x1182.png" alt=""/></figure>
<p>The new <strong>Trial Conversion Rate</strong> chart is cohorted by trial start date and focuses purely on the trial-to-paid step conversion, making it easier to understand how changes to acquisition, pricing, or paywalls affect payment behavior over time.</p>
<h3>Updated Active Subscriptions Movement (state bar chart)</h3>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/fa921be4c1b7e8c773928423d7df7188e7de8222-2048x1318.png" alt=""/></figure>
<p>We’ve refreshed the <strong>Active Subscriptions Movement</strong> chart to better visualize how subscribers move between states (new, renewed, churned, product changes, resubscriptions, etc.) using a more intuitive state bar format.</p>
<h3>Paywall charts</h3>
<p>New <strong>Paywall Charts</strong> give you a dedicated place to understand paywall performance: conversion, drop-off, and downstream value, all segmented by the same dimensions you already use across RevenueCat.</p>
<p>You can also now filter and segment other charts by <strong>RevenueCat Paywalls</strong>, making it much easier to tie conversion and revenue outcomes back to specific paywall experiences and experiments.</p>
<h2>Try Charts v3</h2>
<p>Charts v3 is available in your dashboard today for apps using the supported stores.</p>
<p>If you notice differences in your metrics after switching, refer to the <a href="https://www.revenuecat.com/docs/dashboard-and-metrics/charts/real-time-charts">Charts v3 docs</a> for a chart-by-chart breakdown of what’s changed and why — and, as always, <a href="https://form.typeform.com/to/iuAUBGNC">we’d love your feedback</a> as you start using it in your day-to-day decision making.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[New York App Week 2026: full schedule + how to join]]></title>
      <link>https://www.revenuecat.com/blog/company/new-york-app-week-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/new-york-app-week-2026</guid>
      <pubDate>Thu, 01 Oct 2026 10:56:46 GMT</pubDate>
      <dc:creator><![CDATA[Lorelei Whitman]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Big Apple? Bigger App Week]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/20c7ca9b32abf006afb68edee42c22a5f6958741-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Last year's App Week saw a mix of workshops, meetups, co-working, dinners, and run clubs pop up across the city, and 2026 is shaping up to be even bigger. Each event sets its own RSVP rules, so you don't need a conference badge to join in.</p>
<p>Read on for the full rundown of everything on offer.</p>
<h2><strong>Sunday, October 18</strong></h2>
<h3><strong>Welcome to NYC!</strong></h3>
<p>📅 4:30–5:30 PM
📍 Details shared after approval</p>
<p>Landed early? Meet local and visiting app builders before the week kicks off, with drinks on the house. Hosted by EHVM, Tangent, and RevenueCat, join for a chilled start to a busy week — sightseeing included (but weather-dependent!). <a href="https://luma.com/jqy01bo1">Request to join →</a></p>
<h2><strong>Monday, October 19</strong></h2>
<h3><strong>Warm Start: An Evening at Othership</strong></h3>
<p>📅 7:00–8:15 PM
📍 Othership Flatiron, 23 W 20th Street</p>
<p>A wellness evening for mobile founders and product leaders, pairing sauna and cold plunge sessions with a candid conversation about what it takes to build great mobile products. Come to relax those tense muscles after a busy day, stay for the new connections. Hosted by STRV and RevenueCat. <a href="https://luma.com/strv-f4ju">Request to join →</a></p>
<h3><strong>Sail &amp; Scale: The RAGA Launch Cruise by Paddle</strong></h3>
<p>📅 5:00–7:00 PM
📍 Details shared after approval</p>
<p>A relaxed evening cruise to kick off New York App Week, with skyline views, drinks, bites, music, and casual app growth conversation. Hosted by Paddle. <a href="https://luma.com/paddle-ss1z">Request to join →</a></p>
<h3><strong>iOSoho IndieDev Talks</strong></h3>
<p>📅 6:00–9:00 PM
📍 307 West 38th Street, Studio 1701 (in person and online)</p>
<p>Practical app growth talks for indie iOS developers, plus community updates, pizza, drinks, and networking. Can't make it in person? Tune in online. Hosted by iOSoho and RevenueCat. <a href="https://www.meetup.com/iosoho/events/316478632/">Request to join →</a></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/566086ee780f0c0f836c1559b2ab5f7a4b4f082b-4032x3024.jpg" alt=""/><figcaption>2025's iOSoho NYAW meetup</figcaption></figure>
<h3><strong>Growth Gems &amp; Screw-Ups</strong></h3>
<p>📅 6:30–9:30 PM
📍 TALEA Beer, Penn District</p>
<p>An opening-night meetup where consumer app founders and growth practitioners trade their best subscription growth insights (and their most expensive mistakes) over delicious tacos. Hosted by Growth Gems and RevenueCat. <a href="https://luma.com/gzu9pr1d">Request to join →</a></p>
<h3><strong>AI App Founders Dinner</strong></h3>
<p>📅 6:30–9:30 PM
📍 Details shared after approval</p>
<p>No speakers, no agenda. Just good food, good wine, and candid conversation for founders building consumer AI apps. Hosted by Koah and RevenueCat. <a href="https://luma.com/135vltww">Request to join →</a></p>
<h3><strong>AI For NYC Hackathon</strong></h3>
<p>📅 6:30–10:00 PM
📍 Pursuit, 47-10 Austell Place, Long Island City</p>
<p>A hands-on hackathon where you'll use open data and APIs to build practical solutions for New York City, then pitch your project for prizes. Hosted by Pursuit. <a href="https://luma.com/3jtm4813">Request to join →</a></p>
<h3><strong>Spirits Night: A Growth Leaders Happy Hour</strong></h3>
<p>📅 7:00–10:00 PM
📍 Details shared after approval</p>
<p>An open-bar happy hour for B2C app growth leaders to swap the launch flops, flatlined metrics, and failed features teams rarely talk about — plus, snacks and a Superwall-curated playlist. Hosted by Superwall and AppCap. <a href="https://luma.com/sw-spirits-night">Request to join →</a></p>
<h3><strong>Apps Capital Dinner: Founders &amp; Investors</strong></h3>
<p>📅 7:30–10:00 PM
📍 Details shared after approval</p>
<p>An exclusive dinner bringing consumer app founders together with the angels, VCs, PE firms, and M&amp;A and acquisition partners who back and buy apps. Hosted by EHVM and Tangent. <a href="https://luma.com/8dth97a7">Request an invite →</a></p>
<h2><strong>Tuesday, October 20</strong></h2>
<h3><strong>App Owners Workout</strong></h3>
<p>📅 8:30–9:30 AM
📍 Barry's Chelsea</p>
<p>A private workout and breakfast for app founders, CEOs, and growth or monetization leaders to compare notes on profitable scaling, subscriptions, acquisition, and retention. Hosted by Scalebay and RevenueCat. <a href="https://luma.com/crf57vg4">Request to join →</a></p>
<h3><strong>Pickleball Social by Noise &amp; RevenueCat</strong></h3>
<p>📅 9:00 AM–12:00 PM
📍 CityPickle Times Square, 1501 Broadway, 8th Floor</p>
<p>A morning of pickleball, coaching, and friendly competition, with snacks and candid conversation about content-led app growth. Hosted by Noise and RevenueCat. <a href="https://luma.com/yqj0w6ql">Request to join →</a></p>
<h3><strong>Deadly Good Growth Session</strong></h3>
<p>📅 9:30 AM–4:30 PM
📍 Superwall, 135 W 20th St, Suite 600</p>
<p>A full-day monetization session for growth and product leaders, with a live paywall teardown, customer panel, lunch, and hands-on demo. Hosted by Superwall. <a href="https://luma.com/sw-growth-session">Request to join →</a></p>
<h3><strong>Climb the Paywall</strong></h3>
<p>📅 10:00 AM–12:00 PM
📍 Details shared after approval</p>
<p>A beginner-friendly bouldering session where the routes are named after real growth challenges, followed by coffee and candid growth stories. Hosted by An Nayal (consumerxai) and RevenueCat. <a href="https://luma.com/mnc3hgpf">Request to join →</a></p>
<h3><strong>Women in Apps Brunch</strong></h3>
<p>📅 11:00 AM–1:00 PM
📍 Details shared after approval</p>
<p>An intimate brunch for women building, growing, designing, marketing, investing in, and shaping apps, focused on honest conversation and new connections. Hosted by Molly Williams (Wild Minds) and RevenueCat. <a href="https://luma.com/4gh0zs0j">Request to join →</a></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/93e0f670e0aa66ca9121eca4abc0d6f4c3365a11-1280x853.jpg" alt=""/><figcaption>2025's Women in Apps brunch</figcaption></figure>
<h3><strong>Scaling Paid Ads from $100K to $1M and Beyond: Q&amp;A for Growth Operators</strong></h3>
<p>📅 2:00–3:30 PM
📍 Details shared after approval</p>
<p>A no-slides Q&amp;A on scaling paid acquisition, covering creative systems, predictive LTV, unit economics, and the dashboards behind more than $100M in ad spend. Hosted by Tyler Scheviak and Will Kent. <a href="https://luma.com/yuj3ol88">Request to join →</a></p>
<h3><strong>Oversubscribed</strong></h3>
<p>📅 4:00–6:30 PM
📍 One Times Square</p>
<p>An invite-only gathering for C-suite product, growth, and revenue leaders at consumer subscription apps with $100M+ ARR. Hosted by RevenueCat, Houlihan Lokey, and Goldman Sachs; invitations are issued directly.</p>
<h3><strong>Women in Apps Art Cafe x the gapp</strong></h3>
<p>📅 4:00–6:00 PM
📍 Details shared after approval</p>
<p>An intentionally small art cafe for women working in apps to put their phones down, paint ceramics, and meet the people behind the products, campaigns, and growth charts. Hosted by Larissa Morimoto and the gapp. <a href="https://luma.com/c5ppawzf">Request to join →</a></p>
<h3><strong>App Week Padel: Founders Racket Club × RevenueCat × Paddle × BlueThrone</strong></h3>
<p>📅 4:00–7:00 PM
📍 DUMBO, Brooklyn</p>
<p>An intimate padel session for founders, executives, and app builders to play, meet, and connect away from the conference floor. Hosted by Founders Racket Club, RevenueCat, Paddle, and BlueThrone. <a href="https://luma.com/obt07q4d">Request an invite →</a></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9c405760eb52eac75d5923b7ba7f870540b14515-1228x1536.webp" alt=""/><figcaption>New York App Week 2025's Founders Racket Club meetup</figcaption></figure>
<h3><strong>The Pre Game Audit</strong></h3>
<p>📅 4:30–6:30 PM
📍 Details shared after approval</p>
<p>A casual pregame with free drinks, app owners, and growth practitioners. Bring your app for a live audit and leave with practical ideas for your growth plan. Hosted by Fload. <a href="https://luma.com/7h2l9gyp">Request to join →</a></p>
<h3><strong>(H) Appy Hour with Botsi</strong></h3>
<p>📅 4:30–7:00 PM
📍 Hell’s Kitchen</p>
<p>A relaxed happy hour for app founders and growth leaders, with free drinks, food, and a private lounge for swapping growth wins and losses (or just unwinding after a busy day). Hosted by Botsi. <a href="https://luma.com/0ggiuzl4">Request to join →</a></p>
<h3><strong>Dinner for App Founders</strong></h3>
<p>📅 4:30–6:30 PM
📍 NoMad</p>
<p>A small dinner for founders, CEOs, and growth leaders running subscription apps to share food, drinks, and candid stories from the trenches, with a secret guest joining the table. Hosted by Superwall. <a href="https://luma.com/sw-at-app-week-1">Request to join →</a></p>
<h3><strong>App Founders Dinner</strong></h3>
<p>📅 5:00–7:00 PM
📍 Hell's Kitchen</p>
<p>A candlelit dinner for a small group of founders and growth operators who've scaled real subscription businesses, designed for candid conversation away from the conference crowd. Hosted by Superwall and Elemental Growth. <a href="https://luma.com/sw-eg-dinner">Request to join →</a></p>
<h3><strong>The Shippies Award Show</strong></h3>
<p>📅 7:00–11:00 PM
📍 Intrepid Museum</p>
<p>All aboard <a href="https://appgrowthannual.com/shippies">The Shippies</a> award ceremony! RevenueCat’s annual awards celebrate the people, ideas, and apps shaping the future of the subscription economy. And this time, it’s on a literal ship. Complete with red carpet, retired space shuttle, and unbeatable views of the Manhattan skyline. Join us to celebrate the best in app growth, monetization, and subscription innovation, plus every winner of Shipaton. <a href="https://luma.com/revenu-dk6j">Request to join →</a></p>
<h2><strong>Wednesday, October 21</strong></h2>
<h3><strong>App Growth Annual</strong></h3>
<p>📅 8:00 AM–9:00 PM
📍 The Glasshouse + online worldwide</p>
<p>RevenueCat's flagship event for the people building, growing, and shaping what's next in apps. Keynotes include Ravi Mehta (Tinder), Julie Hansen (Babbel), Greg Cohn (Burner), and Ajay Mehta (Tolan). You'll also get a first look at RevenueCat's 2027 roadmap, side quests in The Meowtrix challenge, plenty of time to network, and six workshops from industry experts like Lisa Kennelly (Klarna), Marcus Burke (Ctrl Shift), Sven Jürgens, and Bria Sullivan (Honey B Games). <a href="https://appgrowthannual.com/register">Attend online for free →</a></p>
<h3><strong>Building &amp; Scaling a Successful UGC Program with Amazing Margins</strong></h3>
<p>📅 6:00–7:00 PM
📍 Details shared after approval</p>
<p>A hands-on workshop for app founders. Discuss building and scaling a high-margin UGC program, leave with your own program mapped out on paper: what it costs to run, what it should return, and where your current setup is leaking margin. Hosted by GlossKit. <a href="https://luma.com/zfdohi1w">Request to join →</a></p>
<h2><strong>Thursday, October 22</strong></h2>
<h3><strong>New York App Week 5K with RevenueCat &amp; Runna</strong></h3>
<p>📅 8:00–10:00 AM
📍 Bluestone Lane Chelsea Piers Café</p>
<p>Start the morning with a relaxed 5k along the Hudson, starting and finishing at Bluestone Lane Chelsea Piers. Breakfast and coffee's waiting at the finish line (+ finisher medals and limited edition swag!). Run, jog, or walk — all paces welcome. Receive a two-week free trial to Runna ahead of the event. Hosted by RevenueCat and Runna. <a href="https://luma.com/nyaw5K">Request to join →</a></p>
<h3><strong>Software Sommelier by Lickability</strong></h3>
<p>📅 10:00 AM–12:30 PM
📍 Manhattan</p>
<p>A cozy, caffeinated design lab: bring an app or prototype for live, actionable product and UX feedback from the Lickability team, then compare notes with fellow app builders. Hosted by Lickability. <a href="https://luma.com/43oort49">Request to join →</a></p>
<h3><strong>Shipaton Demo Day in Times Square</strong></h3>
<p>📅 10:00 AM–6:00 PM
📍 Times Square</p>
<p>We’re taking #buildinginpublic to a whole new level as Shipaton winners take over demo pods in Times Square to show off their apps and get real feedback all day long. Open to the public, no registration required. Hosted by RevenueCat.</p>
<h3><strong>RAGA Apps Cowork</strong></h3>
<p>📅 10:00 AM–4:00 PM
📍 Details shared after approval</p>
<p>A shared workspace for app builders to get things done between App Growth Annual and New York App Week events. Open to locals and visitors, with limited, time-slotted seating. Hosted by EHVM and Tangent. <a href="https://luma.com/wwgi249c">Request to join →</a></p>
<h3><strong>Apple Ads Masterclass: Maximum ROI, End to End</strong></h3>
<p>📅 11:00 AM–12:00 PM
📍 Details shared after approval</p>
<p>A hands-on masterclass that uses a real client case to connect Apple Ads acquisition and ROAS with RevenueCat subscription and paywall data, so you leave with a practical end-to-end ROI framework. Hosted by Asapty and RevenueCat. <a href="https://luma.com/06hgn26s">Request to join →</a></p>
<h3><strong>Appstore Connect — NYC</strong></h3>
<p>📅 6:30–9:30 PM
📍 Details shared after approval</p>
<p>A community meetup for founders and builders in mobile apps and games to exchange ideas and explore consumer app trends, user acquisition, and monetization. Hosted by Appstore Connect. <a href="https://luma.com/gjw3js1t">Request to join →</a></p>
<h3><strong>A/B or Not to Be</strong></h3>
<p>📅 6:30–9:30 PM
📍 Details shared after approval</p>
<p>An interactive evening of real growth experiments and strong opinions. Attendees vote A or B on real growth experiments before the team reveals the actual results. Followed up with Would You Rather: App Industry Edition, and a live app growth roast. Hosted by Apptuitive. <a href="https://luma.com/v63nd8d9">Request to join →</a></p>
<h3><strong>Scaled Consumer App Founder Dinner</strong></h3>
<p>📅 7:00–9:00 PM
📍 Brooklyn</p>
<p>A small dinner for founders of scaled consumer subscription apps to share candidly what is, and isn't, working. Hosted by Will Kent. <a href="https://luma.com/fu6se1rz">Request to join →</a></p>
<h2><strong>What’s on your schedule?</strong></h2>
<p>Whether you’re hosting a meetup or jogging along the Hudson, there’s an event for every type of app pro, and enough going on to quickly fill your calendar. Join as many events as you like, or show up to a single co-working session and stay heads-down. The only non-negotiable in your diary is <a href="http://appgrowthannual.com/virtual">App Growth Annual</a>, happening <strong>October 21. </strong>We’ll see you there!</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Take EU payments outside In-App Purchases, with Apple's reporting handled for you]]></title>
      <link>https://www.revenuecat.com/blog/company/apple-in-app-web-checkouts-eu</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/apple-in-app-web-checkouts-eu</guid>
      <pubDate>Thu, 01 Oct 2026 08:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Niklas Winkels]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[From October 1, EU apps can sell through their own checkout inside the app for 20% (10% reduced) if they pass App Review and report every transaction to Apple. RevenueCat ships the in-app checkout, compliant paywall buttons, and the reporting from day one.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/fa556d13006056f5a1b4ed01b5f9bdcc76550042-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>For 18 years, Apple's standard App Store commission sat at 30%. On October 1, it drops as low as 5% and every EU developer works under one set of Apple terms. Here's everything that's changing, what Apple asks for in return, and how RevenueCat can handle the changes for you.</p>
<h2>What changes on October 1</h2>
<p>Following its dispute with the European Commission under the Digital Markets Act (DMA), Apple <a href="https://www.apple.com/newsroom/2026/08/apple-announces-changes-for-apps-in-the-european-union/" target="_blank" rel="noopener noreferrer">replaced its EU fee structure</a>:</p>
<table>
<thead><tr>
<th><p>Where the buyer pays</p></th>
<th><p>Standard</p></th>
<th><p>Reduced</p></th>
</tr></thead>
<tbody>
<tr>
<td><p>Apple In-App Purchase</p></td>
<td><p>26%</p></td>
<td><p>15%</p></td>
</tr>
<tr>
<td><p>Your own checkout inside the app</p></td>
<td><p>20%</p></td>
<td><p>10%</p></td>
</tr>
<tr>
<td><p>Link out to the web</p></td>
<td><p>15%</p></td>
<td><p>10%</p></td>
</tr>
<tr>
<td><p>Alternative marketplace or web distribution (Core Technology Commission)</p></td>
<td><p>5%</p></td>
<td><p>5%</p></td>
</tr>
</tbody>
</table>
<p><em>Rates are from Apple's </em><a href="https://developer.apple.com/support/payment-options-on-the-app-store-in-the-eu" target="_blank" rel="noopener noreferrer"><em>Payment options on the App Store in the EU</em></a><em>. The reduced column applies if you're in the App Store Small Business Program, the Mini Apps Partner Program, or the Video Partner Program, and to any auto-renewing subscription after its first year.</em></p>
<p>Here's an overview of the changes:</p>
<ul>
<li>The acquisition fee, store services fee, and per-install Core Technology Fee are gone</li>
<li>A single commission replaces all three, and where the buyer pays decides the rate</li>
<li>From October 1, 2026, every EU developer works under one set of Apple terms, and the commission depends on where the buyer pays</li>
<li>Small businesses and subscriptions after their first year pay just 10% on either alternative path</li>
<li>Paying through your own checkout inside the app is now part of standard terms for every EU developer (it was already possible under Apple's Alternative Terms Addendum for Apps in the EU)</li>
<li>You can offer it alongside <a href="https://www.revenuecat.com/platform/ios-in-app-purchases">in-app purchase</a>, as long as Apple's button is at least as prominent as yours, or drop in-app purchase and run only your own checkout. Apple Pay is allowed as a payment method.</li>
</ul>
<h2>The catch: Apple reviews the flow and collects the reports</h2>
<p>The lower rates come with two conditions:</p>
<ol>
<li>Apple reviews the purchase flow</li>
<li>Apple collects a report on every transaction</li>
</ol>
<p>Before a buyer reaches your checkout, your app confirms the device and storefront are eligible for external purchases, shows Apple's disclosure sheet, and requests a token from StoreKit that declares whether the purchase happens inside the app or in a browser. Apple issues a different token for each path, and reviews the order of the steps.</p>
<p>Every token Apple mints is a token you owe a report on. You report the first purchase, every renewal, every product change, every refund, and every cancellation it produces. You also report every token that never turned into a sale at all. Apple gives you seven days to attribute a link-out purchase back to the token that started it, and Apple wants each month's report within 15 days of that month ending. Once you pick your payment options, you keep them available for 12 months.</p>
<p>That's a <em>lot</em> to build before you even see a cent of the lower commission. Luckily, we've built all of that into RevenueCat.</p>
<h2>What RevenueCat handles for you</h2>
<p>From October 1, you'll get in-app checkout, paywalls that clear App Review, the Apple reporting, and revenue numbers with Apple's commission already taken out, without having to build any of the reporting yourself. Here's how:</p>
<h3>Hands-free reporting to Apple</h3>
<p>If the purchase runs through RevenueCat, the reporting is taken care of.</p>
<p>Every purchase through Stripe Billing, Paddle Billing, or Web Billing already flows through RevenueCat, so the reporting runs off data you're already sending. The SDK posts the token before the checkout opens and passes an opaque identifier along in the checkout URL. When the purchase lands, RevenueCat matches the two, works out the tax-exclusive and tax-inclusive amounts Apple's report format demands, and submits on Apple's schedule.</p>
<p>The hard part is that the three providers disagree about what a price means:</p>
<ul>
<li>A Stripe amount is tax-inclusive or tax-exclusive depending on how you configured the product</li>
<li>Paddle zeroes out tax on a refund</li>
<li>RevenueCat normalizes all of it into the shape Apple accepts</li>
</ul>
<p>Renewals, product changes, refunds, and cancellations report off the same subscription as they happen. RevenueCat reports tokens that never converted as unused and enforces the seven-day window on its side, so a late purchase never gets filed against a token Apple would reject.</p>
<h3>Purchase buttons that meet Apple's design rules</h3>
<p>Apple's EU terms include design requirements, meaning Apple doesn't only check your reports; App Review checks your paywall too. If you offer an alternative payment next to in-app purchase, the in-app purchase button has to use Apple's standard styling and be at least as prominent. If you only offer in-app purchase, you're free to style it.</p>
<p><a href="https://www.revenuecat.com/feature/paywalls">RevenueCat Paywalls</a> get new <a href="https://www.revenuecat.com/web-purchase-button">purchase button</a> variants for both cases. Pick the Apple-styled button when your own checkout sits next to it, or use the custom branding options when in-app purchase is the only path.</p>
<h3>Apple's commission is tracked on every web transaction</h3>
<p>Once the money's in, you need to know how much of it you actually keep. A web transaction with a token attached now carries two commissions: your processor's and Apple's. Until now, your payment processor reported its own fee and knew nothing about Apple's, so the moment you added a second path, the take-home figure in your revenue reporting went wrong.</p>
<p>RevenueCat breaks Apple's commission out on those transactions, so your charts will be able to show what you keep after both cuts (coming soon).</p>
<h3>A web checkout that opens in your app</h3>
<p>Tap a purchase button on a RevenueCat paywall and a bottom sheet checkout slides up over it. The buyer pays and the sheet closes without leaving your app.</p>
<p>Behind the tap, the SDK runs Apple's sequence: eligibility check, disclosure sheet, token request declared as in-app, token stored on RevenueCat's backend before the checkout opens.</p>
<p>We are actively working on bringing the bottom sheet in-app checkout to you as soon as possible. If you want to know first when it drops, <a href="https://revenuecateventfeedback.typeform.com/to/RhysQ72x">sign up for early access here</a>. At launch, the in-app checkout works with Stripe Billing and Paddle Billing, with Web Billing to follow.</p>
<p>Prefer the browser? The same button can <a href="https://www.revenuecat.com/blog/growth/web-to-app-funnels/">link out instead</a> at 15%, and Apple's disclosure sheet appears either way.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/026b671cdfb358f2be4509f98f9f1ad179163247-802x698.png" alt="purchase button properies showing web checkout in-app and link out"/></figure>
<h3>Show the alternative checkout only where it's allowed</h3>
<p>Finally, Apple's terms allow external purchase flows on EU storefronts only, and not to minors. Making sure the alternative checkout appears only for eligible users is your responsibility as the developer.</p>
<p><a href="https://www.revenuecat.com/feature/targeting">RevenueCat Targeting</a> lets you serve a paywall with the alternative button to EU storefronts only, and keep a paywall with only in-app purchases everywhere else. <a href="https://www.revenuecat.com/feature/ai-agent">Rico</a> can set that up for you: describe the rule and it builds the targeting.</p>
<h2>Which path should you use?</h2>
<p>Five percentage points separate the in-app sheet from the browser link-out, but the browser path costs you conversion at the disclosure sheet. We've seen this before: in our <a href="https://www.revenuecat.com/blog/growth/iap-vs-web-purchases-conversion-test/">web vs. in-app purchase test</a>, web subscriptions ended up with 6% less take-home revenue.</p>
<p>We're running the new experiment from October 1 and we'll share the numbers after.</p>
<p>Until then, model it for your own price points: our <a href="https://www.revenuecat.com/app-store-fee-calculator">app store fee calculator</a> takes your monthly revenue, average transaction size, provider, and program status, and shows what each path leaves you with, including the processor's fee on top of Apple's.</p>
<h2>Getting started</h2>
<ol>
<li>Request the StoreKit External Purchase entitlement from Apple and list the countries you want it in. That list is fixed in your provisioning profile, so get it right before you build.</li>
<li>Update to <strong>purchases-ios 5.89.0</strong> or later and turn on <a href="https://www.revenuecat.com/blog/engineering/app-to-web-purchase-guidelines">external purchase links</a> in your RevenueCat configuration.</li>
<li>Add a purchase button to your paywall, point it at your Stripe Billing or Paddle Billing checkout, and target the paywall to EU storefronts. The <a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/web-purchase-button">app-to-web purchase docs</a> cover the configuration.</li>
</ol>
<p>Apple's new terms go live on October 1. With RevenueCat, your checkout, paywalls, and reporting can too.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[One prime-time TV segment brought WeWard 300,000 downloads in under five minutes, and crashed the app]]></title>
      <link>https://www.revenuecat.com/blog/growth/yves-benchimol-weward-sub-club-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/yves-benchimol-weward-sub-club-podcast-2026</guid>
      <pubDate>Wed, 30 Sep 2026 13:01:24 GMT</pubDate>
      <dc:creator><![CDATA[David Barnard]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[US journalists passed on Yves Benchimol's pitch until he could offer them a conversation with Venus Williams.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/55ee61eac05fc6e705888583a43f69332e53f49a-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>WeWard launched in France in 2019 as a free app that pays you to walk: 1,000 steps earns points you can convert into cash or gift cards. It grew for four years without buying a single ad. &quot;Didn't spend any money in Meta, Google,&quot; says founder Yves Benchimol. When Jacob Eiting checks whether that covers direct user acquisition too, the answer is &quot;Never. Only organic and press.&quot;</p>
<p><a href="https://www.youtube.com/watch?v=Q_9DVdKR8xw">Watch on YouTube</a></p>
<p>The press wasn't free, exactly. Yves had worked with a French PR agency at his first company and hired them again at, by his recollection, &quot;maybe between three and 5,000 per month&quot; — on deferred payment, because WeWard had no money yet. About seven or eight months after launch the app landed a segment on one of France's biggest prime-time shows. &quot;I think it was in less than five minutes, we got 300,000 downloads,&quot; he says. &quot;Of course, the app crash instantly, it was terrible.&quot;</p>
<p>The crash didn't matter. The inbox filled with people asking to get in, and when the team checked retention it was, in his words, &quot;very high compared to the average market.&quot; So they added ads, on the logic that at roughly 100,000 daily users an ad-supported free app starts to pay. The retainer, meanwhile, kept producing. &quot;Some months you have zero, but some months you have half a million downloads,&quot; Yves says. &quot;So half a million download, you know how much it costs in UA. You can pay the agency for 10 years easily.&quot;</p>
<h2><strong>The same playbook produced nothing in the US</strong></h2>
<p>Press worked in Italy and Spain as well. Then Yves moved to New York, with money this time, and &quot;took one of the most expensive PR agency.&quot; The French success didn't repeat. His diagnosis is unsparing: &quot;It's just due to the fact that I'm nobody here in this country.&quot; American journalists wanted a story, and &quot;one of the biggest app in France&quot; wasn't one. &quot;Did you raise money with someone impressive? Did you do something crazy?&quot; he recalls being asked. &quot;And no, we are a profitable startup that's coming. And so it was not enough.&quot;</p>
<p>Being French didn't help either. &quot;Being French is not a benefit, except if you are in the wine or the luxury brands, or the cheese maybe.&quot; US users needed to see the product as American, and the product itself had to reach the level of polish he saw in US banking apps compared to French ones.</p>
<p>So he went looking for a US icon, ideally an athlete with wellness values, and started asking VCs and agencies for introductions. The search was deliberate; the meeting wasn't. At lunch with his family in a New York restaurant, Venus Williams sat down at the next table and said hello. He waited for a lull, walked over, and pitched. Williams was sitting with the founder of a European tech investment bank, and the questions turned to day-seven retention and LTV within minutes. The deal that followed went further than the ambassador arrangement Yves had in mind. &quot;Okay, I want shares, so I want to be also investor,&quot; she told him.</p>
<p>Two years on, he is careful about what that bought. &quot;There was multiple impacts and it's very difficult to quantify.&quot; But the mechanisms are clear enough to list: hiring got easier because the company had credibility, CAC comes down when creative carries a known face, co-produced content lifts retention and LTV, her social posts drive organic installs, and journalists who ignored a French founder reply when a conversation with Venus Williams is on offer. &quot;You will not be able to quantify day one,&quot; he says. &quot;It's progressive.&quot;</p>
<h2><strong>Nike, Adidas and Amazon pay WeWard a commission</strong></h2>
<p>WeWard may be one of the most diversified apps in the business, and the subscription came last. Ads work because the audience is huge. In-app purchases exist. The premium tier, built on RevenueCat, launched well after everything else and is &quot;not a major source of revenue&quot; yet, though it is growing fast. And then there is affiliate. &quot;We have Nike, Adidas, Expedia, Booking.com, all of Amazon, it's part of the clients of WeWard,&quot; Yves says.</p>
<p>He is quick to say he didn't invent anything. Affiliate is a mature market with the tracking already built, and brands like it because it is performance-based. &quot;They just pay a commission of a sales.&quot; WeWard went to affiliate platforms and to brands directly, offered to promote them for a share of the sales generated, and sold the environment: a wellness and fitness context brands want to be seen in. The team also says no. &quot;This is why sometimes we reject to work with some specific brands because we believe that their value and the way they change the way people act is totally opposite to what we are trying to do.&quot; Jacob's aside that the best CPMs in 2026 probably come from gambling apps went unchallenged.</p>
<h2><strong>WeWard refuses to measure time in app</strong></h2>
<p>Every revenue stream sits under one rule. &quot;We don't track the time spent into the app because we don't want to be a target for the team,&quot; Yves says. The number WeWard watches is steps, on the theory that a user whose lifestyle hasn't changed in six months leaves anyway. Every feature ships through an A/B test that has to move that number. &quot;So for us, the success of the number of steps of people is the success of the company.&quot;</p>
<p>The newest test of that rule is a walking mode that locks TikTok, Instagram and other apps until the user hits a step goal, making screen time itself the reward. The head of growth in New York vibe-coded it and shipped it a few months ago. &quot;We already measure that people that use these features walk 10% more,&quot; Yves says. For a company whose ads depend on people opening the app, building a feature that sends them outside is a strange thing to celebrate. It's also the point.</p>
<p>In <a href="https://www.youtube.com/watch?v=Q_9DVdKR8xw" target="_blank" rel="noopener noreferrer">the full episode</a>, Yves also explains why WeWard dropped its original Ethereum-based rewards after three or four months when users said loyalty points were fine, why he considers deciding to stop the hardest part of any pivot, and why an 80-person company in 29 countries still counts B2B as its biggest failure of the year.</p>
<h2>Guest links</h2>
<ul>
<li><a href="https://www.linkedin.com/in/yvesbenchimol/?isSelfProfile=false" target="_blank" rel="noopener noreferrer">Yves Benchimol on LinkedIn</a></li>
<li><a href="https://www.wewardapp.com/" target="_blank" rel="noopener noreferrer">WeWard</a></li>
<li><a href="https://careers.wewardapp.com/" target="_blank" rel="noopener noreferrer">WeWard careers</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Wrapping a Swift Package in a Gradle Module: How RevenueCat's KMP SDK Builds purchases-ios Without CocoaPods]]></title>
      <link>https://www.revenuecat.com/blog/engineering/kmp-swift-gradle</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/kmp-swift-gradle</guid>
      <pubDate>Wed, 30 Sep 2026 06:39:37 GMT</pubDate>
      <dc:creator><![CDATA[Jaewoong Eum]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[In this article, you'll dive deep into how the SDK wraps a Swift package in a Gradle module.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/fa84be6b43724bed576aaf6f03c8874f2bddcda0-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Kotlin/Native talks to Apple code through <a href="https://kotlinlang.org/docs/native-c-interop.html">cinterop</a>, the tool that generates Kotlin bindings from C and Objective-C headers, and for most of Kotlin Multiplatform's history, the standard way to hand it an iOS dependency has been the CocoaPods plugin. Without CocoaPods, a Swift package leaves cinterop with nothing to read: Swift has no headers, and Gradle can't build a Swift package by itself.</p>
<p><a href="https://github.com/RevenueCat/purchases-kmp">RevenueCat's Kotlin Multiplatform SDK</a> took that route anyway in 3.0.0. It dropped CocoaPods and now compiles the <code>purchases-ios</code> Swift package from source inside two Gradle modules, <code>kn-core</code> and <code>kn-ui</code>, that contain almost no Kotlin. The question worth answering is what those modules actually do: how a Swift package becomes something the Kotlin/Native compiler can bind, cache, and publish.</p>
<p>In this article, you'll dive deep into how the SDK wraps a Swift package in a Gradle module, exploring the <code>swiftPackage()</code> DSL, <code>swift package describe</code>, the Swift build task, the source hash in the <code>.def</code> file, the facade module, the release build configuration, resource bundling, type sharing across modules, and the shims around cinterop.</p>
<h2><strong>The fundamental problem: cinterop reads Objective-C, and Gradle doesn't build Swift</strong></h2>
<p>The cinterop tool takes its instructions from a definition file, a <code>.def</code>, that tells it what to parse and what to link. For an Objective-C library, three entries carry most of the weight:</p>
<pre><code>language = Objective-C
modules = MyLibrary
staticLibraries = libMyLibrary.a</code></pre>
<p><code>language</code> switches the parser from C to Objective-C, and <code>modules</code> names a Clang module, a named set of headers, for cinterop to read. <code>staticLibraries</code> names a static library, an <code>.a</code> archive of compiled object files, that cinterop copies into the klib it produces. A klib is Kotlin/Native's library format, the counterpart of a JAR on the JVM, and as the <a href="https://kotlinlang.org/docs/native-definition-file.html">Kotlin documentation</a> puts it, &quot;When using a <code>klib</code> like this in your program, the library is linked automatically.&quot;</p>
<p>A Swift package provides none of these. Swift has no header files, so there's nothing to parse until you ask the compiler to generate an Objective-C header, and that header only contains the declarations Swift exposes to Objective-C. The package itself contains no Clang module and no archive to link, because SwiftPM, the Swift Package Manager, compiles a package as part of building whatever app depends on it. Even reading the package is a problem: <code>Package.swift</code> is a Swift program, and only SwiftPM can evaluate it.</p>
<p>Before 3.0.0, the SDK solved this the way many Kotlin Multiplatform libraries did, with the Kotlin CocoaPods plugin, which hands the build to CocoaPods and Xcode and then points cinterop at what they produce. Looking at the core of the 2.x configuration in the <code>core</code> module:</p>
<pre><code>cocoapods {
    ios.deploymentTarget = libs.versions.ios.deploymentTarget.core.get()

    framework {
        baseName = &quot;Purchases&quot;
        isStatic = true
    }

    pod(&quot;PurchasesHybridCommon&quot;) {
        version = libs.versions.revenuecat.common.get()
        extraOpts += listOf(&quot;-compiler-option&quot;, &quot;-fmodules&quot;)
    }
}</code></pre>
<p>This works, but every step it hides has a cost. Building the iOS side needed a working CocoaPods installation, apps had to add <code>PurchasesHybridCommon</code> to their own Xcode project and keep its version paired with the Kotlin SDK, and parallel Gradle tasks could race each other inside CocoaPods' shared cache. In April 2026, the team fixed a flaky iOS CI job, and the commit message spells the problem out:</p>
<p>Gradle runs podInstallSyntheticIos for core, models, mappings, and revenuecatui in parallel. All four tasks hit the shared CocoaPods cache (~Library/Caches/CocoaPods/Pods/) concurrently, causing Errno::ENOENT race conditions during cp_r.</p>
<p>A month later, 3.0.0 deleted the plugin and the CI workaround with it. What replaced it doesn't hide the build behind another tool: it runs each step as a Gradle task, in about 1,200 lines of convention plugin code under <code><a href="https://github.com/RevenueCat/purchases-kmp/tree/main/build-logic/convention/src/main/kotlin/com/revenuecat/purchases/kmp/buildlogic/swift">build-logic/.../swift</a></code>.</p>
<p>JetBrains is working on an official answer too: <a href="https://kotlinlang.org/docs/multiplatform/multiplatform-spm-import.html">SwiftPM import</a>, an Alpha feature that arrived with the Kotlin 2.4.20 release candidates. The SDK builds on Kotlin 2.3.20 with its own implementation, which makes it a readable reference for what any such integration has to handle.</p>
<h2><strong>Declaring a Swift package: The swiftPackage() DSL</strong></h2>
<p>Everything starts in a module's build file. In SwiftPM, a target is a module inside a package, and <code>kn-core</code> declares two Swift targets from its <code>appleMain</code> source set: <code>RevenueCat</code>, from the <code>purchases-ios</code> checkout that the repository tracks as a git submodule under <code>upstream/</code>, and <code>AdditionalSwift</code>, a small local package you'll meet later. The second declaration shows the shape without optional arguments:</p>
<pre><code>appleMain.dependencies {
    swiftPackage(
        path = file(&quot;src/swift&quot;),
        target = &quot;AdditionalSwift&quot;,
        packageName = &quot;com.revenuecat.purchases.kn.core.additional&quot;
    )
}</code></pre>
<p>path points at the directory containing Package.swift, target picks one SwiftPM target to build, and packageName is the Kotlin package the generated bindings land in. It reads like a dependency declaration, but it doesn't add anything to a Gradle configuration. If you examine the function:</p>
<pre><code>fun KotlinDependencyHandler.swiftPackage(
    path: File,
    target: String,
    packageName: String,
    customDeclarations: String? = null,
    swiftSettings: SwiftSettings? = null,
) {
    val registry = project.extensions.findByType(SwiftPackageRegistry::class.java)
        ?: error(&quot;SwiftPackageRegistry not found. ...&quot;)
    val dependency = SwiftDependency(
        packagePath = path,
        target = target,
        packageName = packageName,
        sourceSetName = getSourceSetName(),
        customDeclarations = customDeclarations,
        swiftSettings = swiftSettings,
    )
    registry.add(dependency)
    project.getOrCreateGlobalSwiftRegistry().register(target, project, dependency)
}</code></pre>
<p>The function records a <code>SwiftDependency</code> in two places, a registry on the current project and a global registry on the root project, and does nothing else. The real configuration happens later, once the build script has finished evaluating.</p>
<p>The one surprising call is <code>getSourceSetName()</code>. The build needs to know which source set the declaration came from, because that decides which Kotlin/Native targets receive bindings, but <code>KotlinDependencyHandler</code> doesn't expose its source set publicly. The function finds it by reflection:</p>
<pre><code>private fun KotlinDependencyHandler.getSourceSetName(): String {
    var current: Class&lt;*&gt;? = this.javaClass
    while (current != null &amp;&amp; current != Any::class.java) {
        for (field in current.declaredFields) {
            try {
                field.isAccessible = true
                val value = field.get(this)
                if (value is KotlinSourceSet) {
                    return value.name
                }
            } catch (_: Exception) {
                // Continue to next field
            }
        }
        current = current.superclass
    }
    error(&quot;Could not determine source set for swiftPackage(). ...&quot;)
}</code></pre>
<p>It walks the handler's fields up the class hierarchy until one of them holds a <code>KotlinSourceSet</code>. This leans on Kotlin Gradle Plugin internals, and the full error message admits it: &quot;This might be due to a Kotlin Gradle Plugin version incompatibility.&quot; The name then feeds a simple mapping, where <code>appleMain</code> covers every <code>ios</code>, <code>macos</code>, <code>tvos</code>, and <code>watchos</code> target and <code>iosMain</code> covers only the <code>ios</code> ones. That's why <code>kn-core</code>, which declares its packages in <code>appleMain</code> and adds watchOS targets, builds Swift for iOS and watchOS, while <code>kn-ui</code> declares <code>RevenueCatUI</code>, the module that renders RevenueCat's paywalls and Customer Center, in <code>iosMain</code> and builds for iOS only.</p>
<p>The global registry adds one rule: a Swift target can have only one owner. Declaring the same target from a second Gradle project fails with &quot;Each Swift target should be owned by exactly one Gradle project.&quot; That guarantee is what later lets <code>kn-ui</code> find <code>RevenueCat</code> by name. Nothing declares that <code>kn-core</code> has to be configured first, though. The lookup works because Gradle happens to reach <code>kn-core</code> before <code>kn-ui</code>.</p>
<h2><strong>Reading the package: Letting SwiftPM describe itself</strong></h2>
<p>The convention plugin does its work in <code>afterEvaluate</code>, after every <code>swiftPackage()</code> call has run, and it starts by checking the host operating system. Everything that follows shells out to <code>xcrun</code>, which doesn't exist on Linux, so instead of failing configuration there and taking Android builds down with it, the plugin logs &quot;Skipping Swift dependency configuration on non-macOS host&quot; and leaves the module without Apple bindings on that machine.</p>
<p>On macOS, the plugin first needs the package's structure: where each target's sources live, which targets depend on which, and which resources they carry. Rather than parse <code>Package.swift</code>, it asks SwiftPM:</p>
<pre><code>internal fun Project.getSwiftPackageInfo(packageDir: File): SwiftPackageInfo {
    val result = providers.exec {
        workingDir = packageDir
        commandLine(&quot;xcrun&quot;, &quot;swift&quot;, &quot;package&quot;, &quot;describe&quot;, &quot;--type&quot;, &quot;json&quot;)
        isIgnoreExitValue = true
        // Avoids trying to use the iOS SDK to parse the Package.swift when building from Xcode.
        // This environment change is scoped to this subprocess only.
        environment(&quot;SDKROOT&quot;, &quot;&quot;)
    }

    val exitCode = result.result.get().exitValue
    if (exitCode != 0) {
        val stderr = result.standardError.asText.get()
        error(&quot;Failed to run 'swift package describe' in $packageDir (exit code $exitCode): $stderr&quot;)
    }

    return SwiftPackageInfo.parse(result.standardOutput.asText.get())
}</code></pre>
<p><code>swift package describe --type json</code> prints SwiftPM's own model of the package after it evaluates the manifest. The <code>purchases-ios</code> manifest reads <code>CI.xcconfig</code> and environment variables while it evaluates, so no static parser could reproduce what SwiftPM sees. From the JSON, <code>SwiftPackageInfo.parse()</code> keeps each target's <code>name</code>, <code>path</code>, <code>target_dependencies</code>, and <code>resources</code>, plus the package's platform deployment targets.</p>
<p>The <code>SDKROOT</code> line handles a quieter problem. When Gradle runs from an Xcode build phase, Xcode has already exported <code>SDKROOT</code> for the iOS SDK, and SwiftPM would try to use that SDK for a manifest that has to run on the Mac itself. Clearing the variable for this one subprocess keeps builds from Xcode working.</p>
<h2><strong>From Swift target to Clang module: A header, an archive, and a module map</strong></h2>
<p>For each Kotlin/Native target that includes the declaring source set, the plugin registers a <code>SwiftBuildTask</code> with a name like <code>compileSwiftRevenueCatIosSimulatorArm64</code>. Its job is to produce what the <code>.def</code> file needs: an Objective-C header, a static archive, and a module map. The first step runs <code>swift build</code> for one Swift target and one architecture, and each <code>-Xswiftc</code> or <code>-Xcc</code> in the command forwards the argument after it to the Swift compiler or to Clang:</p>
<pre><code>execOperations.exec {
    workingDir = packageDir.get().asFile
    environment(&quot;SDKROOT&quot;, &quot;&quot;)
    commandLine(
        listOf(
            &quot;xcrun&quot;, &quot;swift&quot;, &quot;build&quot;,
            &quot;--target&quot;, targetName,
            &quot;--configuration&quot;, configValue,
            &quot;--triple&quot;, tripleValue,
            &quot;--scratch-path&quot;, scratchPath.absolutePath,
            &quot;-Xswiftc&quot;, &quot;-sdk&quot;,
            &quot;-Xswiftc&quot;, sdkPath,
            &quot;-Xcc&quot;, &quot;-isysroot&quot;,
            &quot;-Xcc&quot;, sdkPath,
            &quot;-Xswiftc&quot;, &quot;-emit-objc-header&quot;,
            &quot;-Xswiftc&quot;, &quot;-emit-objc-header-path&quot;,
            &quot;-Xswiftc&quot;, targetOutputDir.resolve(headerName.get()).absolutePath
        ) + extraSwiftArgs
    )
}</code></pre>
<p>A few arguments carry most of the weight:</p>
<ul>
<li><code><strong>-triple</strong></code><strong> and </strong><code><strong>sdk</strong></code>: both derive from the <code>KonanTarget</code>, so <code>iosSimulatorArm64</code> builds for <code>arm64-apple-ios-simulator</code> against the iPhone Simulator SDK, and <code>watchosArm64</code> builds for <code>arm64_32-apple-watchos</code>. Each Swift target builds once per Kotlin/Native target.</li>
<li><code><strong>emit-objc-header-path</strong></code>: tells swiftc to write the module's Objective-C header, <code>RevenueCat-Swift.h</code>, into the task's output directory. The header holds only what Swift exposes to Objective-C, which is why every Kotlin binding carries an Objective-C name like <code>RCPurchases</code> or <code>RCCustomerInfo</code>.</li>
<li><code><strong>-scratch-path</strong></code>: gives each Swift target its own scratch directory, the folder where SwiftPM keeps its build files. The section on sharing types explains why.</li>
<li><code><strong>extraSwiftArgs</strong></code>: carries <code>SwiftSettings</code>, where each <code>define(&quot;FLAG&quot;)</code> becomes <code>Xswiftc -D -Xswiftc FLAG</code>.</li>
</ul>
<p><code>swift build --target</code> stops at object files and a Swift module. It doesn't produce an archive you can hand to a linker, so the task builds one itself:</p>
<pre><code>val buildPath = scratchPath.resolve(&quot;$tripleValue/$configValue&quot;)
val objectFiles = buildPath.resolve(&quot;$targetName.build&quot;)
    .walkTopDown()
    .filter { it.extension == &quot;o&quot; }
    .toList()

if (objectFiles.isEmpty()) {
    error(&quot;No object files found in ${buildPath.resolve(&quot;$targetName.build&quot;)}&quot;)
}

execOperations.exec {
    commandLine(
        listOf(
            &quot;libtool&quot;, &quot;-static&quot;, &quot;-o&quot;,
            targetOutputDir.resolve(libraryName.get()).absolutePath
        ) + objectFiles.map { it.absolutePath }
    )
}</code></pre>
<p>The walk only covers <code>&lt;Target&gt;.build</code>, so each archive holds exactly one target's code. Today, <code>libRevenueCat.a</code> contains 565 object files and defines the <code>RCPurchases</code> class, while <code>libRevenueCatUI.a</code> contains 333 and doesn't define it. When your app links both, RevenueCat's code comes from one archive, not two.</p>
<p>The last output is a module map, the file that tells Clang which headers make up a module. SwiftPM writes one of its own deep inside the scratch directory, but it points at the header's default location, and <code>-emit-objc-header-path</code> has moved the header elsewhere. So the task writes its own module map next to the header. For <code>RevenueCat</code>, the generated file is four lines:</p>
<pre><code>module RevenueCat {
    header &quot;RevenueCat-Swift.h&quot;
    export *
}</code></pre>
<p>The header, the archive, and the module map now sit together in <code>build/swift-packages/RevenueCat/&lt;konanTarget&gt;/</code>, the task's declared output directory. Writing its own module map also gives the build a place to list headers from other targets, which becomes important once two modules share types.</p>
<h2><strong>The .def file: A comment that keeps cinterop honest</strong></h2>
<p>A second task, <code>GenerateDefFileTask</code>, writes the definition file. Looking at the core of its task action:</p>
<pre><code>val sourceHash = computeSourceHash()

val baseContent = &quot;&quot;&quot;
    # sourceHash=$sourceHash
    language = Objective-C
    package = ${packageName.get()}
    modules = ${moduleName.get()}
    staticLibraries = ${libraryName.get()}
    linkerOpts = -L/usr/lib/swift
    linkerOpts.ios_x64 = -L$toolchain/lib/swift/iphonesimulator/
    linkerOpts.ios_arm64 = -L$toolchain/lib/swift/iphoneos/
    linkerOpts.ios_simulator_arm64 = -L$toolchain/lib/swift/iphonesimulator/
    linkerOpts.watchos_arm64 = -L$toolchain/lib/swift/watchos/
    linkerOpts.watchos_device_arm64 = -L$toolchain/lib/swift/watchos/
    linkerOpts.watchos_simulator_arm64 = -L$toolchain/lib/swift/watchsimulator/
&quot;&quot;&quot;.trimIndent()</code></pre>
<p>Most of it maps onto the three entries from earlier: the language, the module from the module map, and the archive. <code>package</code> sets the Kotlin package for the bindings, and the <code>linkerOpts</code> lines add Swift library search paths for the final link. The interesting line is the first one. <code># sourceHash=</code> is a comment that cinterop ignores, and it's there on purpose:</p>
<pre><code>private fun computeSourceHash(): String {
    val sourceDir = swiftSourceDir.get().asFile
    val digest = MessageDigest.getInstance(&quot;MD5&quot;)

    sourceDir.walkTopDown()
        .filter { it.isFile &amp;&amp; it.extension == &quot;swift&quot; }
        .sortedBy { it.relativeTo(sourceDir).path }
        .forEach { file -&gt;
            digest.update(file.relativeTo(sourceDir).path.toByteArray())
            digest.update(file.readBytes())
        }

    return digest.digest().joinToString(&quot;&quot;) { &quot;%02x&quot;.format(it) }
}</code></pre>
<p>It hashes every <code>.swift</code> file under the target's own source directory, path and contents, in a stable order, so any edit to those files, even to a comment, produces a different <code>.def</code> file.</p>
<p>The reason lives in how Gradle decides whether cinterop needs to run: it skips the task as <code>UP-TO-DATE</code> unless one of the task's tracked inputs changed. As of Kotlin 2.3.20, <code>CInteropProcess</code> tracks the <code>.def</code> file, option strings such as <code>compilerOpts</code> and <code>extraOpts</code>, dependency klibs, include directories registered through the Gradle DSL, and headers listed in a <code>headers</code> entry. This build lists a module instead of headers and passes its output directory only as <code>-I</code> and <code>-libraryPath</code> strings, so nothing inside that directory counts as an input: not the header, not the module map, not the archive.</p>
<p>Skipping cinterop costs more than stale bindings, because cinterop doesn't only read the archive, it copies it. After a build, the cinterop klib contains <code>default/targets/&lt;target&gt;/included/libRevenueCat.a</code>, byte for byte the archive the Swift task produced, and when cinterop doesn't run, that copy doesn't change. Here is the pipeline so far. The only tracked path from a Swift edit to the klib runs through the <code>.def</code> file:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a0941ac6b87b86de1417de2dc7767b6cc1c8c16e-1896x1284.png" alt=""/></figure>
<p>Replacing the hash with a constant makes the consequence concrete:</p>
<ol>
<li><strong>A change to a function body</strong>: the Swift task re-runs and writes a new archive, the header comes out byte for byte identical, and cinterop reports <code>UP-TO-DATE</code>. The klib keeps the old archive, so your app would link yesterday's Swift code.</li>
<li><strong>A new </strong><code><strong>@objc</strong></code><strong> method</strong>: the method appears in the generated header, and cinterop still reports <code>UP-TO-DATE</code>, so it never reaches the Kotlin bindings.</li>
</ol>
<p>With the hash restored, both edits re-run cinterop, and the embedded archive matches the new one. The obvious alternative, declaring the archive as a cinterop input, is what the team tried. The comment above the hash records why it didn't stick:</p>
<p>We add a hash of the Swift source directory to force cinterop to re-run when Swift source changes, even if the changes are non-public. This is necessary because we found using inputs.file() on the cinterop task directly causes failures when building debug after release (e.g. publishToMavenLocal). This seems related to the cinterop commonizer.</p>
<p>The build logic tests pin this behavior with Gradle TestKit. Each test creates a throwaway Swift package, runs cinterop once, changes one thing, and checks the task outcome. In the implementation change test, the package starts with a <code>greet()</code> method that returns <code>&quot;hello&quot;</code>:</p>
<pre><code>// Act
val original = pkg.target.readSourceFile(&quot;TestClass.swift&quot;)
// An implementation change does not modify the header.
pkg.target.writeSourceFile(
    &quot;TestClass.swift&quot;,
    original.replace(&quot;return \&quot;hello\&quot;&quot;, &quot;return \&quot;hello world\&quot;&quot;)
)
val result = runBuild(pkg.cinteropTaskName)

// Assert
assertEquals(TaskOutcome.SUCCESS, result.task(pkg.cinteropTaskName)?.outcome)</code></pre>
<p>Sibling tests cover an edit that only touches a comment, an added file, a deleted file, and the negative case: with no change at all, cinterop must be <code>UP-TO-DATE</code>. That last test matters as much as the others, because a hash that changed on every build would turn the fix into a rebuild tax.</p>
<p>The hash has limits worth knowing if you copy it. It covers only the target's own sources, so a change to a dependency target, to the build configuration, or to compiler defines leaves the <code>.def</code> file as it was and cinterop <code>UP-TO-DATE</code>. Switching the same sources from <code>debug</code> to <code>release</code>, for example, rebuilds <code>libAdditionalSwift.a</code> while the klib keeps the debug copy.</p>
<h2><strong>The facade module: A Gradle module with almost no Kotlin</strong></h2>
<p>With bindings generated, the module that owns them can be published. <code>kn-core</code> has exactly one Kotlin file:</p>
<pre><code>@file:Suppress(&quot;unused&quot;)

package com.revenuecat.purchases.kn.core

/**
 * We need at least 1 Kotlin source file for the kn-core module to be published.
 */
internal object Dummy</code></pre>
<p>The substance is in the published artifacts. For the iOS device target in version 3.10.1, Maven Central has:</p>
<ul>
<li><code><strong>purchases-kmp-kn-core-iosarm64-3.10.1.klib</strong></code>: 3,726 bytes, holding the <code>Dummy</code> object.</li>
<li><code><strong>purchases-kmp-kn-core-iosarm64-3.10.1-cinterop-RevenueCat.klib</strong></code>: a zip of about 14 MB, holding the bindings and a 55 MB <code>libRevenueCat.a</code> under <code>default/targets/ios_arm64/included/</code>.</li>
<li><code><strong>purchases-kmp-kn-core-iosarm64-3.10.1-cinterop-AdditionalSwift.klib</strong></code>: about 60 KB for the local shims.</li>
</ul>
<p>So a Gradle module with one placeholder object is really a delivery vehicle: a Kotlin/Native library whose payload is compiled Swift. Other modules consume it like any project dependency. <code>core</code> and <code>mappings</code> add <code>implementation(projects.knCore)</code> to <code>appleMain</code>, <code>revenuecatui</code> adds <code>knCore</code> and <code>knUi</code> to <code>iosMain</code>, and from there Kotlin code imports <code>com.revenuecat.purchases.kn.core.RCPurchases</code> and calls straight into <code>purchases-ios</code>.</p>
<p>For your app, this is why the Xcode side of the setup disappeared in 3.0.0. The compiled Swift travels inside the klibs and gets linked into your app's framework automatically, so there's no Podfile, no <code>PurchasesHybridCommon</code> package in Xcode, and no second version to keep in sync. Installing the SDK comes down to a Gradle dependency in <code>commonMain</code>, as the <a href="https://www.revenuecat.com/docs/getting-started/installation/kotlin-multiplatform">installation guide</a> shows, and the <a href="https://www.revenuecat.com/blog/engineering/kmp-sdk-3">3.0.0 announcement</a> walks through moving an existing project over.</p>
<h2><strong>Build configuration: Compiled once, in release</strong></h2>
<p>Publishing has a subtler consequence, which starts with how the Swift build configuration is chosen. Leaving out the validation of the property value, the order of checks looks like this:</p>
<pre><code>private fun Project.getSwiftConfiguration(): String {
    val override = findProperty(&quot;swiftConfiguration&quot;)?.toString()
    if (override != null) return override

    val xcodeConfig = providers.environmentVariable(&quot;CONFIGURATION&quot;).orNull
    if (xcodeConfig != null) {
        return when (xcodeConfig.lowercase()) {
            &quot;debug&quot; -&gt; &quot;debug&quot;
            &quot;release&quot; -&gt; &quot;release&quot;
            else -&gt; &quot;release&quot;
        }
    }

    val taskNames = gradle.startParameter.taskNames
    val isPublishing = taskNames.any { it.contains(&quot;publish&quot;, ignoreCase = true) }
    if (isPublishing) return &quot;release&quot;

    return &quot;debug&quot;
}</code></pre>
<p>An explicit <code>-PswiftConfiguration</code> wins, then Xcode's <code>CONFIGURATION</code> variable, where any custom configuration name counts as release, then a check for publishing tasks, and everything else falls back to <code>debug</code> for faster local builds. RevenueCat's release job runs a publishing task with neither override, so the <code>purchases-ios</code> code inside a published artifact is always a release build, no matter how you configure your app. Every <code>#if DEBUG</code> in that Swift code resolves when RevenueCat publishes, not when you build.</p>
<p>That collided with a safeguard in <code>purchases-ios</code>. RevenueCat's <a href="https://www.revenuecat.com/docs/test-and-launch/sandbox/test-store">Test Store</a> lets you run test purchases with a dedicated API key and no App Store Connect or Google Play setup. To keep that key out of production, the iOS SDK deliberately crashes release builds that still use it. With the Swift code always compiled in release, the check would fire during everyday debug runs of a KMP app, so <code>kn-core</code> opts out with a compiler flag:</p>
<pre><code>swiftSettings = SwiftSettings {
    define(&quot;BYPASS_SIMULATED_STORE_RELEASE_CHECK&quot;)
}</code></pre>
<p>The flag exists in <code>purchases-ios</code> for exactly this case, and its comment states the trade off: &quot;Setting this flag means apps shipped to production with a Test Store API key won't be caught at runtime.&quot;</p>
<p>The toolchain is frozen the same way. Version 3.0.1 was compiled with Xcode 26.4.1, and some apps failed to link it, with errors about SwiftUICore (<a href="https://github.com/RevenueCat/purchases-kmp/issues/859">#859</a>), so CI now builds releases with Xcode 16.4. Decisions that a regular Swift app makes on every build, from <code>#if DEBUG</code> to the Xcode version, become decisions made once per release when the Swift code ships pre-compiled.</p>
<h2><strong>Resources: Shipping a Swift bundle through Compose Resources</strong></h2>
<p><code>RevenueCatUI</code> also carries resources: asset catalogs and localized strings that SwiftPM would normally package into a resource bundle. A klib has no place for a SwiftPM bundle, so the plugin borrows a delivery mechanism Kotlin Multiplatform already has. <code>ProcessSwiftResourcesTask</code> sorts the resources by type, running asset catalogs through <code>actool</code> and keeping localized files in their <code>.lproj</code> directories, and the plugin registers the output as a custom Compose Resources directory:</p>
<pre><code>with(resourcesExtension) {
    packageOfResClass = &quot;${dependency.packageName}.resources&quot;
    customDirectory(
        sourceSetName = dependency.sourceSetName,
        directoryProvider = processResourcesTask.map { it.outputDir.get() }
    )
}</code></pre>
<p>On the Swift side, kn-ui compiles RevenueCatUI with define(&quot;COMPOSE_RESOURCES&quot;), which switches the bundle it loads resources from:</p>
<pre><code>static var revenueCatUI: Bundle {
    #if COMPOSE_RESOURCES
    return composeResourcesBundle
    #else
    return module
    #endif
}</code></pre>
<p><code>composeResourcesBundle</code> resolves a path ending in <code>composeResources/com.revenuecat.purchases.kn.ui.resources/files</code>, which is exactly where Compose Resources places files for the package set above. The published <code>kn-ui</code> artifact's resource archive contains that directory, with an <code>Assets.car</code> and 47 <code>.lproj</code> directories.</p>
<h2><strong>Two Swift targets, two Gradle modules: Sharing types across the boundary</strong></h2>
<p><code>RevenueCatUI</code> depends on <code>RevenueCat</code>, and its public API is full of RevenueCat types. A paywall delegate receives an <code>RCPackage</code> when a purchase starts and an <code>RCCustomerInfo</code> when a restore finishes. Each target belongs to a different Gradle module, and the generated header shows what that means for cinterop. Here is an excerpt from <code>RevenueCatUI-Swift.h</code> as Swift 6.3 emits it:</p>
<pre><code>#if __has_feature(objc_modules)
@import Foundation;
@import RevenueCat;
@import UIKit;
#endif

@class RCCustomerInfo;
@class NSError;
@class RCStoreTransaction;</code></pre>
<p>The header imports the <code>RevenueCat</code> module, and each <code>@class</code> line is a forward declaration: it says a class exists without describing its methods or properties. Completing them takes either the <code>RevenueCat</code> module or the full <code>@interface</code> declarations in <code>RevenueCat-Swift.h</code>, and both live in another Gradle module's build directory.</p>
<p>The global registry is how <code>kn-ui</code> finds them. For each name in the target's <code>target_dependencies</code> from SwiftPM, the plugin looks up the owning project and keeps the dependencies owned by other projects. Then it wires each one into the Swift task:</p>
<pre><code>moduleDependencies.forEach { dep -&gt;
    val depTaskName = compileSwiftTaskName(dep.dependency.target, kotlinTarget.konanTarget)
    dependsOn(dep.project.tasks.named(depTaskName))

    val depOutputDir = dep.project.layout.buildDirectory
        .dir(&quot;swift-packages/${dep.dependency.target}/${kotlinTarget.konanTarget.name}&quot;)
    dependencyHeaders.from(depOutputDir.map { it.file(dep.dependency.headerName) })
}</code></pre>
<p>The task now runs after kn-core's Swift task for the same Kotlin/Native target, and RevenueCat-Swift.h becomes one of its inputs. At the end of its action, the task copies each dependency header into its own output directory and lists it first in the module map. The one kn-ui generates:</p>
<pre><code>module RevenueCatUI {
    header &quot;RevenueCat-Swift.h&quot;
    header &quot;RevenueCatUI-Swift.h&quot;
    export *
}</code></pre>
<p>The RevenueCat declarations become part of the <code>RevenueCatUI</code> Clang module, placed before the header whose forward declarations they complete. Separately, the cinterop configuration adds each dependency's output directory to the <code>-I</code> search path, which is what lets the <code>@import RevenueCat</code> line resolve at all.</p>
<p>The code keeps both mechanisms because toolchains disagree about which one they need. The commit that introduced the header merge describes cinterop failing with Xcode 26 and Swift 6.2, where the forward declared types came out as stubs that Kotlin couldn't use. A later commit restored the <code>-I</code> paths because removing them had broken Xcode 16. On Xcode 26.5, only the <code>-I</code> path is essential: without it, cinterop stops at <code>fatal error: module 'RevenueCat' not found</code>, while dropping only the header merge still produces complete bindings. Generated headers change between Swift releases, and carrying both paths keeps cinterop working on either toolchain.</p>
<p>The separate scratch directory per target came out of the same investigation. <code>-Xswiftc</code> flags aren't scoped to the target you ask for. SwiftPM passes them to every swiftc invocation in the build, so when <code>RevenueCatUI</code> builds, the compile of its <code>RevenueCat</code> dependency also writes a header to the path meant for <code>RevenueCatUI-Swift.h</code>. That's harmless only when <code>RevenueCatUI</code> compiles afterwards and writes last. With one scratch directory shared by both modules, each module's build recompiled RevenueCat with its own flags, so simply re-running both builds could leave RevenueCat's header in that file.</p>
<p>Separate scratch directories stopped that, at a measurable price: after a clean build, RevenueCat's 565 object files exist three times for each iOS target, under <code>RevenueCat</code>, <code>AdditionalSwift</code>, and <code>RevenueCatUI</code>. They narrow the leak rather than close it, though. An edit to a RevenueCat function body recompiles only RevenueCat inside <code>kn-ui</code>'s scratch directory, and <code>RevenueCatUI-Swift.h</code> then holds RevenueCat's header until something makes <code>RevenueCatUI</code> compile again.</p>
<p>Sharing types also leaves a visible mark in Kotlin. <code>kn-ui</code>'s cinterop sees the RevenueCat classes in its own module and doesn't depend on <code>kn-core</code>'s bindings, so it generates bindings for them too, and <code>RCPackage</code> exists twice: as <code>com.revenuecat.purchases.kn.core.RCPackage</code> and as <code>com.revenuecat.purchases.kn.ui.RCPackage</code>. The paywall delegate in <code>revenuecatui</code> shows how the Kotlin side copes:</p>
<pre><code>import com.revenuecat.purchases.kn.core.RCPackage
import com.revenuecat.purchases.kn.ui.RCPackage as RCPackageFromKnUi

@Suppress(&quot;CAST_NEVER_SUCCEEDS&quot;)
override fun paywallViewController(
    controller: RCPaywallViewController,
    didStartPurchaseWithPackage: RCPackageFromKnUi,
) {
    listener?.onPurchaseStarted(
        (didStartPurchaseWithPackage as RCPackage).toPackage()
    )
}</code></pre>
<p>The compiler warns that the cast can never succeed, since as far as Kotlin knows, the two classes are unrelated. At runtime, Kotlin/Native checks casts to Objective-C classes with <code>isKindOfClass:</code>, both bindings name the same Objective-C class, and the cast goes through, which lets the existing <code>toPackage()</code> mapping for <code>kn-core</code> types handle the value. The alias, the suppression, and the cast are the cost of the duplicate bindings, paid in a handful of delegate methods.</p>
<h2><strong>What cinterop can't see: Shims around the generated header</strong></h2>
<p>The generated header decides what cinterop can bind, and some things never reach it in a usable form. The SDK covers them with small shims, one in the <code>.def</code> file and the rest in Swift.</p>
<h3><strong>Forcing a binding with a custom declaration</strong></h3>
<p>Store messages are the first case. The Swift API takes a <code>Set&lt;StoreMessageType&gt;</code>, which Objective-C can't express, so <code>purchases-ios</code> adds a separate <code>@objc</code> overload that takes an untyped <code>NSSet</code>:</p>
<pre><code>- (void)showStoreMessagesForTypes:(NSSet * _Nonnull)types completion:(void (^ _Nonnull)(void))completion;</code></pre>
<p>No Objective-C signature mentions the enum. The header still declares RCStoreMessageType, but in a build without any reference to it, cinterop leaves it out: the bindings lose the RCStoreMessageType typealias and all four of its constants, while classes like RCPurchases are unaffected. kn-core's build file supplies the missing reference:</p>
<pre><code>customDeclarations = &quot;&quot;&quot;
    // Force cinterop binding generation for types otherwise not in the public API
    static inline int __forceBindings(
        enum RCStoreMessageType _1
    ) { return 0; }
&quot;&quot;&quot;.trimIndent(),</code></pre>
<p>The generator appends it to the <code>.def</code> file after a <code>---</code> separator, a section the Kotlin docs treat as part of the header, which is why the function is <code>static inline</code>. Nothing calls it. Its only job is to mention <code>RCStoreMessageType</code> in a signature, and with it in place, the <code>mappings</code> module can build the <code>Set&lt;RCStoreMessageType&gt;</code> that Kotlin passes to <code>showStoreMessagesForTypes</code>.</p>
<h3><strong>Kotlin has no #available</strong></h3>
<p>Swift gates newer APIs with <code>#available</code>, roughly what an <code>SDK_INT</code> check does on Android, and Kotlin/Native has no equivalent. The <code>AdditionalSwift</code> package in <code>kn-core/src/swift</code> exists for gaps like this one. It's a small Swift package that depends on <code>purchases-ios</code> by path and exposes whatever Kotlin needs through <code>@objc</code>. Looking at one of its availability checks:</p>
<pre><code>@objc
public class AppleApiAvailability: NSObject {
    @objc
    public func isCodeRedemptionSheetAPIAvailable() -&gt; Bool {
        #if os(tvOS) || os(watchOS) || os(macOS) || targetEnvironment(macCatalyst)
        return false
        #else
        if #available(iOS 14.0, visionOS 1.0, *) {
            return true
        } else {
            return false
        }
        #endif
    }
}</code></pre>
<p>Kotlin calls it like any other binding before it touches the gated API:</p>
<pre><code>internal actual fun IosPurchases.presentCodeRedemptionSheetIfAvailable() {
    if (AppleApiAvailability().isCodeRedemptionSheetAPIAvailable())
        presentCodeRedemptionSheet()
    else Purchases.logHandler.d(
        tag = &quot;Purchases&quot;,
        msg = &quot;`presentCodeRedemptionSheet()` is only available on iOS 14.0 and up.&quot;
    )
}</code></pre>
<h3><strong>Bridging APIs that exist only in Swift</strong></h3>
<p>The same package covers APIs that <code>purchases-ios</code> offers only in Swift. The ad tracking API lives on <code>Purchases.shared.adTracker</code> without an <code>@objc</code> form, so <code>AdTracking.swift</code> wraps each call in an <code>@objc</code> static function. Anything a Kotlin caller needs that the upstream header doesn't express gets a thin wrapper in a package the KMP SDK owns, without adding <code>@objc</code> surface to <code>purchases-ios</code> itself.</p>
<h2><strong>Conclusion</strong></h2>
<p>This approach earns its keep when the code you need ships as a Swift package rather than a pod, and when your consumers shouldn't have to open Xcode to add it. If you build something similar, write every input that decides the output into a file Gradle already tracks, as the source hash does for Swift sources, and before trusting the build, open the artifact you publish and confirm that the archive and bindings you expect are really inside.</p>
<p>A Gradle module doesn't need much Kotlin to be useful. <code>kn-core</code> is a build recipe with an artifact attached, and most of its engineering answers one question: does Gradle know about every file that decides what ends up in the klib? Each file it doesn't know about is a place where a green build can quietly ship yesterday's code.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Money-back guarantee vs. free trial: which should your subscription app run?]]></title>
      <link>https://www.revenuecat.com/blog/growth/money-back-guarantee</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/money-back-guarantee</guid>
      <pubDate>Tue, 29 Sep 2026 12:00:44 GMT</pubDate>
      <dc:creator><![CDATA[Daphne Tideman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Why a pay-upfront guarantee can beat a free trial, how to run one, and when to skip it]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/ef435e7fc356fd9c17553060bc410dd1b07d2c84-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>A few weeks ago, I convinced a client to run an experiment I wasn’t completely sure about.</p>
<p>Instead of the free trial they’d always offered, we asked people to pay upfront, with a 30-day money-back guarantee if they changed their mind. I’ll be honest: I was nervous. Free trials are the safe default, and here I was suggesting we ask strangers to hand over money before they’d even experienced the product.</p>
<p>But the questions it raised were too interesting not to explore: how does the length compare? What kind of signal does it send? And could it actually work better for some apps?</p>
<p>When I mentioned the idea in an app expert group Slack, someone replied: “I just don’t think they replace free trials.” They weren’t wrong, exactly. And that’s the point of this piece.</p>
<p>I’ve discovered that a money-back guarantee won’t magically transform your app. But it is a genuinely powerful free trial alternative — and one that far too few companies are using strategically.</p>
<p>So let’s give it a go, and if you’re not convinced by the end, you can have your 10 minutes back (100% time-back guarantee).</p>
<h2>Money-back guarantees: free trial alternative or passing trend?</h2>
<p>A money-back guarantee lets users pay upfront and claim a full refund (within a set window) if the app isn't right for them. These guarantees haven’t always been used in apps for a simple reason: they’re difficult to offer inside the app itself. But with the rise of <a href="http://revenuecat.com/blog/growth/web-to-app-funnels">web-to-app funnels</a>, they've moved from the SaaS world into subscriptions, and were even highlighted by Nathan Hudson, founder of <a href="https://www.perceptycs.com/">Perceptycs</a>, as one of the <a href="http://revenuecat.com/blog/growth/2025-app-monetization-trends">monetization trends to watch</a> last year.</p>
<p>While running this money-back guarantee experiment, I keep coming back to the same questions:</p>
<ul>
<li>What's the right length?</li>
<li>What works well?</li>
<li>Will it actually make a difference?</li>
<li>Should it run alongside a free or a paid trial?</li>
</ul>
<p>When I talk about this with the app community, there’s a lot of skepticism. Does it really work? Does it meaningfully change conversion or retention? I guess we’ll find out…</p>
<h2>The case for money-back guarantees</h2>
<p>The app industry has long relied on free trials as a way to let people experience an app before committing. But as subscriptions have become more common, users have become increasingly skeptical about signing up, worried they’ll forget to cancel and end up paying for something they no longer want. You can see that hesitation in the data — RevenueCat's <a href="https://www.revenuecat.com/state-of-subscription-apps/">State of Subscription Apps 2026</a> found <a href="https://www.revenuecat.com/blog/growth/post-purchase-screen/">that 55% of three-day trial cancellations occur on day zero</a>, well before users have had a chance to experience the product’s value.</p>
<h3>Cleaner signal for Meta</h3>
<p>As Meta becomes harder to optimize, many of those trial users are effectively window shoppers. The more of them we chase, the more our cost of acquisition and trial conversion rate suffer.</p>
<p>A money-back guarantee flips that dynamic. On platforms like Meta, you optimize for a direct purchase event, teaching the algorithm to find people who are willing to pay rather than people who are simply willing to start a trial. Same spend, cleaner target.</p>
<p>Nathan captured it well when he highlighted the trend: <em>&quot;By pushing a hard paywall with a money-back guarantee, you can optimize your Meta and TikTok campaigns for the direct purchase event, removing the complexity and opacity of dealing with trial conversions.&quot;</em></p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/68436aedc5cbc525106e4c78433c6bbab2ad6f41-760x539.jpg" alt=""/></figure>
<p>There's another advantage, too. When someone takes a free trial and cancels, you've still paid to acquire them. With a money-back guarantee, most customers never claim a refund, so you're only refunding a small subset while keeping the revenue from everyone else. At the same time, the guarantee reduces the fear of committing: customers have paid, but they know they have a safety net if the product isn't right for them.</p>
<p>We saw this with <a href="http://reading.com">Reading.com</a> (owned by Teaching.com), which replaced the free trial on parts of their web-to-app funnel with a 30-day money-back guarantee. Conversion rates fell, but the purchase event gave Meta a much stronger optimization signal. They still use free trials elsewhere, but for these campaigns the guarantee became a powerful way to attract higher-intent users.</p>
<p>As <a href="https://subclub.com/episode/what-readingcom-learned-testing-prices-and-funnels-tim-dikun-teachingcom">Tim Dikun of Teaching.com shared on Sub Club</a>, very few customers actually claimed the refund, partly because the product delivered on its promise. The lower conversion rate was more than offset by attracting users who were more likely to stick around and become long-term subscribers.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/153a957c3c6f1880ffbf1129ac9b515f1c0185d4-604x1670.jpg" alt=""/></figure>
<p>Refund fear is a common reason for not offering a money-back guarantee, but in reality refund rates are often much lower than people expect. I offered a money-back guarantee across a range of courses over five years, and across 100s of sales I had just one refund request. It came from someone who thought the course would focus on something different from what it was actually designed for (and I of course refunded them immediately).</p>
<h2>How to run a guarantee</h2>
<p>Like I said, we don't tend to see money-back guarantees directly inside apps, and the reason is simple: the app stores control the refund process. With Apple, users request refunds directly through Apple, and Apple takes back <a href="https://www.revenuecat.com/blog/growth/does-apple-keep-its-commission/">your post-commission share</a>, not the full price. With Google, users can request a refund directly through Google Play within 48 hours of purchase. After that, the decision is yours, and you can issue refunds yourself from the Play Console or RevenueCat. Either way, the refund comes out of your proceeds.. Neither platform has a native ‘money-back guarantee’ feature, so there's no way to automatically approve one inside the app.</p>
<p>On the web, though, you don't have these limitations. You have full control over refunds through your payment provider, whether that's Stripe or another web billing tool, and the fees are usually lower too. You can also define your own guarantee policy and rules. Some apps offer a 30-day money-back guarantee, while others go as far as 100 days (typically for web purchases and first-time customers only).</p>
<p>That flexibility is slowly making its way in-app through <a href="https://www.revenuecat.com/guides/web-to-app-funnels">app-to-web payment flows</a>. While you can't natively configure a money-back guarantee for in-app purchases, you can use an app-to-web payment flow to achieve the same level of control you have in a web funnel.</p>
<p>In practice, offering a guarantee for in-app purchases isn't a setting you simply switch on. You promise the refund in your paywall and marketing copy, then honor it by issuing refunds directly on Google Play, and on iOS by using <a href="https://www.revenuecat.com/docs/customers/refund-control">Refund Control</a> to tell Apple you'd prefer to grant the refund. Apple still makes the final call.</p>
<p><em>Sidenote: </em>I spotted a few apps on Mobbin promoting in-app money-back guarantees, although I couldn't find the messaging in their App Store listings. I tried reaching out to them but sadly I haven't been able to confirm exactly how, or why, they're implementing those guarantees.</p>
<p><a href="https://www.linkedin.com/in/cristianrotari/">Cristian Rotari</a>, a product growth expert who has designed a <em>lot</em> of subscription paywalls, raised the obvious question here: how do you even run a guarantee on iOS when Apple controls the refund process?</p>
<p>His answer is the one I keep coming back to. You either provide Apple with consumption information to support the refund decision, or you rely on clear terms in your guarantee, something like “use the app at least three times and show that you didn’t get the promised value,” so you have a basis for approving it. It’s less a switch you flip and more a promise you honor case-by-case.</p>
<p>It’s also worth remembering that the app stores already allow users to request refunds, whether or not you offer a guarantee. <strong>The platform mechanisms exist; the difference is that most users don’t know about them.</strong> The guarantee is really about making the promise visible and removing the fear of committing. It’s like telling someone with an avoidant attachment style that they can leave at any time, only for them to suddenly not really fancy going anywhere.</p>
<p>That’s why the most powerful place to use it is usually in your web funnels, especially if you’re running <a href="https://www.revenuecat.com/guides/web-to-app-funnels/paid-ads">paid acquisition</a>. So how do you set one up for success? There are a few key things to consider.</p>
<h2>The implementation playbook</h2>
<h3>1. What do you run it alongside?</h3>
<p>Alongside a free trial, I don’t think you’ll see much lift from adding a money-back guarantee. People already have the chance to try the product for free, so you’re really just layering free on top of free.</p>
<p>It gets far more interesting with the upfront payments we talked about. There’s also a third option: a paid trial. Here, you offer a lower-cost paid trial upfront and then add the guarantee on top. This helps counteract the drop in conversion you often see when asking people to pay before they’ve experienced the full value.</p>
<p>With one client, the uptake of a paid trial is lower than a free trial, but the overall conversion to paid is higher. Adding a money-back guarantee on top, giving users that extra layer of security and trust, pushes it higher still.</p>
<h3>2. What guarantee length should you offer?</h3>
<p>The right length should <a href="https://www.revenuecat.com/blog/growth/7-day-trial-subscription-app/">track time-to-value, in the same way trial length</a> does.</p>
<p>Roughly:</p>
<ul>
<li><strong>7–14 days</strong> for utilities and products with fast, obvious value</li>
<li><strong>Around 30 days</strong> for habit and health apps, where the benefits take a few weeks to become clear</li>
<li><strong>Longer periods</strong> where trust is lower, or the payoff takes more time to appear</li>
</ul>
<p>Jumpspeak, operating in the competitive language-learning space, goes as far as offering a 100-day guarantee:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2956ed626b5107040ba43b0901ca50ddee1e137d-2048x1026.jpg" alt=""/></figure>
<p>Interestingly, the research points in the opposite direction to what you might expect. A <a href="https://www.sciencedirect.com/science/article/abs/pii/S0022435915000822">University of Texas meta-analysis of 21 studies</a> (Janakiraman et al., 2016) found that longer, more generous return windows increased purchases without increasing returns. The explanation is the endowment effect: the longer someone owns something, the less likely they are to give it back.</p>
<p>That research is from ecommerce rather than apps, and money-back-guarantee research is still relatively limited, but it’s a good reason not to make your guarantee unnecessarily restrictive.</p>
<p>I tend to start clients around 30 days (I mostly work with health and wellness apps) because it's a familiar, trusted timeframe, then test longer if refund rates stay low. The downside is that guarantees take time to evaluate — you need to wait until the guarantee period has passed to understand the true impact — so starting with a sensible default helps you learn faster.</p>
<h3>3. Positioning, design and copy</h3>
<p>Let’s talk about how you communicate this money-back guarantee. A lot of apps go for the classic &quot;full refund, cancel anytime&quot;. I think it's important to keep the policy easy to understand, without too many hidden conditions or contradictions. That allows your landing page messaging also to be simple and clear, like MasterClass:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/5ed5b69c3df2df2d2d27b0cdfaff669ac746e8b4-2048x796.jpg" alt=""/></figure>
<p>Their FAQs then spell out exactly what the guarantee covers:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f1123723ad427c3e2bca46fe24a861f579ea5df3-1774x772.jpg" alt=""/></figure>
<p>The best examples I see of money-back guarantees keep the messaging simple and usually will repeat it on the <a href="https://www.revenuecat.com/blog/growth/paywalls-study-guide/">paywall</a> (if in-app), often near the CTA:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/1027e945c12ed1b807c8aa83bdb427ff573f3113-903x1951.jpg" alt=""/></figure>
<p>They’ll often use some type of symbol to represent the guarantee and create an official feeling to it, as <a href="http://monday.com">Monday.com</a> does:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/726432ae99e1b5f9fcd50f856e411dc3376017ea-1074x664.jpg" alt=""/></figure>
<h4>Should everyone be allowed a guarantee?</h4>
<p>Maybe we’ve all got a bit of fear of abandonment when it comes to our customers.</p>
<p>If you're worried about abuse, or you've already tested a money-back guarantee and seen high refund rates from people who never even opened the app, you might tighten the rules. For example, you could ask users to try the app at least three times before they're eligible to claim a refund or if they exceed a certain usage (common with AI tools) you don’t get a refund.</p>
<p>But keep the policy simple and clear. The negative reviews and loss of trust that come from an overly complicated or overly restrictive guarantee can easily outweigh the value of offering one in the first place.</p>
<h3>4. Preparing operations for it</h3>
<p>When you test it, think through your refund logic and the operations around it. Have standard responses ready and a clear process for handling requests.</p>
<p>And yes, you'll still get the occasional person who repeatedly subscribes, claims a refund, and signs up again. They're a bit like the person at the ice cream shop who samples twelve flavors before finally choosing classic strawberry (or not buying an ice cream at all).</p>
<p>I don't think it happens nearly as often as people fear, but those people are out there, so it's worth deciding in advance how you'll handle repeat claims.</p>
<p>A few things make this painless at any volume:</p>
<ol>
<li>Write two or three canned responses so your support team isn't drafting every refund from scratch: one that quickly approves a request, and another that asks a qualifying question first if your guarantee has conditions.</li>
<li>Decide upfront whether you'll automatically approve refunds below a certain amount and only manually review the rest. Fighting a $12 refund often costs more in support time and negative sentiment than simply issuing it.</li>
<li>Finally, keep a simple log of refund claims. That way, the occasional serial refunder becomes a pattern you can spot and manage, rather than a surprise.</li>
</ol>
<p>None of it is particularly complicated, but deciding it before you launch means your support team is prepared, your process is consistent, and customers have a better experience when they do request a refund.</p>
<h3>5. Run a test and measure the results.</h3>
<p>Always run this as an <a href="https://www.revenuecat.com/blog/engineering/price-testing-for-mobile-apps/">A/B test</a>. And when you analyze the results, wait until the full refund window has closed for the last cohort, because <strong>refund requests often come towards the end of the guarantee period.</strong></p>
<p>Treat refund rate as a KPI, but the metric you really care about is net profit. Once you've accounted for refunds and measured the impact on conversion, you can work out which option actually leaves you with more money on the bottom line.</p>
<p>A simple way to think about it: imagine your free trial converts 10 out of every 100 visitors into paying subscribers. Your money-back guarantee might only convert 7. But if just one of those seven claims a refund, you're left with 6 full-price, committed customers — and Meta is optimizing against 7 real purchases instead of a large pool of trial starts.</p>
<p>Whether 6 committed buyers beats 10 trial starts depends on your refund rate, your pricing, and how much stronger the purchase signal makes your acquisition. Which is exactly why you run it as an A/B test and judge it on net profit, not conversion rate alone.</p>
<p>One final caveat: keep an eye on <a href="https://www.revenuecat.com/blog/growth/review-mining-for-subscription-apps/">reviews</a> and customer feedback. If the guarantee is hurting word of mouth or driving an unexpectedly high number of refunds, you may decide it isn't the right fit after all.</p>
<h2>Pitfalls and when not to offer a money-back guarantee</h2>
<p>Just like cilantro or pineapple on pizza, money-back guarantees aren’t for everyone — although Italians would probably argue pineapple on pizza isn’t for anyone (and I’d be inclined to agree with them).</p>
<p>If you're not running any <a href="http://revenuecat.com/blog/growth/web-to-app-funnels">web-to-app</a> or app-to-web payments, and it's worth remembering these still aren't available in every country, I wouldn't fixate on a money-back guarantee right now.</p>
<p>Remember that line from the group chat: &quot;I just don't think they replace free trials&quot;? That was Cristian Rotari. He pushed back on some of the hype, and he’s largely right. As he puts it, a money-back guarantee “can’t sit at the level of a trial or a discounted period; it’s more of a nice-to-have,” — it’s a conversion lever rather than a groundbreaking pricing tactic.</p>
<p>And he had the receipts. Looking through three months of refund tickets at one of the apps he worked with, he found fewer than ten mentions of the guarantee, most from people appealing after a refund had been rejected. Hardly anyone actually uses it, which is exactly why it costs relatively little to offer, and exactly why you shouldn’t expect it to transform your numbers.</p>
<p>There are two more things to weigh. First, <a href="https://www.revenuecat.com/blog/growth/why-you-shouldnt-use-app-to-web/">app-to-web checkout</a> can feel unfamiliar or untrustworthy to some audiences, especially younger, app-native users. A guarantee won’t fix a payment flow they already don’t trust (Cristian makes this point too).</p>
<p>Second, this all depends on external payments, which have only recently become more available through changes like the Digital Markets Act in Europe and the <a href="https://www.revenuecat.com/blog/growth/epic-vs-apple/">Epic v. Apple court ruling</a> in the US, and <a href="https://www.revenuecat.com/app-to-web-purchase-guidelines">still aren't accessible everywhere.</a></p>
<p>To be specific about when to skip it: if your app’s value only becomes clear after weeks of use, asking people to pay upfront means asking for commitment before you’ve earned it. If you’re operating at a low price point where volume is everything, upfront payment may cost you more installs than the cleaner signal is worth. And if you’re not running meaningful paid acquisition, the biggest benefit — a stronger ad signal and potential conversion lift — largely disappears, meaning a free trial may do the job with less friction.</p>
<h2>Should you offer a money-back guarantee?</h2>
<p>So, should you run one? Here’s my honest take: if you’re scaling paid ads through a web or app-to-web funnel, and you have a product that people genuinely stick with, a money-back guarantee is well worth testing. The biggest upside is the signal it gives ad networks, just don’t let yourself believe it will suddenly transform your conversion rate overnight.</p>
<p>If you’re not running paid acquisition, or you’re still figuring out <a href="https://www.revenuecat.com/blog/growth/product-market-fit-subscription-apps/">product-market fit</a>, I’d park it for now. A trial will probably serve you better.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f7508f70d2958983aee0b2d23e632129f7f16e84-813x496.jpg" alt=""/></figure>
<p>The way I think about it is as a risk-reversal ladder:</p>
<ul>
<li>A free trial removes the most risk for the user</li>
<li>A hard paywall removes the least</li>
<li>A money-back guarantee sits in the middle: the user pays, but they have protection if it doesn’t work out</li>
</ul>
<p>It keeps commitment high while still saying: “If this isn’t right for you, you’re safe.”</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b89277b974e97515c08d3a7b56f51b35caaaab2b-770x547.jpg" alt=""/></figure>
<p>So start small: pick one segment, one funnel, and one guarantee window — ideally a generous one — and measure two things: your refund rate and whether your ad signal actually. Then judge it on net profit, not conversion rate.</p>
<p>That’s exactly what I’m doing with my client right now. I’ll report back on what the refund rate really looks like, and whether the window shoppers do turn into buyers. (My money’s cautiously on <em>yes</em>.)</p>
<p>Before you test one, run through five quick checks. Try a money-back guarantee if:</p>
<ul>
<li>You're scaling paid UA</li>
<li>You have web or app-to-web payments live</li>
<li>Your value is clear within the guarantee window</li>
<li>You can handle refunds operationally</li>
<li>Your margin can absorb a realistic refund rate</li>
</ul>
<p>If you can’t tick most of those boxes, a free trial is probably still the better option.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[How to make your paywall iPhone Duo-ready: latest advice]]></title>
      <link>https://www.revenuecat.com/blog/engineering/paywalls-on-iphone-duo</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/paywalls-on-iphone-duo</guid>
      <pubDate>Mon, 28 Sep 2026 17:42:32 GMT</pubDate>
      <dc:creator><![CDATA[Austin Blake]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[What foldables change for your paywall, and how to design paywalls that feel native for 5+ fold angles]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/8479f4d78a98196e5f70a7798d87c11cb483953f-1774x887.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p><em>Last updated: October 24, 2026. This is a living guide — we'll keep updating it as Apple shares more and we ship new RevenueCat Paywalls features for foldables. So bookmark this and check back.</em></p>
<aside class="tip"><strong>TL;DR</strong><p>Build with Xcode 27.1, present your paywall in a sheet, and turn on Use system navigation for your close button (it needs purchases-ios 5.98.0 or later). iOS then puts Close in the vertical side bar on iPhone Duo, right where people expect it. You can submit iPhone Duo-optimized apps today, and starting April 2027 every app you submit will need iPhone Duo screenshots. For more, see “Check your close button” and “Ship it” below.</p></aside>
<p>iPhone Duo gives your app two very different screens to lay out for, plus everything in between. And your paywall is the one screen where something looking ‘a little off’ costs you real money.</p>
<p>A paywall is already one of the hardest screens to get right on a normal iPhone. Now the same paywall has to work on a 5.4-inch outer screen, a 7.6-inch inner screen, sideways, half-folded on a table, and in Split View next to another app. If you present your <code>PaywallView</code> in a <code>.sheet</code> like most of us do today, it'll show up in every one of those states, so we want to make sure that everything still looks intentional.</p>
<p>This guide covers what iPhone Duo is, and what each posture does to your paywall. Plus, what Apple recommends, and exactly how to set up a <a href="https://www.revenuecat.com/feature/paywalls-lp/">RevenueCat paywall</a> so it feels native on Duo starting today.</p>
<h2><strong>What is iPhone Duo?</strong></h2>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/654ea09a3f6f93338687bb616a08b9388f179f6f-1960x1102.jpg" alt="iPhone Duo marketing image"/></figure>
<p>iPhone Duo is Apple's first foldable iPhone. It has a shape like a passport and opens like a book to about the size of an iPad Mini.</p>
<p>iPhone Duo went <a href="https://www.apple.com/newsroom/2026/09/apple-unveils-iphone-duo/">on sale</a> Friday October 23, 2026, running iOS 27.1. You can test it today with the iPhone Duo simulator in <a href="https://developer.apple.com/xcode">Xcode 27.1</a>, and you can already submit iPhone Duo-optimized apps to the App Store.</p>
<p>The simplest way to think about it: closed, it's an iPhone; open, it's much closer to an iPad. Apple's Human Interface Guidelines (HIG) ask you to design for two sizes: compact width on the outside and regular width on the inside.</p>
<h2><strong>Lots of different postures</strong></h2>
<p>This is where things start to get really interesting...</p>
<p>iPhone Duo can be closed, fully open, or angled somewhere in between. Apple's hinge API (<code><a href="https://developer.apple.com/documentation/swiftui/view/onhingechange(isenabled:_:)">onHingeChange</a></code> in SwiftUI, <code><a href="https://developer.apple.com/documentation/uikit/uihingeinteraction">UIHingeInteraction</a></code> in UIKit) reports these as <code>.closed</code>, <code>.partiallyOpen</code>, or <code>.fullyOpen</code>. On top of that come portrait, landscape, and a few ways to set the phone down.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/83c286510c1d4181af36405642f4519d6098fe4c-2222x2222.png" alt="Different postures of iPhone Duo"/><figcaption>Different postures of iPhone Duo</figcaption></figure>
<h2><strong>Check your close button</strong></h2>
<p>RevenueCat Paywalls can now swap your close button for the system Close control. iOS puts it in the vertical side bar on iPhone Duo and in the navigation bar on iPhone and iPad. Select your close button in the paywall editor and turn on <strong>Use system navigation </strong>on the Paywall Editor. It needs <code>purchases-ios</code> 5.98.0 or later.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/31e18723b026eaaf07c7f5d90aecab23ac37116d-824x352.png" alt=""/><figcaption>Use the Use System Navigation checkmark in the Paywall Editor (on by default for Close Paywall action).</figcaption></figure>
<p>Existing buttons keep their designed look until you turn it on. It works the same way for Back buttons in multi-step paywalls. Android, web, and hybrid SDKs show your designed button.</p>
<p><strong>Using </strong><code><strong>presentPaywallIfNeeded</strong></code><strong>?</strong> The SDK provides the navigation bar for you, so once Use system navigation is on, there's nothing else to do.</p>
<p><strong>Presenting </strong><code><strong>PaywallView</strong></code><strong> yourself in a sheet?</strong> The SDK adds Close to your existing navigation bar. If there isn't one, wrap <code>PaywallView</code> in a <code>NavigationStack</code>:</p>
<pre><code class="language-swift">.sheet(isPresented: $showPaywall) {
  NavigationStack {
    PaywallView()
  }
}</code></pre>
<p><strong>Using </strong><code><strong>PaywallViewController</strong></code><strong> in UIKit?</strong> The SDK adds its controls to the presenting navigation controller.</p>
<p><strong>Keeping your designed X?</strong> If you're on an older SDK or a hybrid SDK, or you leave Use system navigation off, your X stays inside the paywall. You can opt the sheet out of the vertical bar instead:</p>
<pre><code class="language-swift">.sheet(isPresented: $showPaywall) {
  if #available(iOS 27.1, *) {
    PaywallView()
      .toolbarVerticalBehavior(.disabled)
  } else {
    PaywallView()
  }
}</code></pre>
<p><strong>Built a custom X into your design?</strong> Any close button you've positioned yourself, like an overlay in your own code, stays exactly where you put it. On the outer display, that can put it right next to the camera or the side bar. Check it in the Duo preview in the paywall editor and in the simulator, and move it if it collides.</p>
<aside class="tip"><strong>One thing to know</strong><p>If you do opt out, remember that the vertical bar is Apple's default. Apple says opting out &quot;works well for sheets with only a single toolbar button,&quot; and asks you to treat it as a stable choice rather than toggling it on and off.</p></aside>
<p>Then build with Xcode 27.1, check the result in the Duo simulator, and use the Duo preview in the paywall editor to tune your layout for the shorter outer screen and the wider inner one.</p>
<h2><strong>Official Apple recommendations for paywalls on iPhone Duo</strong></h2>
<p>Apple published a new HIG page for iPhone Duo and a set of tech talks alongside it. None of it is written specifically for paywalls or StoreKit, but a handful of the general rules map directly onto how a paywall should behave.</p>
<h3><strong>Prioritize small adjustments over big rearrangements</strong></h3>
<p>Apple asks you to &quot;avoid extreme layout changes as people fold the device&quot; and to &quot;maintain the same functionality across device poses&quot;. For a paywall, that means the same packages, prices, trial terms, and call to action whether the phone is open or closed.</p>
<h3><strong>Put Close where people now expect it</strong></h3>
<p>On Duo, toolbars move to a vertical strip on the side. Apple says to reserve the top of that vertical axis for navigation controls like Back or Close. Turn on Use system navigation for your paywall's close button, and RevenueCat swaps it for the system control so it moves with the toolbar. The &quot;Check your close button&quot; section above covers the fix.</p>
<h3><strong>Keep anything tappable off the fold</strong></h3>
<p>When the device is partially folded, iOS slides sheets out of the fold for you, so a paywall presented in a sheet is covered. If you present your paywall full screen, the fold runs right through the middle of it. In that case, Apple suggests an even number of columns so nothing straddles the fold — so two package cards side by side work better than three.</p>
<h3><strong>Know where your sheet will land</strong></h3>
<p>On the inner display, sheets are centered by default. You can move one to the leading or trailing edge with <code>presentationPlacement(_:)</code>. On the outer display, a sheet with a single button can opt out of the vertical bar and stop just short of the camera.</p>
<h2><strong>Adapting your existing paywall for iPhone Duo</strong></h2>
<p>You don't need to wait for anything new to get a good result on Duo. Here's the setup I'd use, built entirely on features that are live in RevenueCat Paywalls right now.</p>
<h3><strong>1. Preview your paywall on iPhone Duo in the paywall editor</strong></h3>
<p>The RevenueCat paywall editor now includes an iPhone Duo preview. You can switch between open and closed, portrait and landscape. The preview matches the Duo's screen shape, safe areas, and camera, and it shows your paywall as it looks in an app built with Xcode 27.1. It's the fastest way to spot problems before you open Xcode.</p>
<h3><strong>2. Build with Xcode 27.1 and open the Duo simulator</strong></h3>
<p>Install Xcode 27.1, choose iPhone Duo in Device Hub, and use the controls at the bottom of the simulator to open, close, rotate, and fold it. Walk your paywall through every posture above before you change anything, so you know what you're actually fixing.</p>
<aside class="tip"><strong>Get the versions exactly right</strong><p>Use Xcode 27.1, and make sure your simulator runs iOS 27.1, not iOS 27. I learned this one while setting up my own test project, so double-check both before you start.</p></aside>
<p>If you present <code>PaywallView</code> in SwiftUI, Xcode Previews can help too. The canvas overrides picker now has a Display group for previewing content on the device's other display.</p>
<h3><strong>3. Present your paywall in a sheet</strong></h3>
<p>A sheet does a lot of Duo work for you. On the inner display, the system centers it by default. When the device is partially folded, the sheet slides away from the fold on its own.</p>
<p>The simplest option is RevenueCat's <code>presentPaywallIfNeeded</code> modifier. It checks the entitlement for you, presents the paywall in a sheet, and dismisses it once the entitlement is active. On iPad, it shows the paywall in a modal that's roughly iPhone-sized.</p>
<p>If you want more control, present <code>PaywallView</code> yourself in a <code>.sheet</code>. Just make sure to check the placement of your close button when you do so (see &quot;Check your close button&quot; above).</p>
<h3><strong>4. Give your content a maximum width</strong></h3>
<p>On the open, inner display, a paywall built for a phone can stretch edge to edge. Set your main content stack to Fill, then give it a Max width so it stops growing on wide screens. Min/max sizing needs <code>purchases-ios</code> 5.93.0 or later; older SDKs ignore the limits. You can also ask the Paywall AI Editor to make your paywall look good on iPhone Duo. For a design walkthrough with starting values, see <a href="https://www.revenuecat.com/blog/company/designing-revenuecat-paywalls-for-both-iphone-duo-displays">Designing RevenueCat Paywalls for both iPhone Duo displays</a>.</p>
<h3><strong>5. Run the checklist</strong></h3>
<p>Before you ship, walk your paywall through every posture and check:</p>
<ul>
<li>Close is reachable and doesn't collide with the camera or the side bar</li>
<li>Close and Back show up as system controls, in the side bar on the closed display</li>
<li>The call to action is visible without scrolling on the closed, landscape screen (or you've made peace with the scroll)</li>
<li>Nothing tappable, and no price or trial term, sits on the fold when partially folded</li>
<li>The inner display in landscape doesn't look stretched</li>
<li>Select a package, fold, and unfold — your selection is still there</li>
<li>It still looks right on a regular iPhone and iPad</li>
</ul>
<h2>Ship it</h2>
<p>You can submit iPhone Duo-optimized apps in App Store Connect today. Starting April 2027, any app you submit will need to include iPhone Duo screenshots, so it's worth capturing your app on both displays now. App Store Connect also has a new preview tool that shows how your screenshots and product page look on iPhone Duo before you submit.</p>
<h2><strong>Why native paywalls help here</strong></h2>
<p>This is the part I'm most excited about: RevenueCat Paywalls render natively on iOS, and <code>PaywallView</code> is a regular SwiftUI view. That's why everything in this guide works with Apple's own tools. You present it in a system sheet, swap its close button for the system one, and let iOS move it out of the fold.</p>
<p>Someone who just unfolded a brand-new phone is going to notice a paywall that wasn't built for it. Make sure yours was.</p>
<h2><strong>What's next</strong></h2>
<p>We're actively working on making RevenueCat Paywalls even better on iPhone Duo and other foldable devices, and this guide will grow as that work ships.</p>
<p><strong>Changelog</strong></p>
<ul>
<li><strong>October 24, 2026: </strong>Added Use system navigation for close and back buttons (<code>purchases-ios</code> 5.98.0) and min/max sizing, and updated for iPhone Duo going on sale.</li>
<li><strong>October 6, 2026: </strong>Updated for Xcode 27.1, App Store submissions opening for iPhone Duo, and the April 2027 screenshot requirement. Added the close button section and the <code>toolbarVerticalBehavior(.disabled)</code> quick fix.</li>
<li><strong>September 28, 2026:</strong> First version. Covers postures, Apple's guidance, the Duo preview in the paywall editor, and presenting your paywall with a native Close button.</li>
</ul>
<h2><strong>Thank you</strong></h2>
<p>Huge thanks to the Paywalls team for all the work they've been putting into foldables. If you've built a Duo-ready paywall with RevenueCat, I'd love to see it! Send it my way on X @austnstuff, or to <a href="mailto:austin.blake@revenuecat.com">austin.blake@revenuecat.com</a>.</p>
<h2><strong>Further reading</strong></h2>
<ul>
<li><a href="https://developer.apple.com/iphone-duo/prepare/">Three steps to make your app shine on iPhone Duo</a></li>
<li><a href="https://developer.apple.com/videos/play/tech-talks/111461/">Prepare your app for iPhone Duo</a></li>
<li><a href="https://developer.apple.com/videos/play/tech-talks/111462/">Raise the bar with iPhone Duo</a></li>
<li><a href="https://developer.apple.com/videos/play/tech-talks/111466/">Design for iPhone Duo</a></li>
<li><a href="https://developer.apple.com/design/human-interface-guidelines/designing-for-iphone-duo">Designing for iPhone Duo</a></li>
<li><a href="https://developer.apple.com/videos/play/tech-talks/111463/">Strike a pose with adaptive layouts on iPhone Duo</a></li>
<li><a href="https://www.revenuecat.com/blog/company/designing-revenuecat-paywalls-for-both-iphone-duo-displays">Designing RevenueCat Paywalls for both iPhone Duo displays</a></li>
<li><a href="https://www.revenuecat.com/docs/tools/paywalls/displaying-paywalls#paywalls-on-iphone-duo">Paywalls on iPhone Duo (RevenueCat docs)</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[How long should your free trial be? Data from 17,000+ apps]]></title>
      <link>https://www.revenuecat.com/blog/growth/free-trial-length</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/free-trial-length</guid>
      <pubDate>Mon, 28 Sep 2026 09:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Margarita Loktionova]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[We analyzed 17,000+ apps to see how free trial length relates to conversion and retention, from weekly subscriptions to annual plans.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/ec62fb8c8826094fefd5b017e9747e974c4403da-3200x1700.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Free trials keep dividing app developers. Give users more time, get paid faster, or skip the trial entirely?</p>
<p>Sure, shorter trials mean earlier payments, faster feedback for paid campaigns, and lower costs of providing free access. That last one matters most if you’re running an AI app, where every free session carries a real, variable cost behind it.</p>
<p>Yet our State of Subscription Apps 2026 report found a 42.5% median conversion rate for trials of 17+ days, versus 25.5% for trials of four days or less. That’s roughly 67% higher.</p>
<p>But do those subscribers keep paying? And does the pattern differ depending on subscription plans?</p>
<p>We analyzed 17,000+ apps to benchmark conversion and renewal across trial durations for weekly, monthly, and annual subscriptions. We also compared AI and non-AI apps and explored differences by category and subscriber region.</p>
<h2>Which trial lengths do high-volume apps choose?</h2>
<p>First things first: we looked at the 100 apps with the most trial starts in each category and plan duration, and checked what trial length each one runs.</p>
<p>Short trials tend to dominate. On weekly plans, 81–100 of those apps use trials of four days or less, depending on category.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b95c2ef046ff518c583822d025e68a5a2bc879b8-3842x2800.png" alt="Weekly subscription trial lengths among each category’s top 100 apps. Trials of four days or less dominate every category, ranging from 81% in Health &amp; Fitness to 100% in Gaming."/></figure>
<p>Monthly and annual plans also lean toward trials of nine days or less, although the mix varies by category.</p>
<p>Education, Health &amp; Fitness, and Travel favor 5–9-day trials, while Productivity and Photo &amp; Video favor trials of four days or less. One possible explanation is how long users need to evaluate the app: some benefits take repeated use to assess, while others are apparent in a single session.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a8b07bcffee52aba8602b501cbe55dc661dcbccf-1921x2400.png" alt="Trial length distribution among each category’s top 100 apps for monthly and annual subscriptions. Trials of 5–9 days lead in most categories, while trials of four days or less are most common in Gaming and Photo &amp; Video."/></figure>
<p>These are apps with the most trial starts, rather than the highest revenue or retention. Their choices show what’s common at scale. But this doesn’t establish which trial length <em>performs </em>best.</p>
<p>That leaves a useful question: how do conversion and renewal compare across those common short trials? And what changes in the less common, longer-trial groups?</p>
<h2>How many trial users reach a second payment?</h2>
<p>Our data actually shows that longer trials are often associated with higher renewal rates, though the pattern varies by subscription plan:</p>
<ul>
<li><strong>Weekly subscriptions:</strong> The share of trial users who paid and renewed once is slightly higher after 5–9-day trials than after trials of four days or less: <strong>14.4% vs. 11.8%</strong>. Evidence for longer trials remains limited.</li>
<li><strong>Monthly subscriptions:</strong> Longer trials are associated with more trial users paying and renewing: <strong>30.6%</strong> after 10–16-day trials vs. <strong>18.5% </strong>after trials of four days or less. The pattern then levels off in the 17–32-day trial group.</li>
<li><strong>Annual subscriptions:</strong> The share of trial users who paid and renewed increases with trial length across all four groups, from <strong>3.5%</strong> after trials of four days or less to<strong> 18.5% </strong>after 17–32-day trials. Though trial duration alone may not explain higher retention.</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/17cf835cbccd288d0ddc8c22c2199f75148050a8-1921x1080.png" alt="Share of trials that convert to paid subscriptions and renew once, by plan and trial length. Monthly plans rise from 18% to around 30%, and annual plans from 3% to 18%. Weekly estimates for trials longer than nine days are based on fewer than 20 apps."/></figure>
<p><em>One caveat before we go further: there is a correlational pattern, and a flattering one. Someone who’s still in your trial on day 25 of a 30-day window is simply more likely to pay than someone who bails on day two. Keep that in mind as the ceilings and floors get more specific below.</em></p>
<h2>Do conversion and renewal favor the same trial length?</h2>
<p>We also looked at conversion and first renewal separately to see whether longer trials coincide with more users paying, more paying subscribers renewing, or both.</p>
<p>For<strong> weekly</strong> subscriptions, the difference between the two common short-trial groups is larger at renewal. Trials of 5–9 days convert 24.3% of trial users, compared with 22.3% for trials of four days or less. Among paying subscribers, 65.9% renew after 5–9-day trials, versus 57.9% after shorter trials.</p>
<p>There are too few qualifying apps offering weekly plans with trials beyond nine days to extend that comparison reliably.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/cb69c0787ba3a034c60784232fe3117dda4980b4-1921x1080.png" alt="Weekly subscription trial conversion and first renewal rates by trial length. Conversion is 22% for trials of four days or less and 24% for 5–9 days; first renewal among paying subscribers is 58% and 66%, respectively."/></figure>
<p>For<strong> monthly</strong> subscriptions, conversion is higher in the 5–9-day trial group than in the shortest-trial group: 45.9% versus 39.6%. The 10–16-day group has similar conversion, at 46.6%, but higher first renewal: 72.0% versus 62.8% of paying subscribers.</p>
<p>The 17–32-day group has the highest first renewal rate, at 77.5%, but lower conversion than either middle group, at 43.7%.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b0aafb8914b89db29588a7d5e78cd7f054b1a227-1921x1080.png" alt="Monthly subscription trial conversion and first renewal rates by trial length. Conversion is 40%, 46%, 47%, and 44% for trials of four days or less, 5–9, 10–16, and 17–32 days, respectively."/></figure>
<p>For <strong>annual</strong> subscriptions, both rates are higher across longer-trial groups. Comparing trials of four days or less with 17–32-day trials, conversion rise from 24% to 44.6%, and first renewal from 18.3% to 47.5%.</p>
<p>Of course, a trial rarely builds a year of loyalty on its own: a longer trial mostly gives annual buyers more time to be sure before a bigger, harder-to-reverse commitment. So the people who convert after that longer look might already be the ones less likely to cancel.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2f2620df57e66936da0b2d994fa4845d79618764-1921x1080.png" alt="Annual subscription trial conversion and first renewal rates by trial length. Conversion is 24%, 33%, 43%, and 45% for trials of four days or less, 5–9, 10–16, and 17–32 days, respectively."/></figure>
<h2>What if you remove the trial altogether?</h2>
<p>Here’s another interesting finding: subscriptions purchased without a trial have lower first-renewal rates on weekly and monthly plans than those that begin with one.</p>
<p>The weekly gap is particularly large: 35.9% of paying subscribers renew without a trial, compared with 57.9% after trials of four days or less and 65.9% after 5–9-day trials.</p>
<p>Annual plans show a different pattern. No-trial renewal is higher than renewal after trials of up to nine days, but lower than after longer trials.</p>
<table>
<thead><tr>
<th><p><strong>Trial length</strong></p></th>
<th><p><strong>Weekly (1st renewal)</strong></p></th>
<th><p><strong>Monthly (1st renewal)</strong></p></th>
<th><p><strong>Annual (1st renewal)</strong></p></th>
</tr></thead>
<tbody>
<tr>
<td><p>No trial</p></td>
<td><p>35.9%</p></td>
<td><p>49.5%</p></td>
<td><p>26.6%</p></td>
</tr>
<tr>
<td><p>Trial of four days or less</p></td>
<td><p>57.9%</p></td>
<td><p>54.2%</p></td>
<td><p>18.3%</p></td>
</tr>
<tr>
<td><p>Trial of 5–9 days</p></td>
<td><p>65.9%</p></td>
<td><p>62.8%</p></td>
<td><p>25.3%</p></td>
</tr>
<tr>
<td><p>Trial of 10–16 days</p></td>
<td><p>Too few apps</p></td>
<td><p>72%</p></td>
<td><p>36.4%</p></td>
</tr>
<tr>
<td><p>Trial of 17–32 days</p></td>
<td><p>Too few apps</p></td>
<td><p>77.5%</p></td>
<td><p>47.5%</p></td>
</tr>
</tbody>
</table>
<p><em>Note: this table measures first renewal among paying subscribers, not all trial users, so these numbers run higher. This comparison a;sp doesn’t tell us how many people purchase when shown a no-trial offer.</em></p>
<p>Lower renewal doesn’t automatically make a no-trial offer less profitable, though. <a href="https://www.revenuecat.com/blog/growth/should-your-app-stop-offering-free-trials">David Vargas’s case study</a> found that removing the trial, alongside changes to pricing and plans, helped one app earn enough per paying customer to support paid acquisition without it.</p>
<h2>How does trial length relate to conversion and renewal in AI apps?</h2>
<p>For AI apps with usage-based inference or generation costs, extra free access needs to justify its expense. Besides, <a href="https://www.revenuecat.com/blog/growth/ai-app-retention-study">higher churn</a> with lower conversions means there's less room to get the length wrong.</p>
<p>The <strong>monthly</strong> results offer a useful comparison.</p>
<p>Trials of 5–9 days and 10–16 days show almost identical conversion, at 38.2% and 38.5%. First renewal is higher in the 10–16-day group, at 64.2% versus 57.4%.</p>
<p>But the 17–32-day group shows no further renewal gain: first renewal is 64.1%, while conversion falls to 31.8%. In this dataset, the longest monthly trials don’t provide a stronger conversion or renewal benchmark than 10–16-day trials.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/2b8f108620e9e08eb0571774cafdb4dab29ddc3f-1921x1800.png" alt="For monthly subscriptions, AI apps have lower trial conversion and first renewal rates than non-AI apps at every trial length. Beyond 16 days, AI trial conversion falls from 39% to 32%, while first renewal stays at 64%."/></figure>
<p>For <strong>annual AI subscriptions</strong>, conversion is 33.8% after 10–16-day trials, compared with 23.3% after 5–9-day trials. First renewal is also higher, at 29.4% versus 19.6%. For <strong>weekly AI subscriptions</strong>, the difference is smaller. Conversion is 23.1% after 5–9-day trials versus 22.3% after trials of four days or less; first renewal is 62.5% versus 57.9%.</p>
<p>Without a trial, first renewal is lower on weekly and monthly AI plans. Annual no-trial renewal is 18.9%, compared with 15.1% after trials of four days or less and 19.6% after 5–9-day trials.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/27ccd923b1205b3e7ed58467fffa32a972b71240-3842x2160.png" alt="First renewal rates for AI subscriptions by trial duration and plan. Weekly rates rise from 33.8% without a trial to 62.5% with 5–9-day trials. Monthly rates rise from 41.3% without a trial to around 64% with 10–32-day trials. Annual rates range from 15.1% to 29.4%."/></figure>
<h2>Does giving users more trial time improve conversion across regions?</h2>
<p>On <strong>annual</strong> plans, North America and Western Europe have higher conversion across progressively longer-trial groups. Asia-Pacific follows that pattern up to 10–16 days, but has lower conversion in the 17–32-day group.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/285979b02bcfc31113442b6f62ecef3f718aa4c6-1921x1080.png" alt="Annual subscription trial conversion by trial length across seven regions. Conversion rises through 10–16 days in every region. At 17–32 days, it increases further in North America, Western Europe, and Latin America but declines elsewhere. North America has the highest rates; India &amp; Southeast Asia has the lowest."/></figure>
<p>On <strong>monthly plans</strong>, conversion in Middle East and Africa, India and Southeast Asia, and Latin America is highest in the 5–9-day group and lower in the 17–32-day group. The largest gap among these regions is in Middle East and Africa: 38.3% versus 27.4%.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/52a2bea226f311b9b509e7dbfa3567d774991874-1921x1080.png" alt="Monthly subscription trial conversion by trial length across seven regions. North America and Asia-Pacific have the highest rates, while India &amp; Southeast Asia has the lowest."/></figure>
<h2>Do the same trial lengths outperform across app categories?</h2>
<p>On monthly plans, longer-trial groups generally have higher renewal rates across app categories.</p>
<p>But they don’t always have higher conversion. In many categories, conversion is highest in the 10–16-day group and lower in the 17–32-day group.</p>
<p>When interpreting these differences, consider two timelines: how quickly users recognize the app’s value, and how often they have a reason to return. The data don’t measure habit formation, but those questions can help you decide what extra trial days should accomplish.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b726329a5c624396c25e04b83cefb11bb604d829-1921x1500.png" alt="Heatmap of monthly subscription trial conversion and first renewal rates across 11 app categories and four trial lengths. Conversion patterns vary by category, while longer trials generally have higher first renewal rates among paying subscribers. Dashed borders mark results based on fewer than 20 apps."/></figure>
<p>Three categories illustrate the differences:</p>
<ul>
<li><strong>Media &amp; Entertainment</strong>: The 10–16-day group leads on both conversion (58.9%) and first renewal (79.4%), with neither rate higher in the 17–32-day group. For streaming apps, the question to explore is whether extra trial days help users discover more content they want to keep watching.</li>
<li><strong>Utilities</strong>: Conversion is highest in the 10–16-day group, at 47.4%, compared with 44.0% in the 17–32-day group. First renewal is highest in the longest group, at 77.9%, versus 55% for trials of four days or less. A user might recognize a tool’s value after one task. The question for a longer trial is whether it gives them opportunities to use it again.</li>
<li><strong>Health &amp; Fitness</strong>: Conversion is higher in the 5–9-day group than in the 10–16-day group: 46.8% versus 40.4%. First renewal is higher across longer-trial groups, from 51.5% for trials of four days or less to 77.1% for 17–32-day trials. If you’re considering more trial time, check whether users keep completing workouts or logging progress during those extra days. That would give you evidence of continued engagement, rather than assuming a longer trial builds a habit.</li>
</ul>
<h2>So, what's the right trial length for your app?</h2>
<p>These findings make a case for challenging a short trial chosen by default. But they also show where longer trials have no clear advantage, helping you narrow down which alternatives deserve a test.</p>
<p>At the end of the day, the answer isn't &quot;run longer (or shorter) trials,&quot; &quot;cut trials entirely,&quot; or a number copied from the leader in your niche. It's whichever pattern matches your app:</p>
<ul>
<li><strong>Time to value</strong>. How long a user needs to experience your app before they can fairly judge whether it's worth paying for.</li>
<li><strong>Habit versus motivation</strong>. Utilities' renewal kept climbing long after conversion had already peaked, because deciding fast and building a routine aren't the same thing. Health &amp; Fitness runs the same tension the other way: a longer trial builds the habit, but it also gives motivation more time to fade first.</li>
<li><strong>Audience context</strong>. Price sensitivity and trust in recurring billing can change what a longer trial buys: more confidence to pay, or just more time to back out. So does whether users are weighing you against a free alternative, and how much they need to learn before they can fairly judge your app at all.</li>
<li><strong>Cost</strong>. Longer trial delays revenue and stretches out how long paid acquisition takes to pay for itself. If your app has a real per-user cost behind every session, that spend adds up whether or not the person converts.</li>
</ul>
<p>The best next action? Use the benchmarks above as a starting point, then test trial length against your own subscription plans, your app's specifics, and what you know about your audience.</p>
<p><a href="https://www.revenuecat.com/feature/experiments">RevenueCat Experiments</a> lets you get there faster. Set trial length as the variable, and let your own revenue numbers make the call.</p>
<h2>How we analyzed the data</h2>
<p>We compared trial conversion and paid renewal separately for weekly, monthly, and annual subscriptions on the App Store and Google Play, with breakdowns by platform, AI classification, category, and subscriber region. We also looked at the 100 apps with the most trial starts in each category.</p>
<ul>
<li><strong>Period:</strong> August 2025–July 2026, based on trial starts for conversion and renewal dates for retention. The combined trial-to-first-renewal measure follows the same trials through both payments, including earlier annual cohorts.</li>
<li><strong>Measurement:</strong> Median app-level rates. Each renewal rate measures subscriptions that paid again, conditional on the previous payment.</li>
<li><strong>Sample requirements:</strong> More than 100 eligible trials or subscriptions reaching first renewal per qualifying app segment. Groups with fewer than 20 apps are directional.</li>
<li><strong>Trial coverage:</strong> No-trial subscriptions appear only in renewal comparisons. Trials longer than 32 days are excluded from conversion figures.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[César Pinto Castillo's $150 Mac could not build iPhone apps]]></title>
      <link>https://www.revenuecat.com/blog/growth/cesar-pinto-castillo-sofia-larsson-ambre-launched-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/cesar-pinto-castillo-sofia-larsson-ambre-launched-podcast-2026</guid>
      <pubDate>Wed, 23 Sep 2026 13:08:51 GMT</pubDate>
      <dc:creator><![CDATA[Charlie Chapman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[César Pinto Castillo built Ambre to test CloudKit and forgot about it; Sofia Larsson, a textile engineer, rewrote it to learn Swift — and it ended up featured on the App Store.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/3299eec284b5d3fca8b397ecd42d2753452269c9-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Ambre is a recipe app built by two people in Gothenburg, Sweden. César Pinto Castillo wrote the first version to test a new Apple framework, put it on the App Store, and forgot about it. Sofia Larsson, who has a bachelor's in textile engineering and no formal software training, rewrote its views to learn SwiftUI and never stopped. On this episode of Launched, Charlie Chapman gets both origin stories, the Apple program that turned a beginner into a co-founder, and a closing argument about what's left to compete on when AI can build the features.</p>
<p><a href="https://www.youtube.com/watch?v=y2xmqABa3EM">Watch on YouTube</a></p>
<h2><strong>The $150 Mac that couldn't build iOS apps</strong></h2>
<p>César had a computer science degree and a job writing C# for an ad agency when he walked past a shop window in Västerås and saw an iPhone in demo mode, scrolling through contacts. He'd been building for a Sony Ericsson Xperia X1 and hated everything about it. &quot;I just saw it and I was like, 'Oh my God, this is amazing.'&quot;</p>
<p>That night, a well-known Swedish comedian posted asking whether someone could make him an app. &quot;And I have no idea how to build an app. I didn't have a Mac. I didn't know what Xcode was. I knew nothing,&quot; César says. Charlie's follow-up: &quot;Well, you didn't even have an iPhone at this point, right? You just saw one?&quot; He hadn't. He wrote back anyway, agreed a price, and went back to the store to ask what he'd need.</p>
<p>They sold him a returned PowerBook G4 for $150. He installed Xcode, opened the sample project, hit build, and discovered that iOS apps needed an Intel Mac to compile. &quot;So I had to spend all the money I was getting paid to build the app to buy my first Mac. So that's how I got into app development.&quot;</p>
<p>The app itself was a blog reader: a WordPress plugin he wrote to expose posts as JSON, pulled into a list. &quot;When there only was one screen size,&quot; as he puts it. The commitment came before the tooling, and the fee paid for the tooling. He's been employed as an iOS engineer since 2011.</p>
<h2><strong>A CloudKit experiment, forgotten, then handed to a beginner</strong></h2>
<p>Around the time CloudKit launched, César built a recipe app to see what the framework could do. Recipes were CloudKit records; you could see other people's. He named it Cooking, shipped it, and moved on. &quot;I put it out there and I forgot about it.&quot;</p>
<p>It kept being used. One user had more than a hundred recipes in it, which mattered because of a detail Sofia raises: you couldn't reorder instructions or ingredients. Get a step wrong and you redid the recipe. Charlie: &quot;This is the classic looking for some app as an excuse to try out a piece of technology.&quot;</p>
<p>Years later, Sofia wanted to learn to code. She'd started with Swift Playgrounds on an iPad, steering a character called Byte through a maze. César had an app lying around that he wanted rebuilt in SwiftUI. &quot;Why don't you just rewrite all the views?&quot; Charlie's verdict: &quot;Easy first task.&quot;</p>
<p>&quot;I started just building up the views and I guess I never stopped because I'm still building the views.&quot; Her biggest obstacle wasn't the logic. &quot;I didn't have any terminology. So when I wanted to Google for something, I had to describe what I wanted.&quot; Even the word <em>modifier</em> wasn't hers yet. Cooking became Ambre, and Ambre started getting featured on the App Store.</p>
<h2><strong>Apple said the call was of utmost importance. It was a two-hour technical interview.</strong></h2>
<p>Sofia's plan was to get good enough to be hired as a developer somewhere. Then César saw a post from Apple's Mike Stern about Apple Entrepreneur Camp and told her to apply.</p>
<p>&quot;I was terrified and César said, 'Oh, well, what's the worst thing that's going to happen?' They say no or they say yes. And I said, 'Yeah, that's the worst thing. They can say yes.'&quot; Because then she'd have to do it.</p>
<p>The email that came back said it was imperative to get a call scheduled that week. She asked César what Apple could want. His guess: a five-minute welcome call, they just want to tell you you're in. &quot;Oh, it turned out it was a technical interview for two hours.&quot;</p>
<p>She got in. The camp ran virtually during the pandemic, two weeks of DTS hours with Apple engineers, design sessions with Apple designers, and pitch training, on a nine-hour time difference. &quot;We literally slept and ate and coded.&quot; What she took from it, she says, is going to sound cheesy: &quot;my confidence, because going in, I felt like such an imposter. I was nervous at every meeting thinking, oh, they're going to call me out.&quot; The environment did the opposite. Her first client project followed, with César, across three time zones.</p>
<h2><strong>Everyone posted code. She posted about color.</strong></h2>
<p>Ambre's marketing, by their own account, has been light: an Instagram account that mostly works as a way for users to reach them. What grew was Sofia's own presence, after César and a contact at Apple all but pushed her into it.</p>
<p>She posted about app development with a tilt toward UI and design, and it found an audience. Her explanation: the community's feed was mostly code — someone found a new API or SDK and tried it out. &quot;Whereas I would like, ooh, this color combination is super nice. And if you put it this way in the layout, it would read much better.&quot; Being one of the few people talking about design in a stream of code posts was the differentiation.</p>
<p>Charlie's own first memory of them is a marketing move too. On the roof of the Apple Park Visitor Center, Sofia handed him a card with a QR code. It opened Ambre as an App Clip, inside which another QR code led to a recipe: how to make Swedish fish. Then she pulled a bag of actual Swedish fish out and handed it over. &quot;I watched you do this to other people after me, and we all had the same reaction.&quot; His conclusion at the time: these are very fun people.</p>
<h2><strong>Does the code really matter anymore?</strong></h2>
<p>The episode ends where the edit opens, on a question that a designer-and-engineer pair are unusually well placed to argue. César's position: &quot;With AI today, we can build so much stuff. The thing that's going to put you apart or your product apart from the competition is the experience you offer.&quot; The feature set won't do it; design is about to matter more, not less.</p>
<p>He borrows an analogy from a Linux figure he'd seen quoted: complaining that you don't know what AI-generated code is doing is like complaining that you don't read the assembly. &quot;What we own is the UX and quality of our app. Our customers are not going to read the code.&quot;</p>
<p>Sofia's version comes from the toolbox. &quot;Procreate is a tool. An iPad is a tool. A pencil is a tool. When you buy watercolor, it's pigment and something that bind the pigment.&quot; AI is the next one, and it opens the door for designers who had the idea in their head but not the means to build it. This year that's been her: &quot;I can talk directly with Claude and see my ideas come to life within minutes, hours, depending on the complexity.&quot;</p>
<p>Charlie pushes back a little — AI-built things feel similar unless someone is making all the small choices — and César closes with the question he wants developers to sit with: &quot;If you make sure QA is awesome, the app looks amazing and it solves a real problem, does the code details really matter?&quot;</p>
<p>Also<a href="https://www.youtube.com/watch?v=y2xmqABa3EM" target="_blank" rel="noopener noreferrer"> in the full episode</a>: what a textile engineer actually studies (geotextiles under airport runways, a thesis on getting a medical bandage to a CE standard), César's detours through blackjack dealing and Volvo factory software, the designers Sofia keeps a running iMessage thread with herself about, and the Ambre update landing with iOS 27.</p>
<h2>Guest links</h2>
<ul>
<li><a href="https://www.linkedin.com/in/sofiachristinalarsson/">Sofia Larsson on LinkedIn</a></li>
<li><a href="https://www.linkedin.com/in/jagcesar/">César Pinto Castillo on LinkedIn</a></li>
<li><a href="https://apps.apple.com/us/app/ambre-recipe-book-organizer/id1102236212">Ambre on the App Store</a></li>
<li><a href="https://ambi.se/">Ambi Studio</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[App Growth Annual 2026: full agenda and speaker lineup ]]></title>
      <link>https://www.revenuecat.com/blog/company/app-growth-annual-agenda-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/app-growth-annual-agenda-2026</guid>
      <pubDate>Wed, 23 Sep 2026 10:20:23 GMT</pubDate>
      <dc:creator><![CDATA[Lorelei Whitman]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[Five keynotes, 36 workshops, and one day worth clearing your meetings for]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/310d6e75ce2e5735af615a421607a4e53d2780d4-1600x850.gif" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>We’re less than a month away from <strong>App Growth Annual</strong>, RevenueCat’s event for the people building, growing, and shaping what’s next in apps. On <strong>October 21</strong>, thousands of app professionals will be gathering in New York and online worldwide for a day packed with candid lessons from some of the biggest names in the App Store.</p>
<p>We’ve been polishing the agenda for months, and it’s finally ready for your eyes: five keynotes and 36 workshops, split across exclusive virtual and in-person content. So whether you’re joining us in NYC or from the comfort of your home office, you’ll be leaving with fresh ideas and the momentum to ship them.</p>
<aside class="tip"><strong>App Growth Annual is just one part of a bigger week</strong><p>The app economy is taking over New York from October 19 for <a href="https://www.appweek.events/" target="_blank" rel="noopener noreferrer">New York App Week</a>: four days of community-led events across the city, culminating in The Shippies on October 20 and App Growth Annual on October 21.</p></aside>
<h2><strong>Keynotes</strong></h2>
<p>Here’s what’s heading up the main stage: five sessions from app leaders about what worked, what didn’t, and what they’d do differently — straight from the people building the apps.</p>
<p>All the keynotes will be available to watch on YouTube after the event. So even if October 21 is back-to-back meetings for you, there’s no need to miss anything. <a href="https://appgrowthannual.com/virtual">Register now</a> to get sent the replays as soon as they drop.</p>
<h3><strong>Confidence engineering: why your app's activation flow is probably too short — Ravi Mehta, ex-CPO, Tinder</strong></h3>
<p>Every mobile growth team obsesses over friction — fewer screens, fewer fields, fewer taps between install and activation. It's conventional wisdom, and it's incomplete. One healthcare app replaced a three-step checkout with a twenty-five-step intake flow and conversion jumped 40%.</p>
<p>At Tinder, Ravi's team did the opposite — stripping onboarding to under two minutes to pull in millions who'd never have finished an eHarmony profile. In this session he'll share a framework for diagnosing your own flow: when to strip friction to the bone, when to add steps that earn trust, and how to spot the 'death zone' where teams split the difference and get the worst of both.</p>
<p><strong>You'll leave with</strong>: a way to audit your own funnel, step-by-step, and a sharper question than &quot;should this be shorter?&quot;</p>
<h3><strong>Breaking the rules on purpose: the contrarian bets that built Babbel — Julie Hansen, CRO &amp; US CEO, Babbel</strong></h3>
<p>Every subscription app has heard the same advice: never sell lifetime deals, don't bother with web checkout, go freemium to keep the funnel full, spend only on acquisition… Well, Babbel has ignored most of the advice — and is still here, 30 million subscriptions later.</p>
<p>As Babbel’s US CEO for nearly a decade, Julie had a hand in most of those bets. She’ll walk through why they made them against the advice of the day, which ones they’d make again, and what they cost Babbel.</p>
<p><strong>You’ll leave with: </strong>the insight to discern which rules in your own playbook are real, and which are just habits the industry hasn’t questioned yet.</p>
<h3><strong>Built to burn: how to turn a high-churn product into good subscription economics — Greg Cohn, Co-founder &amp; CEO, Burner (Ad Hoc Labs)</strong></h3>
<p>Burner is an app for disposable phone numbers, with a button that destroys the very thing you're paying for… yet it has been growing and profitable for almost a decade. In fact, half of Burner’s new subscription revenue on iOS is from users who previously churned.</p>
<p>Greg says they didn’t plan it that way. But along the ride, they learned not all churn is failure — some of it, in fact, is success. His session will tell the story of that success: from designing for profitable churn, to cohort math and monetization architecture.</p>
<p><strong>You’ll leave with: </strong>analytical frameworks and mechanics you can steal — and a sneak preview of the new-to-the-market retention hack Burner’s testing next.</p>
<h3><strong>Speedrunning subscription monetization: what Tolan learned when the AI bill came due — Ajay Mehta, Founder, Tolan</strong></h3>
<p>For a decade, subscription apps ran on one assumption: the marginal cost of a subscriber is roughly zero. AI broke that. Now free users cost real money, and the heaviest users can cost more than they pay.</p>
<p>At Tolan, they learned this lesson in week one, when the OpenAI bill outgrew their userbase and a paywall became a survival mechanism. Since then, the team has run a decade’s worth of monetization plays in double-time. Ajay will recount each of these decisions; what they expected, what actually happened, and how they came to their ultimate monetization rule: would they be happy explaining it on a podcast?</p>
<p><strong>You’ll leave with:</strong> a sense of what the end of zero marginal cost means for your own pricing, whether you're building an AI app today or adding AI to the one you have.</p>
<h3><strong>RevenueCat product update ahead of 2027 — Jacob Eiting, CEO &amp; Co-founder, and Miguel Carranza, CTO &amp; Co-founder, RevenueCat</strong></h3>
<p>We won’t spoil too much about our product keynote, but you can expect a retro on the last 12 months, roadmap reveals, and live product launches. </p>
<p><strong>You’ll leave with: </strong>an insider's look at what's next for RevenueCat.</p>
<h2><strong>In-person workshops (block one)</strong></h2>
<p>One of App Growth Annual’s biggest opportunities is getting the right people in one room, asking the same questions you’ve been wondering about. Whatever your focus is right now, there’s a hands-on session to help, filled with genuinely useful conversations.</p>
<h3><strong>Apple Ads 2026, beyond the basics: what changed, what matters, and how to act on it — Thomas Petit, Growth Advisor</strong></h3>
<p>Apple Ads has changed more in the last two years than in the eight before it. This interactive session skips the 101 and tackles four core questions: Which numbers can you trust? Should you let Apple decide how you buy inventory? How do you test and iterate on creatives? And how much should you really buy?</p>
<p>Each question is unpacked with Thomas’s personal experience, how to use the functionality, and where the numbers can fool you. Expect questions, strong takes, and polite disagreement. Not recommended for ASA beginners.</p>
<p><strong>You’ll leave with: </strong>a varied set of perspectives and focused examples to help inform complex ASA decisions.</p>
<h3><strong>Signal engineering: build your subscription app signal plan — Shumel Lais, Co-founder, Day30</strong></h3>
<p>Want to send better signals to Meta, Google, and TikTok, but your event tracking and measurement setup isn’t ready for it?</p>
<p>Shumel’s workshop will have attendees audit their own subscription funnel from install through renewal, scoring your existing events against speed, volume, intent, commercial value, and measurement reliability. Take part to identify which signals to test first and what you need to fix before you begin.</p>
<p><strong>You’ll leave with: </strong>a signal readiness scorecard and prioritized action plan for your growth and data teams.</p>
<h3><strong>The executive roundtable: networking and problem-solving with fellow leaders at scaled apps — Ron Schneidermann, CEO, Acely</strong></h3>
<p>No agenda, no slides — just candid discussion and problem-solving with a small group of fellow app executives. Ron kicks the session off with lessons from scaling AllTrails to millions of subscribers, then opens the floor to questions and discussions on team building, product decisions, and growth challenges.</p>
<p><strong>You’ll leave with: </strong>fresh perspectives from peers who’ve been there too.</p>
<h3><strong>The viral iteration loop: adapt trending content into high-converting creative — Diego Kafie, CEO &amp; Co-founder, Noise</strong></h3>
<p>Viral content already exists in your niche — the opportunity is turning it into creative that drives installs for your app. In this workshop attendees will search live for what's working on TikTok and Instagram in your category, break down why it's performing, and adapt it into iterations you can test as paid or organic creative.</p>
<p><strong>You’ll leave with: </strong>a shortlist of proven content angles tailored to your app, and a repeatable process for finding and adapting them.</p>
<h3><strong>How AI is changing subscription app monetization — Phil Carter, Founder and CEO, Elemental Growth</strong></h3>
<p>LLM-powered features carry non-trivial costs, but most subscription apps are still approaching monetization as though their gross margins haven't changed. Spend this session reviewing the strategies that the best AI apps are using to adapt: restricting freemium, shortening trials, adding higher-priced AI tiers. Wrap up by benchmarking your own metrics against peer apps and apply proven pricing tools (Van Westendorp, Gabor-Granger, MaxDiff, Conjoint) to your own packaging.</p>
<p><strong>You’ll leave with: </strong>a benchmarking tool, a pricing study template, and playbooks for adapting your monetization to the AI era.</p>
<h3><strong>How to launch and grow your app in Japan and Korea — Hyerin Choe, Customer Success Lead, AB180</strong></h3>
<p>Japan and Korea are two of the highest-spending mobile markets in the world, but most Western apps stall at entry because the playbook that works in the US doesn't translate. Join Hyerin Choe from Korean app growth agency, AB180, to identify where your current growth strategy breaks down and build a localized go-to-market plan.</p>
<p><strong>You’ll leave with: </strong>a market-entry framework covering channels, positioning, and the cultural signals that data alone won't reveal.</p>
<h3><strong>Build a user research system that runs itself — Ekaterina Gamsriegler, Growth Consultant &amp; Director of Growth at DataCamp</strong></h3>
<p>Most apps run user research in bursts, then go dark for months. Ekaterina will walk through how to design an always-on feedback system for your app instead. From choosing the right research inputs (cancellation surveys, in-app prompts, price sensitivity polls), to writing the templates, mapping distribution channels, and building a cadence that feeds insights into your experimentation pipeline without becoming a full-time job.</p>
<p><strong>You’ll leave with: </strong>a research system that’s ready to launch next week.</p>
<h3><strong>Roast my ASO: your App Store listing, audited live — Steve P. Young, Founder, App Masters</strong></h3>
<p>Bring your App Store listing and get it torn apart live. Steve will audit real participants' metadata, screenshots, and keyword strategies against what's actually driving installs in their category — with pre-prepared case studies showing where AI-assisted ASO is outperforming manual approaches.</p>
<p><strong>You’ll leave with: </strong>knowledge of exactly what’s costing you rankings and downloads — and what to fix first.</p>
<h3><strong>Find the moment your app earns the right to ask for money — Olha Yohansen, Independent Growth Consultant</strong></h3>
<p>Has your app earned the user’s trust by the time it asks for money? Olha’s workshop will get you mapping your own onboarding to pinpoint where users first feel “this app gets me” — and check whether that moment lands before or after you ask them to pay. If it's after, you're converting on template momentum, not trust.</p>
<p><strong>You’ll leave with: </strong>a redesigned onboarding sequence that puts the <em>aha! </em>moment where it belongs.</p>
<h2><strong>In-person workshops (block two)</strong></h2>
<p>Our second round of workshops offer 10 hands-on sessions packed with in-the-trenches tactics to grow your app. We know it’s hard to choose, but whichever session you attend, you’re guaranteed to leave with new ideas and experiments to try right away.</p>
<h3><strong>Meta UA in 2026: what still works, what's changed, and how scale changes the game — Gessica Bicego, Fractional CMO, Independent Consultant</strong></h3>
<p>The right Meta campaign structure depends on your spend level, but most teams use the same setup whether they're spending $10K or $500K a month. Gessica’s workshop will group attendees by budget range where you’ll work through how campaign structure, optimisation approach, setup (CBO, Advantage+, W2A, signal engineering), and creative strategy should adapt for your scale.</p>
<p><strong>You’ll leave with: </strong>understanding of which structure fits your current spend and what to change as you grow.</p>
<h3><strong>Google Ads for iOS: how to make it work — Ashley Black, Founder &amp; CEO, Candid Consulting</strong></h3>
<p>Google has been overlooked as a serious iOS acquisition channel, but the platform has changed, and the opportunity is bigger than most teams realize. Join Ashley to evaluate your own app against Google's current campaign types, identify which formats match your funnel and audience, and stress-test whether the setup pain is worth the payoff for your specific scale and category.</p>
<p><strong>You’ll leave with: </strong>a clear yes/no on Google Ads for your app, and a plan for what to test first.</p>
<h3><strong>How your ad creative shapes your LTV — Natalia Drozd, Independent Growth Expert</strong></h3>
<p>The creative that lowers your CPA isn't always the creative that attracts subscribers who stay. Viral hooks and trending formats can flood your funnel with users whose expectations don't match your product — and you won't see the damage until retention data catches up. In Natalia’s workshop, you’ll map the relationship between your creative narratives and the customer segments they attract, then test your assumptions in a simulation using real campaign data.</p>
<p><strong>You’ll leave with: </strong>knowledge of which creative angles to scale, and which are quietly eroding your LTV…</p>
<h3><strong>Hybrid monetization: add new revenue without hurting your subscription — Cristian Rotari, Independent Consultant</strong></h3>
<p>Not every user will subscribe, but that doesn't mean they're worth zero. In this session you’ll map your user segments, identify which audiences are under-monetized, and evaluate which hybrid tactics fit your product: ads, IAPs, credits, affiliates, partnerships.</p>
<p>Finish by building a monetization ladder for your app and running a cannibalization check to confirm new revenue streams are truly incremental, not cannibalizing existing subscriptions.</p>
<p><strong>You’ll leave with: </strong>one experiment that’s ready to test in the next 30 days.</p>
<h3><strong>Design habit mechanics that don't fade after week one — Yves Benchimol, CEO &amp; Co-founder, WeWard</strong></h3>
<p>Gamification mechanics lose their impact the moment novelty wears off. Most teams respond by layering on more features, rather than questioning whether the mechanic should exist at all. Join Yves to apply a motivation-first framework to your own product and identify what's actually driving repeated use. You’ll then evaluate which mechanics are earning their place, and find where simplifying or removing features would improve retention more than adding new ones.</p>
<p><strong>You’ll leave with: </strong>a clear map of which habit mechanics to keep, kill, or redesign.</p>
<h3><strong>Ship faster with AI without learning slower — Ethan Garr, Growth Advisor, Breakout Growth Labs</strong></h3>
<p>AI has collapsed the time between idea and shipped feature, but most teams haven't upgraded their learning loops to match. You're deploying faster, but are you discovering what users value faster, or just building confidently in the wrong direction?</p>
<p>Ethan will walk through how to map your current growth initiatives across a Velocity vs. Learning Matrix, to identify where AI is genuinely accelerating progress, and where speed is masking the absence of real customer signals.</p>
<p><strong>You’ll leave with: </strong>a prioritized view of where to let AI run and where human judgment is key to helping more users reach experiences they can’t live without.</p>
<h3><strong>Diagnose your biggest revenue bottleneck — Alice Muir Kocourková, Independent Growth and Subscription Consultant</strong></h3>
<p>You've spent the last quarter optimizing something, but was it the right thing? Most teams pour effort into onboarding when the real opportunity is renewal, or obsess over trial conversion when Month 3 churn is quietly draining more revenue.</p>
<p>Join Alice to score each stage of your subscription lifecycle against industry benchmarks, identify where the gap between your app and the norm is largest, and prioritize fixes using an Impact vs. Effort framework.</p>
<p><strong>You’ll leave with: </strong>your bottleneck diagnosed, and a 90-day plan to close it completely.</p>
<h3><strong>Segmentation as a growth engine: how to scale across segments without breaking efficiency — Bohdan Lopatiy, Product Director - Growth, Flo</strong></h3>
<p>Segment smarter, not thinner. Too many segments mean too many tests nobody can read.</p>
<p>Bring your own user base and find the splits that actually matter.</p>
<p><strong>You’ll leave with: </strong>a segmentation model that helps you scale, rather than holding you back.</p>
<h3><strong>From investment to exit: tactics for raising capital and selling your business — Eric Crowley, Managing Director, Houlihan Lokey</strong></h3>
<p>Whether you're preparing for a fundraise or positioning for acquisition, the gap between ‘growing nicely’ and ‘investor-ready’ is usually a handful of specific metrics and positioning decisions. Eric’s workshop will offer a chance to benchmark your business against what buyers and investors are <em>actually</em> looking for right now, identify gaps in your financial story, and pressure-test your positioning with the room.</p>
<p><strong>You’ll leave with: </strong>readiness to navigate your next round or exit conversation with confidence.</p>
<h3><strong>Roast my web funnel — Nathan Hudson, Founder, Perceptycs / Swoopy</strong></h3>
<p>Your web funnel follows the playbook. But the playbook doesn't account for your category, your traffic, or the assumptions baked into every ‘best practice’ you copied.</p>
<p>Come along to Nathan’s web funnel roast and put your funnel on screen to get it torn apart from ad to checkout: what's cargo-culted, what's actually earning its place, and which experiments carry hidden risk.</p>
<p><strong>You’ll leave with:</strong> prioritized test hypotheses and the context for why the obvious fix might not be yours.</p>
<h2><strong>In-person workshops (block three)</strong></h2>
<p>Wrapping up the day, our final block of workshops are definitely last but <em>not </em>least. Expect intimate conversations, practical takeaways, and new playbooks to take home.</p>
<h3><strong>Affiliate marketing: when it works and how to start — David Vargas, App Growth Consultant</strong></h3>
<p>Most apps treat affiliate marketing as an afterthought or assume it only works for e-commerce. But at scale, it can deliver high-intent users at a fraction of your Meta CPAs — if you pick the right partners and structure the right payouts.</p>
<p>David’s workshop will give you the chance to assess whether your app has the economics and category fit for affiliate marketing, map your ideal partner types, and design a commission structure that protects your margins.</p>
<p><strong>You’ll leave with: </strong>a go/no-go decision and a 30-day launch plan.</p>
<h3><strong>Build an organic-to-paid acquisition engine — Shane Troy, Co-founder &amp; Partner, Cousin Labs</strong></h3>
<p>Organic and paid teams rarely drive the same outcome, and the translation layer between them is where most growth teams leave the biggest gains on the table. In this session, you’ll map the exact loop that turns organic content into scalable paid creative: hypothesis design, creator selection and coaching, asset tagging, feedback loops, and the velocity payoff when winning formats feed directly into paid briefing.</p>
<p><strong>You’ll leave with: </strong>a playbook to connect your organic and paid programs immediately.</p>
<h3><strong>Your creative pipeline is broken and more AI won't fix it — Elise Zareie, Head of Paid Social, Lingokids</strong></h3>
<p>AI handles analysis, iteration, and automation well, but when it's producing your actual ad creative, you often get volume without differentiation, or content that erodes trust with your audience. Join Elise’s session to audit your current pipeline and separate where AI earns its place, versus where it’s producing sameness. Finish by designing the right mix for your vertical: what stays AI, what needs human creators, and how to build that alternative without blowing your budget.</p>
<p><strong>You’ll leave with:</strong> a creative production map tailored to your app and audience.</p>
<h3><strong>Ad monetization playbook for subscription apps — Felix Braberg, Ad Monetization, 2.5 Gamers</strong></h3>
<p>Your app is sitting on a monetization layer it's probably not using (or not using well). Ad revenue data reveals what each user segment is actually worth and whether they should be subscribing or staying on a free, ad-supported tier. This workshop covers how to read impression data as a willingness-to-pay signal, segment users accordingly, and set differentiated pricing.</p>
<p><strong>You’ll leave with: </strong>a segmentation framework and a step-by-step tracking setup guide.</p>
<h3><strong>Make your app a habit: the lifecycle messaging that keeps people coming back — George Deglin, Founder &amp; CEO, OneSignal</strong></h3>
<p>How do you make your app part of your customers' routine? Join this workshop to identify a behavior worth repeating, map where users drop off before they get there, and work out which of those moments can actually be fixed with messaging — and which are product problems a message won't solve. Wrap up by drafting a three-touchpoint journey with the audience, timing and rules behind it.</p>
<p><strong>You’ll leave with: </strong>a brief you can hand to your team, from draft messages to behavioral rules and a measurement plan.</p>
<h3><strong>From 1 to 100 A/B tests per month: a practical framework for scaling experimentation — Olga Berezovsky, Head of Data &amp; Analytics, Data Analysis Journal</strong></h3>
<p>Running more A/B tests doesn’t necessarily mean learning faster. As experimentation programs grow, tests often compete for the same traffic, run for months, and produce results that teams don’t trust. In this workshop, you’ll learn how to decide which ideas deserve a fully powered A/B test, which should be evaluated using faster methods, and which aren’t worth testing. You’ll then spend time scoring your current test portfolio by potential business value, identifying where rigor is slowing down learning, and redesigning your pipeline to complete more meaningful experiments.</p>
<p><strong>You’ll leave with: </strong>a prioritized experimentation roadmap sized to your actual traffic and resources.</p>
<h3><strong>Your AI marketing team: what it can run, what it can't, and what to do about the rest — Jessica Gotti, Head of Performance Marketing, Bend</strong></h3>
<p>One marketer with AI can now do the work of a small team. This session breaks marketing into single functions and asks one question for each: can AI run it alone, does it just help, or is it still a person's job? Each answer comes from practice — real projects where AI took over, helped, or failed.</p>
<p><strong>You’ll leave with: </strong>a map of the AI marketing team and real examples to make it work.</p>
<h3><strong>$1M to $10M ARR: the founder-to-CEO mistakes that stall your next stage — Josh Peleg, Vice President M&amp;A and Business Development, BlueThrone</strong></h3>
<p>The skills that got you to $1M ARR won't get you to $10M, but they’ll get you to this workshop and that’s pretty close. Join M&amp;A guru Josh to audit your own setup and mindset across four dimensions — growth, product, monetization, and team – and identify which founder habits are now bottlenecks. Bring your real growing pains; the room will workshop them together.</p>
<p><strong>You’ll leave with: </strong>knowledge of the three changes needed to unlock your next stage.</p>
<h3><strong>Fix your activation before it kills your retention — Daphne Tideman, Growth Advisor &amp; Consultant, Growth Waves</strong></h3>
<p>Your activation metric is probably measuring activity, not predicting revenue — which means every onboarding test you've shipped this year optimised for the wrong thing. Daphne’s session will help diagnose whether your drop-off is a trust, context, or value problem, before you’ll define the one user action that actually predicts LTV, and pressure-test it with the room.</p>
<p><strong>You’ll leave with: </strong>a validated activation hypothesis and one test to ship next week.</p>
<h3><strong>Hyper-personalization in app onboarding and monetization — Thomas Cotter, Principal, Elemental Growth</strong></h3>
<p>Many apps invest heavily in personalizing onboarding and monetization without seeing meaningful ROI. In this workshop, you’ll learn how to make personalization actually work: what information to collect, how to identify and prioritize opportunities, where and how to experiment, and what to measure. Thomas will walk through real examples, then put attendee apps on screen for live personalization teardowns and test ideas.</p>
<p><strong>You’ll leave with: </strong>a shortlist of personalization tests to run, prioritized by likely impact.</p>
<h2><strong>Virtual workshops</strong></h2>
<p>You didn’t think we’d neglect those watching from home? Our online attendees have six insightful sessions to choose from, but even better — unlike the in-person workshops, these will be recorded and shared to watch back after the day. So no need to panic if you can’t decide which one to tune in to!</p>
<h3><strong>Make AI-generated ads that actually convert — Julien Daver, Head of User Acquisition at SplitMetrics Growth Services</strong></h3>
<p>Generic AI creative tools promise to fix the production bottleneck of creative production every UA team is battling — but mostly, the output tanks your ROAS, or gets rejected by the network outright. For this session, Julien shares how his team actually integrates AI into their creative workflow, with real campaign data on what worked and what didn't.</p>
<p><strong>You’ll leave with: </strong>a production framework built for mobile performance constraints, rather than a list of prompts.</p>
<h3><strong>From competitor ad to finished creative, in one hour — Sven Jürgens, Mobile App Growth Consultant, Sven Juergens Consulting</strong></h3>
<p>Your competitors have already paid to find out what works in your category, and the results are sitting in a public database anyone can open. What are you waiting for?</p>
<p>Sven will share the whole process live: pulling a competitor's longest-running ads from the Meta Ad Library, lining up the first three seconds of each one until the pattern is obvious, then writing the brief and building the creative that answers it. Copying their ad might get you a worse version of theirs; but answering the pattern gets you yours.</p>
<p><strong>You’ll leave with: </strong>a one-page checklist of the nine steps to run this process on your own category the next day.</p>
<h3><strong>6+1 AI workflows to scale Meta from your phone — Marcus Burke, Meta Ads &amp; App Growth Advisor, Ctrl Shift</strong></h3>
<p>With AI, small and midsized teams can get much of the operating leverage that used to require a fully staffed marketing and growth team. In Marcus’s workshop, he’ll walk through the workflows used across real clients, all drawing on a shared memory that compounds as it learns your account, and how each one makes Meta easier to operate and scale.</p>
<p><strong>You’ll leave with:</strong> real workflows Marcus runs across client accounts.</p>
<h3><strong>Copy that converts: the language-market fit teardown — Lisa Kennelly, Growth and Product Marketing Strategist, Klarna</strong></h3>
<p>AI-written onboarding and paywall copy reads fine but converts flat — because nobody fed it real customer language. In Lisa’s session you’ll learn how to mine App Store reviews, support tickets, and cancellation reasons for the actual phrases, anxieties, and objections your users use, then watch a section of conversion copy rewritten live using what that turns up.</p>
<p><strong>You’ll leave with: </strong>a repeatable method for mining customer language going forward.</p>
<h3><strong>Building apps people want: the playbook behind 23 million organic downloads — Bria Sullivan, CTO &amp; Founder, Honey B Games</strong></h3>
<p>Most app developers build features nobody talks about. In this session, Bria will share the system she uses to decide what to build, and the process of making sure the ideas are compelling enough that users will tell their friends. From researching the market to running surveys, customer interviews, and testing visual directions on real people, this is Bria’s full workflow.</p>
<p><strong>You’ll leave with</strong>: a repeatable process for designing features around what your target users actually want.</p>
<h3>Nothing works until this does: the app marketing sequence — Yuliya Lennox, Genesis Tech</h3>
<p>Most apps don't stall because of bad tactics. They stall because something earlier in the chain was never in place, and every optimization since has been built on top of that gap. This session lays out app marketing as a sequence rather than a timeline: what has to be true before the next thing works. Whether you're pre-launch or years into slow, steady growth, you'll be able to place yourself on it, spot what you skipped, and know what to fix first.</p>
<p><strong>You’ll leave with</strong>: a framework for sustainable growth, and foolproof steps to remove blockers along the way </p>
<h2><strong>Other highlights</strong></h2>
<p>But App Growth Annual isn’t just about keynotes and workshops. Also on the agenda:</p>
<h3><strong>Who wants to be a millionARR?</strong></h3>
<p>The live interactive game show where app growth knowledge meets dramatic tie-breaker questions, phone-a-friend lifelines, and (of course) real buzzers. If you want to take part you’ll have to make it through the audience qualifier round, before beating fellow contestants in different categories to make it to the top of the leaderboard.</p>
<p>With questions covering app trivia, industry knowledge and growth benchmarks, we’d suggest you start polishing up your knowledge now… (<a href="https://www.revenuecat.com/state-of-subscription-apps/">The State of Subscription Apps</a> is good revision material!)</p>
<h3><strong>Exclusive virtual content</strong></h3>
<p>Online attendees get access to all the main stage content, streamed live from New York, but they <em>also </em>get to enjoy a whole hub of virtual-only material. Including: a drop-in lounge, paywall roast, virtual RevenueCat Booth, and 1:1 networking conversations.</p>
<p>Best of all, you can enter <strong>The Meowtrix</strong>, an interactive challenge for your App Growth Annual experience. Gather the most points among attendees by checking off specific side quests to climb the leaderboard and claim your prize.</p>
<h3><strong>The legendary afterparty</strong></h3>
<p>Last year saw Jason Paige (of Pokémon theme fame) bring the energy with a performance of <a href="https://www.youtube.com/watch?v=5rabIf__Ma8">RevenueCat’s official theme song</a>. We then had DJ Jazzy Jeff soundtrack the evening while folks networked, got airbrush tattoos, custom caricatures, and enjoyed plenty of delicious street food.</p>
<p>Will Jason Paige be back this year? Who knows. Will we play the RevenueCat theme song regardless? Probably. Will there be tasty food and free drinks? Of course!</p>
<p>And that’s our full lineup for App Growth Annual. A jam-packed schedule filled with insights, inspiration, and new connections — maybe you’ll even meet your future co-founder.</p>
<section><h3>Don’t miss out on app growth’s biggest event</h3><p>Register for free to watch every main-stage session, take part in online workshops, and participate in the virtual experience.</p><p><a href="http://appgrowthannual.com/virtual">Register for free</a></p></section>
<p>We’ll see you on October 21!</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[‘Not enough usage’: your biggest churn reason is a broken habit loop]]></title>
      <link>https://www.revenuecat.com/blog/growth/not-enough-usage-churn</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/not-enough-usage-churn</guid>
      <pubDate>Tue, 22 Sep 2026 12:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Daphne Tideman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Diagnosing why users really leave (and no, it isn’t price-related)]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/1f5cc2f6f6afd5523589e910b410e4a0186d5e29-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Every January, I do a <a href="https://www.revenuecat.com/blog/growth/how-to-tackle-new-year-subscription-churn/">little subscription cull</a>. Consider it a bit of early app spring cleaning. The stretching app I swore I’d use daily but opened maybe three times? Gone. The language app a friend once recommended, now sitting there with a sad little four-day streak? Bye-bye. The new meditation app I was convinced would finally fix my sleep? Deleted.</p>
<p>None of them were canceled because they were bad or too expensive. They were canceled because, well… I wasn’t using them. Oops.</p>
<p>And it turns out I’m not alone.</p>
<p>According to<a href="https://www.revenuecat.com/state-of-subscription-apps-2025/"> the State of Subscription Apps 2025</a>, ‘not enough usage’ is the number one reason people cancel subscription apps, accounting for <strong>37% of cancellations</strong> — ahead of ‘price’ at 35%. <a href="https://www.revenuecat.com/blog/growth/subscription-app-trends-benchmarks-2026">The 2026 report</a> showed we got a little more price-sensitive, but ‘not enough usage’ is still the second biggest driver of churn;<strong> ranging from 26%–40%</strong>, depending on the category.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/20a8b558437980d366b827d7ce7987f483a7aaed-1758x864.png" alt=""/></figure>
<p>When I covered ‘not enough usage’ in my <a href="https://www.revenuecat.com/blog/growth/subscription-app-churn-reasons-how-to-fix">top five cancellation reasons article</a>, I gave it a short and sweet section. This has bothered me ever since. I haven’t seen a single article or podcast give this mega churn reason the attention it deserves. So here we go!</p>
<p>By the end of this read, you'll be able to tell the difference between a usage problem and a price problem. More importantly, you’ll know what to actually do about each issue.</p>
<aside class="tip"><strong>Before we start</strong><p>This is about voluntary usage churn, not the <a href="https://www.revenuecat.com/blog/growth/google-play-billing-error-churn-how-to-fix">involuntary billing kind</a>. Failed payments and expired cards are a completely different beast with a very different fix.</p></aside>
<h2>How to diagnose price vs. usage churn</h2>
<p>Here's the hard part: you often don’t know for sure why someone actually left. Even when you have a stated cancellation reason (Google Play surfaces these, while the App Store is a little trickier), cancellation surveys are not the reliable source of truth many teams treat them as.</p>
<p><a href="https://www.userintuition.ai/posts/why-your-exit-survey-is-lying-to-you-the-case-for-ai-moderated-churn-interviews/">A SaaS study on 723 participants by User Intuition</a> found that stated exit reasons matched the actual churn driver only 27.4% of the time. Price was the reason 34.2% of churners gave, but it was the real driver in just 11.7% of those cases. Whilst the study wasn’t focused specifically on mobile apps, the lesson still holds: <strong>the reason someone gives when they cancel is often the surface explanation, not the underlying cause</strong>.</p>
<p>“Too expensive” might actually mean “I wasn’t getting enough value.”</p>
<p>“Not using it enough” might actually mean “I stopped believing this would work for me.”</p>
<p>So don't stop at the survey data. Use quantitative signals to understand whether you’re dealing with a price problem, a value perception problem, or a usage problem. Look at things like engagement patterns, feature adoption, time-to-value, and retention cohorts.</p>
<p>Then layer in qualitative research (user interviews, cancellation conversations, and feedback from churned users) to understand the story behind the numbers.</p>
<h3>1. Define your version of ‘enough’ usage</h3>
<p>Before you can solve for usage or even start a lovely little quantitative deep dive, you need to know what ‘enough’ actually looks like for your app. And sometimes ‘enough’ is much less frequent than you expect. Sometimes it varies wildly between user groups.</p>
<p>I worked with a meditation and workshop app where some users opened it once every month or two and still happily paid for an annual subscription. Because the value they got from that single session was high enough to justify the cost.</p>
<p>Meanwhile, other users who opened the same app a few times a week didn’t think it was worth paying for. Same app, but completely different definitions of ‘enough’.</p>
<p>As Dan Layfield of <a href="https://subclub.com/episode/the-subscription-growth-formula-dan-layfield">Subscription Index</a> argued, usage cadence should match how long and <strong>how often the user actually experiences the problem you solve. </strong>A fitness app might need three sessions a week. A meditation app might need daily engagement. A personal finance tool might only need to be opened once or twice a month. <a href="https://www.revenuecat.com/glossary#daily-active-users-dau">Daily active users</a> are often the wrong <a href="https://www.revenuecat.com/blog/growth/activation-metrics/">metric</a> entirely.</p>
<p>This is exactly the point <a href="https://www.revenuecat.com/blog/growth/asya-paloni-welltory-sub-club-podcast">Asya Paloni of Welltory also makes</a>: Duolingo's daily streak and light-guilt mechanics work because the behavior is tiny — opening the app for three to five minutes gives the user an immediate reward. That model does not translate neatly to a behavior-change app where the user has to do something difficult in the real world.</p>
<p>Asya suggests that reminders and human support improve engagement in those contexts, while <a href="https://www.revenuecat.com/blog/growth/gamification-in-apps-complete-guide/">gamification</a> (especially shallow attempts like badges and streaks) often doesn’t improve <em>retention</em>. In some cases, forcing the Duolingo playbook onto a health or finance app can actually make things worse, creating pressure around the wrong behavior.</p>
<p>The classic activation thresholds are useful reference points:</p>
<ul>
<li>Slack found that around 2,000 messages correlated with teams that almost never churned</li>
<li>Facebook identified reaching 7 friends within 10 days.</li>
<li>Twitter/X focused on following 30 accounts.</li>
</ul>
<p>All widely reported. All completely different. All built around the natural cadence of a specific use case. So the question is: what’s yours?</p>
<aside class="tip"><strong>A useful exercise</strong><p>Look at what your long-term subscribers actually do in their first billing cycle. Not what they do once, or the flashy activation event. What behavior do they repeat? That recurring action, at whatever cadence makes sense for your product, is your version of <em>enough</em>.</p></aside>
<p>Then it’s time to start running the analyses that actually matter.</p>
<h3>2. Look at pre-cancellation usage</h3>
<p>Start with the 30 days leading up to cancellation for churned users. Were they still actively using the app? Or had usage already dropped to almost nothing?</p>
<p>If engagement had fallen off a cliff before they canceled, that’s a usage problem. The cancellation was just the final step.</p>
<p>If they were actively using the app right up until the moment they left, then price (or another factor) becomes a much more likely driver.</p>
<p>The key question: did they stop because they stopped seeing value, or did they stop because the price no longer felt justified? <a href="https://www.revenuecat.com/blog/growth/how-to-spot-churn-before-it-happens">This guide on spotting churn before it happens</a> covers the signals to look for in more detail.</p>
<h3>3. Segment churners by usage frequency</h3>
<p>Look at who is saying they left because the app was ‘too expensive’.</p>
<p>Are your heaviest users also citing price as a reason for leaving? If so, you may have a genuine pricing or value perception issue.</p>
<p>But if price complaints are concentrated among your lightest users, you might actually be looking at a usage problem wearing a price mask. They’re not thinking, “This costs too much”. They’re thinking, “I’m not getting enough out of this to keep paying”.</p>
<h3>4. Compare cancellation reasons across price tiers (if applicable)</h3>
<p>If you have multiple pricing tiers, compare churn reasons by plan. If your lowest-priced subscribers are still saying ‘not enough usage’ rather than ‘too expensive’ — especially if their actual usage data is low — the problem probably isn’t the price. The issue is that the product hasn’t become valuable enough in their routine.</p>
<p>Price is often the reason people say when the value equation stops making sense. The job is figuring out whether the problem is the number on the bill or the value on the other side of it.</p>
<h2>Why the discount reflex is the wrong response to churn</h2>
<p>What most teams do when they see ‘not enough usage’ in a cancellation survey is slap a discount on it. But think about it. If someone isn't using the app, why would they care what it costs? The issue usually isn’t that the app is too expensive. It’s that <strong>they’re not getting enough value.</strong> Lowering the price doesn’t fix that.</p>
<p>I saw this play out with a previous client. The cohort that signed up with a 50% discount had a £50 lower <a href="https://www.revenuecat.com/blog/growth/what-is-lifetime-value-ltv-apps">lifetime value (LTV)</a> and higher churn than the full-price sign-ups. Cheaper didn't fix usage, it made things worse.</p>
<p>A discount doesn’t repair any of those four points where a habit can break; it won’t create a trigger that brings someone back, or refresh a reward that stopped feeling valuable. So don’t try to win back usage churners with a price cut. <strong>Find where the habit is breaking, and fix that instead.</strong></p>
<h2>‘Not enough usage’ is actually just a broken habit</h2>
<p>The reason this category gets so little attention is that the label itself is a catch-all. What does ‘not enough usage’ actually mean? And is it just a softer version of ‘it’s too expensive’?</p>
<p>Does it mean that your subscriber:</p>
<ul>
<li>Forgot about your app? (How dare they!)</li>
<li>Used it for a bit and stopped? (But why?)</li>
<li>Lost interest in your app? (Ouch.)</li>
</ul>
<p>I quit my <a href="https://www.revenuecat.com/blog/growth/peloton-retention-takeaways/">Peloton subscription</a> last year. I never received a cancellation survey, but if I had, I would have selected ‘not enough usage’. And that was true; I wasn't using it enough anymore. But the real reason was that I'd fallen out of the habit because it had started to feel repetitive, and I wasn't seeing any difference in my workouts. <strong>‘Not enough usage’ was the symptom —</strong> the actual cause was my habit coming apart.</p>
<p>That word, ‘habit’, is the whole thing. Almost every version of ‘not enough usage’ comes down to:</p>
<ul>
<li>A habit never formed</li>
<li>A habit failed to deliver enough value</li>
<li>A habit quietly fell apart</li>
</ul>
<p>If you want to fix usage, you first need to understand <strong>how a habit actually holds together</strong>.</p>
<p><strong><a href="https://www.revenuecat.com/docs/dashboard-and-metrics/charts/subscription-retention-chart">Nir Eyal's Hook Model</a></strong> is one of the clearest ways to think about habit loops:</p>
<ol>
<li>A trigger (a notification, a time of day, a feeling, or a problem they want to solve) brings someone back to your app</li>
<li>They take an action (use the app)</li>
<li>They receive a reward that feels worth the effort</li>
<li>They invest something (a preference, a history, a streak, personal data) that makes the next cycle easier and increases the cost of leaving.</li>
</ol>
<p>Repeat that loop enough times, and a habit forms. But remove the value from step three, and the loop collapses. You could argue that the reward is the most important part, and technically it is. But I think it goes deeper than that. You can create a short-term dopamine hit; a temporary reward, or a moment of engagement — but if the user doesn’t believe the app is delivering real value, the habit won’t survive. A habit isn’t built on activity alone. It’s built on <strong>the belief that coming back is worth it</strong>.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/917884d4cb165e15448c2582b8c95129172cd5f9-1172x924.png" alt=""/><figcaption>Credit: Nir Eyal’s Hook Model</figcaption></figure>
<p>Usage churn is this loop breaking. And in my experience, it tends to break at one of four predictable points.</p>
<h2>The trigger never fired</h2>
<p>Out of sight, out of mind. This happens right at the start, or with low-frequency apps, where people quietly drift back to whatever they used before because it's easier. This is the “I forgot about it” category.</p>
<p>You download a new recipe app, but you still end up opening Instagram or reaching for a dog-eared cookbook (guilty!). The novelty that made you download it faded before the app earned a place in your day, and once it's off the home screen, it's gone.</p>
<h3>How to fix a trigger that never fired</h3>
<p>Try a well-timed re-engagement sequence (<em>not</em> spray-and-pray notifications). Look at when usage typically drops during the billing cycle for your product, then meet users there with something genuinely useful.</p>
<p>I’ve seen this work directly: for users who had hit our definition of inactive (hadn’t completed a workout in five days), we sent a small number of targeted emails that acknowledged where they were and prompted them to re-engage. It drove a 100%+ lift in people completing a workout within three days, versus the control group who received no emails.</p>
<p>Focus on <strong>relevance </strong>and <strong>timing</strong>, not volume.</p>
<h2>The action never stuck</h2>
<p>Some people get going and still don't stick. The hardest part of a new habit is usually the beginning, when the effort is real, and the results are not visible yet. A five-minute journaling practice or a morning stretch feels like more work than payoff in week one. People start with good intentions, but then they slip once or twice, and never come back. This often starts with <a href="https://www.revenuecat.com/blog/growth/fix-onboarding-funnels">onboarding that's too short</a> to reach the moment where the app clicks.</p>
<p>The other half of sticking is finding a slot in the day for it, making it a part of your routine. My Spanish used to be decent. Then I left Spain after living there for a few months, stopped practicing, and it faded. During a trip to Ecuador, embarrassed by my Spanish and keen to pick it up again, I started using a language app: a lesson in the morning, another while waiting for the boat. But when I got home, I had no natural moment to keep it up, and I drifted. My older sister is the opposite. She practices French every night right before bed. Same time, same trigger, sticks to it religiously.</p>
<p>That is the difference between an action that sticks and one that doesn't: <strong>a trigger that fires on its own, not because of some push notification or email. </strong>Without one, daily becomes ad hoc, ad hoc becomes occasional, occasional becomes embarrassingly-bad Spanish. The user wasn't unmotivated. The action just never anchored to an <a href="https://www.revenuecat.com/blog/growth/solve-app-problems-emotionally/">emotional trigger</a>.</p>
<h3>How to fix it an action that doesn’t stick</h3>
<p>Try these two levers, depending on whether the user never activated or never anchored:</p>
<p>First: get them to the core action faster. The longer someone waits after paying <a href="https://www.revenuecat.com/blog/growth/hard-paywall-activation-journey">before they experience real value</a>, the less likely they are to ever form a habit around the product.</p>
<p>Second, borrow an existing trigger rather than trying to build one from scratch. The “Try it right after your morning coffee&quot; prompt, or the &quot;Try it before you sleep&quot; nudge: that anchor matters as much as the product itself. My sister's French habit holds because it's welded to her bedtime. Give users a way to weld by notifying them at the time they are most likely to take action.</p>
<h2>The reward went flat</h2>
<p>This is the one that most usage analyses miss, and the one I'd argue is entirely missing from most articles. Some users get all the way here. They built the routine, came back for a while, but then stop because the app ran out of reasons to pull them back.</p>
<p>Remember my <a href="https://www.revenuecat.com/blog/growth/peloton-retention-takeaways/">Peloton example</a>? I had the routine, and I'd felt the value, at first. But the workouts stopped surprising me: same format, same structure, nothing new to come back for. A reward that's identical every time stops being a reward; we need variation and excitement (especially this ADHDer).</p>
<p>This tends to be further down the user journey. Variability in rewards is key here; fresh content and visible progress are really powerful — these motivations are the app's responsibility, not the users’.</p>
<h3>How to fix it</h3>
<p>This is where variable rewards and investment earn their keep. The same reward every time gets boring. (Peloton again: same workouts, same format, no surprise, no fresh reason to come back.)</p>
<p>Vary your rewards, show progress, unlock rewards based on that progress; give people something new to come back for. Finch is a great example: offering a rotating daily shop, and seasonal rewards linked to repeated usage and habit-building.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/8fffe9c2312433112666bbc4484cd2307371dbb4-828x1792.jpg" alt=""/></figure>
<p>Also focus on building investment early: the more someone puts into an app — preferences, history, a profile — the more they lose by leaving. It’s the same sunk-cost fallacy that keeps us in relationships longer.</p>
<p>AI tools like <a href="https://www.granola.ai/">Granola</a> or <a href="https://wisprflow.ai/">Wispr Flow</a> are good examples. The more content I build up in them, the more useful they become, and the harder it would be to switch. Building that investment before the first renewal changes the value calculation.</p>
<h2>The value never landed, or never fit</h2>
<p>This one usually surfaces later. The user genuinely tried, but it just didn't feel like it was working. They used a language app for weeks and felt like the main word they learned was ‘apple’ (<em>manzana</em>, in my experience with Duolingo). Value can take longer than people expect, or the outcome the app delivers may be different from what they wanted.</p>
<p>The pattern looks like this: you journal for a month and feel no different, so you stop. You might not cancel immediately because part of you thinks, “maybe I’ll get back into it.” Then renewal comes around and suddenly the decision feels obvious: “I’m not using it enough. Might as well cancel.”</p>
<p>Technically, that’s true. But ‘not enough usage’ was the symptom. The root cause was a value gap.</p>
<p>There's a quieter, less obvious reason worth separating out, because the fix is completely different:<strong> the app works; it just doesn't fit their situation.</strong> A meal-planning app that assumes you cook most nights might be genuinely excellent. But for someone who eats out five times a week, it feels useless or unnecessary.</p>
<p>The value isn’t slow to arrive; rather, the product simply doesn’t match the reality of the person using it. When this happens, no reminder, notification, or re-engagement campaign can fix that (no matter how good they are). And sometimes that’s okay. Some churn isn’t a failure to prevent; it’s the right decision for the user.</p>
<h3>How to fix it</h3>
<p>Surface outcomes and progress, not just features. Most apps show what they do. Few show what the user has actually achieved. A monthly progress summary, a milestone email, a reminder of how far someone has come. Those moments are what shift someone from “I’m not sure this is working” to “actually, look at that.” Take Loom’s monthly recap emails — they literally show you the value in one image:</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/bda4f3c5ce9206b4e9c1592096ad75f632696ec8-1400x994.jpg" alt=""/></figure>
<p>For users who've already dropped the app, your typical <a href="https://www.revenuecat.com/blog/growth/app-reactivation-strategy-how-to/">reactivation playbook</a> applies, though reactivation rates are low by default, which is exactly why catching the value gap earlier matters so much. And if it's a life-fit problem rather than a delivery problem, be honest with yourself: no email can surface value that was never going to exist for that user. <strong>Qualifying the right people beats winning back the wrong ones</strong>.</p>
<aside class="tip"><strong>A practical note on sequence</strong><p>These breaks tend to cluster by stage. The trigger and action problems usually appear early, often in the first billing cycle. A flat reward shows up a little later, once the novelty's gone. The value break can surface at any point, but it tends to be why long-term users eventually drift. If your early numbers are soft, start at the trigger and the action, then work your way around the loop.</p></aside>
<p>Each break sits at a different point on the loop, and each needs a different fix. Not one of them responds to a discount. But before you reach for a fix, you need to know which break you're actually looking at and make sure you aren’t dealing with a price problem.</p>
<h2>How to know which part of the habit loop is broken</h2>
<p>You've established the churn is coming from usage, not price. Now you need to figure out which of the four breaks you’re dealing with. Luckily, each one leaves a different fingerprint on the usage curve.</p>
<p>Pull your churned cohorts and find the moment users actually went inactive (not when they canceled, but when they genuinely stopped coming back). Then look at the shape of the drop-off:</p>
<table>
<thead><tr>
<th><p><strong>Drop off</strong></p></th>
<th><p><strong>What’s broken in the habit loop</strong></p></th>
</tr></thead>
<tbody>
<tr>
<td><p>They barely used the app from the start and disappeared after the first session or two</p></td>
<td><p>The <strong>trigger never fired</strong>: the user never built a reason to return</p></td>
</tr>
<tr>
<td><p>They started, used it a handful of times, then gradually tailed off before it became a routine (often within the first few weeks)</p></td>
<td><p>The <strong>action never stuck</strong>: the behavior was too much effort, too unclear, or didn’t become part of their routine</p></td>
</tr>
<tr>
<td><p>They used it consistently for a while, then engagement slowly declined once the novelty wore off</p></td>
<td><p>The <strong>reward went flat</strong>: the app delivered enough initial value to create a habit, but not enough ongoing value to maintain one.</p></td>
</tr>
<tr>
<td><p>They kept showing up, but the outcome never changed, or usage was always sporadic because their situation didn’t really require frequent use</p></td>
<td><p>The <strong>value never landed</strong>, or the product never truly fit their life</p></td>
</tr>
</tbody>
</table>
<p>The usage curve will tell you where to look, but don’t stop there — to understand why the curve looks the way it does, you need to talk to churned users. The data can show you when the habit broke, but conversations tell you <em>why</em>.</p>
<h3>Talk to churned users (and what to do if you can't)</h3>
<p>The most direct way to find out why a habit broke is to talk to the people who left. It’s the equivalent of asking for directions when you’re lost.</p>
<p>Ask users two things:</p>
<ol>
<li>What did they expect from the app?</li>
<li>What did they actually experience?</li>
</ol>
<p>That gap is almost always where usage broke, and it usually tells you which break it was. Then ask what was going on in their life around the time they drifted, especially if you can see usage drop before they canceled.</p>
<p>I know churned users are harder to reach. I know they don't always reply. But trust me, it's worth it.</p>
<p>If you genuinely can't get to your churned users, shorten the feedback loop instead. <a href="https://subclub.com/episode/finding-product-market-fit-by-unbundling-photoshop-matthieu-rouif-photoroom">Matthieu Rouif of PhotoRoom deliberately launched with a monthly-only plan</a> so users would churn faster and become interviewable sooner. If you're early-stage and your usage understanding is low, that trade-off is worth considering.</p>
<p>Other proxies when you can't reach users directly:</p>
<ul>
<li>Support tickets</li>
<li>App store reviews</li>
<li>More in-depth post-cancellation surveys</li>
</ul>
<p>They might not tell you the exact cause 100% of the time, but they can help you narrow down where to look.</p>
<h2>Fix the right problem</h2>
<p>‘Not enough usage’ can sound vague, but once you dig into the quantitative and qualitative data, it usually isn’t. At its core, it comes down to a habit loop that never fully formed, due to one of four breaks: the trigger, the action, the reward, or the value.</p>
<p>And none of those problems are solved by the classic <a href="https://www.revenuecat.com/blog/growth/win-back-customers-how-to-guide">winback</a> discount. They require a better diagnosis and a better strategy.</p>
<p>Start by understanding what ‘enough usage’ actually looks like for your product, then run the quantitative checks I outlined. From there, work backward: where are users dropping off, and why?</p>
<p>Fixing this mega churn reason is a journey, but it’s worth taking the time to get it right. When an app delivers real value, users become far less sensitive to both price and usage. The value they get in the moment is enough to justify coming back, and enough to justify staying.</p>
<p>Usage churn is harder to fix than a pricing problem, but it’s also the lever almost nobody pulls. And that’s exactly why the opportunity is so wide open.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Seven more ways to win Shipaton]]></title>
      <link>https://www.revenuecat.com/blog/company/seven-more-ways-to-win-shipaton</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/company/seven-more-ways-to-win-shipaton</guid>
      <pubDate>Fri, 18 Sep 2026 12:42:28 GMT</pubDate>
      <dc:creator><![CDATA[Perttu Lähteenlahti]]></dc:creator>
      <category><![CDATA[Company]]></category>
      <description><![CDATA[You may not need another app. One useful integration, growth experiment, or sharper audience could put the app you're already building in contention for another prize.
]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/3aa26c27cbd3199b39e2e3e9aac3bb620ecf08d4-1600x818.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Shipaton has a lot of prizes. Enough that it's easy to pick a headline category, get back to building, and forget to look at the rest. I'd understand if you haven't studied every row of the prize table.</p>
<p>To help you out, I went through the categories again, and collected seven that you should consider targeting during the remaining two weeks. All of these reward work that would make your app stronger anyway: finding a route to market, testing growth, launching to a new market, or getting clearer about your audience.</p>
<p>Some can fit an app you already have in progress, and there’s still time to pivot or even build a new app.</p>
<h2>Launch a web-to-app funnel for the Funnel Vision Award</h2>
<p>If your app is nearly done, the next question is awkward but important: how will anyone find it?</p>
<p>The Funnel Vision Award gives you a reason to answer that now (that plus the $15,000). To enter, launch a live web-to-app funnel with <a href="https://www.revenuecat.com/docs/tools/funnels">RevenueCat Funnels</a> and use Stripe for checkout. Best thing: you most likely don’t need to release a new version of your app at all.</p>
<p>Keep the first version laser-focused. Pick one audience, one promise, and one offer. Send people to the funnel from a post or small ad campaign, then watch where they continue, pay, or drop off. The judges will look at payment volume, funnel design, and conversion. So don’t build a generic landing page that could belong to any app, but instead make the journey feel like the first useful part of your product.</p>
<p>Personally, I’d spent the least amount of time on the visuals and more time making the all the steps serve a purpose. The app that understands why someone clicked has a much better shot at converting them. If web acquisition is new to you, the <a href="https://www.revenuecat.com/guides/web-to-app-funnels">Web-to-App Funnels Handbook</a> covers pretty much everything.</p>
<h2>Take an idea all the way to income with Replit</h2>
<p>The Idea to Income Award offers $15,000 for first place. And it’s exactly what the name suggests: start with an idea, use Replit to build it, publish it, and prove that somebody will pay for it. You’ll need to build the mobile app with Replit, integrate RevenueCat using Replit Agent, and share at least three public posts from different points in the build.</p>
<p>To make the best use of your time, build for iOS, with that you have the biggest chance to get your app approved in time. The scope can be small (and should be). A specialist calculator, a training companion for one sport, or a practical tool for one profession can become a real product faster than an app trying to serve everyone.</p>
<h2>Give your Android app a Galaxy-specific advantage</h2>
<p>Already building for Android? Publishing on the Galaxy Store opens another route into Shipaton — and another market for customers to find you. Galaxy store optimization accounts for 20% of the score, and the store listing itself needs to be polished. A thing you can work on without having to release a new version of your app.</p>
<p>Should you target Samsung products by focusing on their unique selling points, that is also possible. Look at the hardware your app will run on. For example a foldable can turn a recipe app into ingredients on one side and instructions on the other. A planning app can keep the calendar and task details visible together. Small updates you can make to your app to make it more suited for Samsung devices.</p>
<p>You don’t need to use every Samsung-specific feature. Pick the one that makes sense for your app and make it feel intentional.</p>
<p>The prize is three weeks of featured placement across the Galaxy Store’s Apps and Discover tabs. There’s no cash prize here, but for an app that’s ready to convert and retain users, the distribution could bring even more money.</p>
<h2>Run one honest growth experiment with Layers</h2>
<p>You don’t need a huge audience to enter the Growth Loop Award. You need a clear idea of who the app is for, a reason they might care, and an experiment you can learn from. That makes this a good category for a newly launched app, and the winner takes $15,000.</p>
<p>Install the Layers SDK, choose one growth loop , and write down what you expect to happen. Then run it long enough to see a signal. If this sounds foreign to you, Layers has good resources on how to do this, and while going through those you also learn how to market your app, something you should do even if you didn’t target this prize category.</p>
<p>The result you get doesn’t have to be a hockey stick. The judges want to see that you treated growth as a loop: form a hypothesis, run the test, and then decide what comes next.</p>
<h2>Make today’s movement decision easier for Simone Sharice’s audience</h2>
<p>The Simone Sharice Influencer Award (with $20,000 for first place) asks for a personalized wellness companion covering movement, Pilates, recovery, and other day-to-day wellness needs. This does not mean that you need to build an endless workout library and ask users to figure it out. Better to build something personalized; a plan they can actually start.</p>
<p>To give you an example, on a low-energy day, that may be 10 minutes of recovery work. On another, it may be a focused Pilates session. The value is in making the choice feel manageable. So personalization matters, but so does restraint. The brief specifically rewards apps that avoid overload and make the next action obvious.</p>
<p>A strong first version could do three things well: understand today’s context, recommend a suitable session, and learn enough from the result to improve tomorrow’s recommendation.There are multiple Shipaton submissions targeting health and fitness, if yours is one, check if you could submit it for this award.</p>
<h2>Build a nutrition app that doesn’t turn lunch into homework</h2>
<p>Most nutrition apps start with numbers. Abbey’s Kitchen brief starts somewhere more human: help people make meals more satisfying without calorie counting or macro tracking.</p>
<p>Your app could let someone photograph lunch and ask what would make it more filling or enjoyable. It could let customers turn leftovers into a workable meal, or suggest useful additions based on what’s already in the kitchen. Something that is missing completely from current calorie and meal tracking apps. </p>
<p>You can build the core around one simple question: “What can I add?” Answer it well, in the moment when someone is hungry and short on ideas, and you’ve built something that is perfect for Abbey’s Kitchen’s massive audience.</p>
<h2>Let new managers rehearse the conversation they’re avoiding</h2>
<p>New managers are expected to give feedback, hold boundaries, and say no. These are often things you don’t get to practise beforehand. That’s the opening behind the Leadership Heather Influencer Award, which also offers $20,000 for first place.</p>
<p>What you could build is for example a rehearsal room, giving the user a realistic situation, letting them respond by voice or text, and then offer feedback they can use on the next attempt.</p>
<p>Start with a handful of conversations people genuinely dread:</p>
<ul>
<li>Giving direct feedback to a strong performer whose behaviour is negatively affecting the team</li>
<li>Saying no to an unrealistic request from a stakeholder</li>
<li>Setting a boundary with a direct report</li>
</ul>
<p>Build it so that users of the app can get personalized feedback. Did the manager state the issue clearly? Did they bury the message? Did they set a boundary? Did they leave room for the other person to respond? All of these are very easy to codify in such a way that LLM’s can answer and give personalized feedback.</p>
<h3>A quick note on the Influencer Awards</h3>
<p>These categories give you an audience and a problem to solve. They don’t give you permission to use someone else’s identity. Name the Influencer Award in your submission and explain how the app serves that audience. But don’t use the influencer’s name, image, likeness, voice, branding, or logos in the product or marketing without written consent.</p>
<p>You can enter each project in only one Influencer Award. The same project can also compete in other non-Influencer categories when it meets their requirements.</p>
<h2>Make things easy for the judges</h2>
<p>Each award has its own requirements: a live funnel URL, Stripe Project ID, Replit preview URL, public build posts, Galaxy Store listing, verified Layers installation, or an explanation of how you served the chosen audience. It’s crucial that you don’t make judges hunt for that information in your submission.</p>
<p>The rest of your submission matters too. You’ll need a live store listing, a demo video under two minutes, an app icon, the required screenshot, and a free trial or promo code that unlocks paid features for judging. Our <a href="https://www.revenuecat.com/blog/engineering/how-to-submit-your-app-for-shipaton">Shipaton submission guide</a> walks through each of these requirements so take a look at it before you submit.</p>
<p><strong>Shipaton closes on September 30, 2026 at 11:45pm PDT</strong>. Store review can take days, so ship the smallest valuable version now and improve it while the review is ongoing. For guidance on submitting your app for review, see our <a href="https://www.revenuecat.com/blog/company/the-late-submitters-guide-to-getting-through-app-review">Late submitter’s guide</a>.</p>
<p><a href="https://revenuecat-shipaton-2026.devpost.com/rules">Read the full rules and enter Shipaton 2026.</a></p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Custom Purchase Flows and Paywalls with RevenueCat on Android]]></title>
      <link>https://www.revenuecat.com/blog/engineering/custom-flows-android</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/custom-flows-android</guid>
      <pubDate>Fri, 18 Sep 2026 05:45:39 GMT</pubDate>
      <dc:creator><![CDATA[Jaewoong Eum]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[In this article, you'll explore building a custom paywall against the Purchases SDK.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/42a58b25733f00d9a57645e280a2069bd64a5ec5-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>RevenueCat's prebuilt <code>Paywall</code> composable handles the common case in one line, and for most apps that is where the story ends. But two questions come up over and over: how to run your own code when a purchase succeeds or fails, and how to control what Google Play does when a subscriber switches plans. Both answers live in builders, and both are easy to get wrong because the defaults are silent. A subscription upgrade that charges the wrong amount looks exactly like one that charges the right amount until a user complains.</p>
<p>In this article, you'll explore building a custom paywall against the Android <code>Purchases</code> SDK, every option on <code>PurchaseParams.Builder</code>, the five replacement modes that govern plan changes, purchase results in callback and coroutine form, and the customization surfaces on RevenueCat's own paywall UI: <code>PaywallOptions</code>, <code>PaywallListener</code>, <code>PaywallActivityLauncher</code>, and <code>PaywallPurchaseLogic</code>.</p>
<h2><strong>Two decisions, not one</strong></h2>
<p>Before any code, separate two things that get conflated. Who owns the UI and who completes the transaction are independent choices, and the SDK lets you mix them.</p>
<ul>
<li><strong>Your own UI, RevenueCat completing the purchase</strong>: <code>PurchaseParams</code> and <code>Purchases.purchase</code>.</li>
<li><strong>RevenueCat's paywall UI, RevenueCat completing the purchase</strong>: <code>PaywallOptions</code> and <code>PaywallListener</code>.</li>
<li><strong>RevenueCat's paywall UI, your own code completing the purchase</strong>: <code>PaywallOptions</code> and <code>PaywallPurchaseLogic</code>.</li>
<li><strong>Your own UI, your own code completing the purchase</strong>: your own <code>BillingClient</code> calls, followed by <code>syncPurchases</code>.</li>
</ul>
<p>Most people asking how to customize the purchase flow want one of the first two. They both let RevenueCat complete the transaction and differ only on who draws the screen. This article covers those, plus the third, where your own code completes the Google Play transaction. The fourth is a migration topic rather than a customization one and is out of scope here.</p>
<p>You choose who completes the transaction at configuration time:</p>
<pre><code>val configuration = PurchasesConfiguration.Builder(context, apiKey)
    .purchasesAreCompletedBy(PurchasesAreCompletedBy.REVENUECAT)
    .build()

Purchases.configure(configuration)</code></pre>
<p><code>PurchasesAreCompletedBy.REVENUECAT</code> is the default and means the SDK acknowledges verified purchases for you. <code>PurchasesAreCompletedBy.MY_APP</code> means it does not, and that obligation has a deadline covered in the last section. The older <code>observerMode(true)</code> setter still exists but is deprecated in favor of this one.</p>
<h2><strong>Reading an offering into your own UI</strong></h2>
<p>A custom paywall starts by fetching offerings and rendering whatever the dashboard returned. The callback form takes a lambda pair:</p>
<pre><code>Purchases.sharedInstance.getOfferingsWith(
    onError = { error -&gt; _uiState.value = PaywallUiState.Error(error.message) },
    onSuccess = { offerings -&gt;
        val current = offerings.current
        if (current == null) {
            _uiState.value = PaywallUiState.Error(&quot;No current offering&quot;)
        } else {
            _uiState.value = PaywallUiState.Success(current)
        }
    },
)</code></pre>
<p><code>offerings.current</code> is nullable, and an app that assumes otherwise crashes the first time someone forgets to mark an offering as current in the dashboard. From there, <code>offering.availablePackages</code> is the list you render. Each <code>Package</code> carries an <code>identifier</code>, a <code>packageType</code>, and the <code>product</code> itself.</p>
<p>There is one nullable accessor per standard package type, <code>offering.monthly</code>, <code>offering.annual</code>, <code>offering.weekly</code>, <code>offering.lifetime</code>, and so on for each <code>PackageType</code>. Lookup by identifier behaves differently and catches people out:</p>
<pre><code>val monthly: Package? = offering.monthly
val custom: Package = offering.getPackage(&quot;my_custom_package&quot;)</code></pre>
<p><code>getPackage</code> returns a non-null <code>Package</code> and throws <code>NoSuchElementException</code> when the identifier is absent, as does the <code>offering[&quot;my_custom_package&quot;]</code> operator form. Wrap it or use the nullable accessors.</p>
<p>For the price label, read through to the product:</p>
<pre><code>val Package.buttonText: String
    get() = with(product) {
        if (type == ProductType.SUBS) {
            &quot;${price.formatted} for ${period?.value} ${period?.unit?.name?.lowercase()}&quot;
        } else {
            &quot;${price.formatted} one time&quot;
        }
    }</code></pre>
<p><code>price.formatted</code> is the localized string Google Play returned, already carrying the right currency symbol and grouping for the user's storefront. <code>price.amountMicros</code> and <code>price.currencyCode</code> are there when you need to compute something. <code>period</code> is returned only for Google subscriptions, so it is null for one time products and for Amazon.</p>
<h3><strong>Why there is no eligibility check on Android</strong></h3>
<p>On iOS there is an eligibility check you call before showing a trial badge. On Android there is not, and looking for one is a common dead end. <code>getEligibleWinBackOffers</code> and <code>checkTrialOrIntroDiscountEligibility</code> do not exist in the Android SDK. Google Play bakes eligibility into what it returns: an offer the user is not eligible for is not in <code>subscriptionOptions</code> at all.</p>
<p>So you read the offers you were given. Two container types are involved and the naming is close enough to confuse. <code>SubscriptionOptions</code> is the collection of offers on a product, and each <code>SubscriptionOption</code> is one offer made of pricing phases. Accessors on the collection pick an offer, accessors on an option pick a phase:</p>
<pre><code>val options = packageToPurchase.product.subscriptionOptions
val trialOption = options?.freeTrial
val introOption = options?.introOffer
val defaultOption = packageToPurchase.product.defaultOption</code></pre>
<p><code>SubscriptionOptions</code> implements <code>List&lt;SubscriptionOption&gt;</code>, so you can iterate it directly. On an individual option, the phase accessors are <code>freePhase</code>, <code>introPhase</code>, and <code>fullPricePhase</code>. There is no <code>freeTrialPeriod</code> on a Google product. That property exists only on <code>AmazonStoreProduct</code>. A single option can carry both a free phase and an intro phase.</p>
<p>To show what a multi-phase offer costs over time, join the phases:</p>
<pre><code>val pricingText = option.pricingPhases.joinToString(separator = &quot; then &quot;) { phase -&gt;
    &quot;${phase.price.formatted} for ${phase.billingPeriod.iso8601}&quot;
}</code></pre>
<p>You might reach for <code>phase.offerPaymentMode</code> to label each phase instead of formatting the price. It is worth knowing its shape before you do. The type is <code>OfferPaymentMode?</code>, one of <code>FREE_TRIAL</code>, <code>SINGLE_PAYMENT</code>, or <code>DISCOUNTED_RECURRING_PAYMENT</code>, and it is null for any phase that is not <code>FINITE_RECURRING</code>. That includes the full price phase of every recurring subscription and every phase of a prepaid plan. So branch on it with a <code>when</code> that treats null as the ongoing price rather than as an error.</p>
<h2><strong>Making the purchase with </strong><code><strong>PurchaseParams.Builder</strong></code></h2>
<p>Every purchase goes through <code>PurchaseParams</code>. Its builder has exactly three public constructors, and which one you pick decides how much control you have over the offer:</p>
<ol>
<li><code>PurchaseParams.Builder(activity, packageToPurchase)</code> takes a <code>Package</code>.</li>
<li><code>PurchaseParams.Builder(activity, storeProduct)</code> takes a <code>StoreProduct</code>.</li>
<li><code>PurchaseParams.Builder(activity, subscriptionOption)</code> takes a specific <code>SubscriptionOption</code>.</li>
</ol>
<p>The first two do not purchase the base plan. They purchase the product's <code>defaultOption</code>, and the builder KDoc spells out how that is chosen: offers tagged <code>rc-ignore-offer</code> or <code>rc-customer-center</code> are filtered out, then the option with the longest free trial or the cheapest first phase wins, then it falls back to the base plan. That is usually what you want, and it is why most apps never touch <code>SubscriptionOption</code> at all.</p>
<p>Pass a <code>SubscriptionOption</code> when you need to override that choice, for example when your paywall shows two offers side by side and the user picked the one that is not the default:</p>
<pre><code>val params = PurchaseParams.Builder(activity, selectedOption).build()</code></pre>
<p>The simplest possible purchase is the package form with nothing else set:</p>
<pre><code>Purchases.sharedInstance.purchaseWith(
    PurchaseParams.Builder(activity, packageToPurchase).build(),
    onError = { error, userCancelled -&gt;
        if (!userCancelled) showError(error)
    },
    onSuccess = { _, customerInfo -&gt;
        unlockContent(customerInfo)
    },
)</code></pre>
<p>Two things about that snippet need stating outright. First, the <code>onSuccess</code> transaction parameter is typed <code>StoreTransaction?</code>, nullable, unlike the non-null <code>StoreTransaction</code> you get from the <code>PurchaseCallback</code> interface. Writing <code>onSuccess = { transaction, _ -&gt; log(transaction.orderId) }</code> does not compile. Discard it with <code>_</code> or use <code>transaction?.orderId</code>.</p>
<p>Second, <code>userCancelled</code> is not extra information. It is derived, in <code>PurchasesOrchestrator</code>, from a single comparison:</p>
<pre><code>onError(
    error,
    error.code == PurchasesErrorCode.PurchaseCancelledError,
)</code></pre>
<p>So checking the boolean and checking the code are equivalent. Either way, a user tapping outside the Google Play sheet is not an error you should surface. Showing a toast that says &quot;Purchase failed&quot; because someone changed their mind is a common defect in hand rolled paywalls.</p>
<h3><strong>The callback interface</strong></h3>
<p><code>purchaseWith</code> is a Kotlin extension. The interface form takes a <code>PurchaseCallback</code>, which declares <code>onCompleted</code> and inherits <code>onError(error, userCancelled)</code> from <code>PurchaseErrorCallback</code>, so you implement two methods:</p>
<pre><code>Purchases.sharedInstance.purchase(
    purchaseParams = params,
    callback = object : PurchaseCallback {
        override fun onCompleted(storeTransaction: StoreTransaction, customerInfo: CustomerInfo) {
            unlockContent(customerInfo)
        }

        override fun onError(error: PurchasesError, userCancelled: Boolean) {
            if (!userCancelled) showError(error)
        }
    },
)</code></pre>
<p>Here <code>onCompleted</code> hands you a non-null <code>StoreTransaction</code>, which is the reason to prefer this form when you need the purchase token. Note that <code>purchaseToken</code> is non-null but <code>orderId</code> is still <code>String?</code> even here, because it is absent for pending and test purchases.</p>
<h3><strong>Coroutines</strong></h3>
<p>The suspend variants come in two flavors per operation, one that throws and one that returns <code>Result</code>:</p>
<pre><code>viewModelScope.launch {
    try {
        val result = Purchases.sharedInstance.awaitPurchase(params)
        unlockContent(result.customerInfo)
    } catch (e: PurchasesTransactionException) {
        if (!e.userCancelled) {
            showError(e.error)
        }
    }
}</code></pre>
<p><code>awaitPurchase</code> returns a <code>PurchaseResult</code> holding <code>storeTransaction</code> and <code>customerInfo</code>. That type gets its <code>equals</code>, <code>hashCode</code>, and <code>toString</code> from the Poko compiler plugin rather than from <code>data class</code>, which means there is no generated <code>copy()</code> and no destructuring. Read the two properties directly.</p>
<p><code>PurchasesTransactionException</code> is where <code>userCancelled</code> lives on the coroutine path, and it exposes <code>error</code>, <code>code</code>, and <code>underlyingErrorMessage</code>. That last one matters for support, because <code>PurchasesError.message</code> is not a server message. It is a computed property returning <code>code.description</code>, the same canned English sentence for every occurrence of a given code. <code>underlyingErrorMessage</code> carries what actually went wrong, so log both.</p>
<h2><strong>Reading the result</strong></h2>
<h3><strong>Entitlements</strong></h3>
<p>The <code>CustomerInfo</code> handed to your success callback is already up to date, so gate on it directly rather than issuing another fetch. Watch which collection you read from. <code>entitlements.all</code> contains every entitlement the user has ever had, including expired ones, while <code>entitlements.active</code> contains only the live ones. Reading <code>all</code> and forgetting to check <code>isActive</code> grants access to a lapsed subscriber.</p>
<pre><code>val isPro = customerInfo.entitlements[&quot;pro&quot;]?.isActive == true
val hasAnything = customerInfo.entitlements.active.isNotEmpty()</code></pre>
<p>The indexing operator reads through <code>all</code>, which is why the <code>isActive</code> check is not optional there. <code>entitlements.active</code> is a <code>Map&lt;String, EntitlementInfo&gt;</code>, not a list, so <code>active.keys</code> gives you the identifiers.</p>
<p>Individual <code>EntitlementInfo</code> properties worth surfacing in a settings screen:</p>
<ul>
<li><code><strong>willRenew</strong></code>: false once the user has turned off auto renew. Always true for lifetime access.</li>
<li><code><strong>expirationDate</strong></code>: null for lifetime access. For a trial, this is the trial expiration.</li>
<li><code><strong>periodType</strong></code>: one of <code>NORMAL</code>, <code>INTRO</code>, <code>TRIAL</code>, <code>PREPAID</code>.</li>
<li><code><strong>unsubscribeDetectedAt</strong></code>: when the user cancelled, if they did.</li>
<li><code><strong>billingIssueDetectedAt</strong></code>: when a payment problem started, if there is one.</li>
</ul>
<p>The KDoc on the last two carries the important part. An entitlement can still be active while both are set, so branch on <code>isActive</code> for access control and treat the dates as context for messaging.</p>
<p>To react to changes that happen outside your purchase call, set the listener once and clear it:</p>
<pre><code>Purchases.sharedInstance.updatedCustomerInfoListener = UpdatedCustomerInfoListener { info -&gt;
    _customerInfo.value = info
}</code></pre>
<p>This fires when the SDK updates its cache after an app launch, a purchase, a restore, or a fetch. It is not a server push, so it is not a substitute for calling <code>getCustomerInfo</code> when your UI needs current state.</p>
<h3><strong>Restore</strong></h3>
<p>Restore is a single call with the same two shapes, and it is not optional. Both stores require a way for a user on a new device to recover access:</p>
<pre><code>Purchases.sharedInstance.restorePurchasesWith(
    onError = { error -&gt; showError(error) },
    onSuccess = { customerInfo -&gt; refreshUi(customerInfo) },
)</code></pre>
<p>One naming inconsistency to watch for when you implement the interfaces instead: <code>ReceiveCustomerInfoCallback</code> uses <code>onReceived</code>, while <code>SyncPurchasesCallback</code> uses <code>onSuccess</code>.</p>
<h2><strong>Switching plans: Product changes and replacement modes</strong></h2>
<p>Everything above buys a new subscription. Moving an existing subscriber from one plan to another is a different operation, and it needs two more builder calls:</p>
<pre><code>val params = PurchaseParams.Builder(activity, annualPackage)
    .oldProductId(currentSubscriptionId)
    .replacementMode(StoreReplacementMode.CHARGE_PRORATED_PRICE)
    .build()</code></pre>
<p>Product changes work in the Play Store and the Galaxy Store. The Amazon Appstore ignores them.</p>
<h3><strong>Getting </strong><code><strong>oldProductId</strong></code><strong> right</strong></h3>
<p>The value is the subscription id, not the id plus base plan. This trips people up because the place you naturally read it from returns the combined form:</p>
<pre><code>val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()
val oldProductId = customerInfo.activeSubscriptions
    .map { it.split(&quot;:&quot;).first() }
    .firstOrNull()</code></pre>
<p><code>activeSubscriptions</code> returns <code>subscriptionId:basePlanId</code> for Google subscriptions. The builder is forgiving here, its KDoc says anything after a <code>:</code> is ignored, but splitting yourself makes the intent visible.</p>
<h3><strong>The five replacement modes</strong></h3>
<p><code>StoreReplacementMode</code> replaced <code>GoogleReplacementMode</code> in SDK 10.3.0. The old enum and the old <code>.googleReplacementMode()</code> setter both still exist and are both deprecated, so new code should use <code>StoreReplacementMode</code> and <code>.replacementMode()</code>.</p>
<ul>
<li><code><strong>WITHOUT_PRORATION</strong></code>: takes effect immediately. The user pays nothing today, then the full new price on the old plan's expiration date.</li>
<li><code><strong>WITH_TIME_PRORATION</strong></code>: takes effect immediately. The user pays nothing today, and the time remaining on the old plan becomes free time on the new one.</li>
<li><code><strong>CHARGE_PRORATED_PRICE</strong></code>: takes effect immediately. The user pays the price difference today and the billing date does not move.</li>
<li><code><strong>CHARGE_FULL_PRICE</strong></code>: takes effect immediately. The user pays the full new price today and the remaining time from the old plan is credited forward.</li>
<li><code><strong>DEFERRED</strong></code>: takes effect when the old plan expires. The user pays nothing today, then the new price on the old expiration date.</li>
</ul>
<p>One structural note if you plan to branch on the mode you were given. <code>StoreReplacementMode</code> is not an enum, it is a class exposing five constants, so a <code>when</code> over it will not compile as exhaustive. Include an <code>else</code> branch.</p>
<p>The default is <code>WITHOUT_PRORATION</code>. It is set in the builder's internal state, not left null, so a product change with no <code>replacementMode</code> call is a real decision that was made for you. For a paid upgrade that is rarely the one you want, because the user gets the better plan immediately and pays nothing until their old period ends.</p>
<p>If you want to have a better undertsanding on the proration mode, check out <a href="https://www.revenuecat.com/blog/engineering/google-proration">Understanding Google Play subscription proration: a developer’s guide</a>.</p>
<h3><strong>Choosing a mode</strong></h3>
<ul>
<li><strong>Upgrade, charge now</strong>: <code>CHARGE_PRORATED_PRICE</code>. The user pays the difference and the renewal date does not move. This mode is only valid when the new plan's price per time unit is higher than the old one's, which Google Play checks and rejects otherwise, so it is an upgrade only mode rather than a general purpose one.</li>
<li><strong>Upgrade, generous</strong>: <code>WITH_TIME_PRORATION</code>. The user pays nothing today and their unused time becomes free days on the higher tier.</li>
<li><strong>Downgrade</strong>: <code>DEFERRED</code>. The user keeps what they paid for until it runs out, then drops to the cheaper plan. Charging someone immediately to receive less is a support ticket.</li>
<li><strong>Crossgrade at the same price</strong>: <code>WITH_TIME_PRORATION</code> or <code>DEFERRED</code>, depending on whether the switch should be felt now.</li>
</ul>
<p>Two constraints come straight from the KDoc and are worth encoding as guards. <code>WITH_TIME_PRORATION</code> fails on the Play Store when both options belong to one <code>StoreProduct</code>, so use a different mode for base plan changes within a single product. <code>CHARGE_FULL_PRICE</code> is not supported by the Galaxy Store, and passing it there surfaces a <code>PurchasesErrorCode.UnsupportedError</code> in your error callback rather than starting a purchase.</p>
<h3><code><strong>DEFERRED</strong></code><strong> reports back differently</strong></h3>
<p>The deferred mode is the one that makes purchase handling code look broken. Nothing changes today, so the <code>CustomerInfo</code> your success callback receives still describes the old plan. Code that reads the new product id out of the callback and writes it somewhere will write the old one.</p>
<p>Under <code>DEFERRED</code> the product id is also what matches the purchase callback to the transaction Google Play returns, which is another reason to pass the plain subscription id. Treat a deferred change as scheduled rather than done, and let the entitlement's <code>expirationDate</code> and your webhook pipeline tell you when it happened.</p>
<h2><strong>Other </strong><code><strong>PurchaseParams</strong></code><strong> options</strong></h2>
<h3><code><strong>isPersonalizedPrice</strong></code></h3>
<pre><code>val params = PurchaseParams.Builder(activity, packageToPurchase)
    .isPersonalizedPrice(true)
    .build()</code></pre>
<p>This is a compliance flag, not a pricing feature. It tells Google Play to disclose in the purchase sheet that the price shown was personalized for this user, which EU consumer law requires when that is true. The default is false, and the Amazon Appstore ignores it. Set it only when you are actually personalizing the price.</p>
<h3><strong>Add-ons</strong></h3>
<p>For Play Store subscriptions with add-ons, the builder takes extra items:</p>
<pre><code>@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
val params = PurchaseParams.Builder(activity, basePackage)
    .addOnPackages(listOf(addOnPackage))
    .build()</code></pre>
<p>There are matching <code>addOnStoreProducts</code> and <code>addOnSubscriptionOptions</code> overloads. All three sit behind <code>@ExperimentalPreviewRevenueCatPurchasesAPI</code>, and the restrictions from the KDoc are strict: Play Store subscriptions only, every add-on's renewal period must match the base product's period, and at most 49 add-ons per purchase.</p>
<h2><strong>Keeping RevenueCat's paywall UI</strong></h2>
<p>Now the case where RevenueCat draws the paywall. If the paywall you designed in the dashboard is what you want to render but you need your own code in the flow, you do not have to give up the UI.</p>
<p>The composable takes a single options object, and <code>dismissRequest</code> is a constructor parameter of the builder rather than a setter:</p>
<pre><code>Paywall(
    PaywallOptions.Builder(dismissRequest = { navController.popBackStack() })
        .setOffering(offering)
        .setListener(paywallListener)
        .setShouldDisplayDismissButton(true)
        .build(),
)</code></pre>
<p><code>setOffering(null)</code> falls back to the current offering. <code>setShouldDisplayDismissButton</code> defaults to <code>false</code> here, while <code>PaywallDialogOptions</code> and the activity launch options default it to <code>true</code>, so an embedded paywall with no close button is the expected behavior rather than a bug.</p>
<h3><code><strong>PaywallListener</strong></code><strong>: Observing the flow</strong></h3>
<p><code>PaywallListener</code> is an interface where every method has a default implementation, so you override only what you need:</p>
<pre><code>private val paywallListener = object : PaywallListener {
    override fun onPurchaseCompleted(customerInfo: CustomerInfo, storeTransaction: StoreTransaction) {
        analytics.track(&quot;purchase_completed&quot;, storeTransaction.productIds)
        unlockContent(customerInfo)
    }

    override fun onPurchaseError(error: PurchasesError) {
        analytics.track(&quot;purchase_error&quot;, error.code.name)
    }

    override fun onPurchaseCancelled() {
        analytics.track(&quot;purchase_cancelled&quot;)
    }

    override fun onRestoreCompleted(customerInfo: CustomerInfo) {
        refreshUi(customerInfo)
    }
}</code></pre>
<p>The full set is <code>onPurchaseStarted</code>, <code>onPurchaseCompleted</code>, <code>onPurchaseError</code>, <code>onPurchaseCancelled</code>, <code>onRestoreStarted</code>, <code>onRestoreCompleted</code>, <code>onRestoreError</code>, plus <code>onUrlOpened</code> for links inside the paywall and <code>onWebCheckoutOpened</code> for the case where the user left to pay on the web. That last one is distinct from cancellation and needs handling separately, because the user has not given up, they have gone elsewhere to finish. If you also plan to supply purchase logic, read the last section first: four of these callbacks go quiet in that mode.</p>
<h3><strong>Gating the flow before it starts</strong></h3>
<p>The two hooks people want when they ask about customizing the purchase flow are not in the list above. They are the initiated callbacks, and they hold the flow open until you answer.</p>
<p>Each one receives a <code>Resumable</code>, a <code>fun interface</code> with <code>resume(shouldResume: Boolean)</code> and an <code>invoke</code> operator defaulting to true. So <code>resume()</code> proceeds and <code>resume(false)</code> aborts. If you never call it, the purchase never starts. Watch the import: the one <code>PaywallListener</code> wants is <code>com.revenuecat.purchases.ui.revenuecatui.utils.Resumable</code>. There is an identical <code>com.revenuecat.purchases.customercenter.Resumable</code> for Customer Center, and picking it gives you a confusing message about overriding nothing.</p>
<p>Both hooks are members of the same listener shown above:</p>
<pre><code>override fun onPurchasePackageInitiated(rcPackage: Package, resume: Resumable) {
    if (userIsSignedIn()) {
        resume()
    } else {
        showSignInSheet(
            onSuccess = { resume() },
            onDismiss = { resume(false) },
        )
    }
}</code></pre>
<p><code>onRestoreInitiated(resume)</code> gives you the same gate for restore. This is the place to require sign in, ask for a confirmation, or check an age gate, because it runs before Google Play's sheet appears rather than after the user has paid.</p>
<h3><strong>Getting a result from a full screen paywall</strong></h3>
<p>For a paywall presented as its own activity, results come back through <code>PaywallResultHandler</code>. Construct the launcher in <code>onCreate</code>. There is no <code>remember</code> based helper:</p>
<pre><code>class MainActivity : ComponentActivity(), PaywallResultHandler {

    private lateinit var launcher: PaywallActivityLauncher

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        launcher = PaywallActivityLauncher(this, this)
    }

    override fun onActivityResult(result: PaywallResult) {
        when (result) {
            is PaywallResult.Purchased -&gt; unlockContent(result.customerInfo)
            is PaywallResult.Restored -&gt; refreshUi(result.customerInfo)
            is PaywallResult.Error -&gt; showError(result.error)
            PaywallResult.Cancelled -&gt; Unit
        }
    }
}</code></pre>
<p><code>PaywallResult</code> is a sealed class, so that <code>when</code> is exhaustive and a new case would break the build rather than fall through silently. A user cancellation arrives as <code>Cancelled</code> rather than as an <code>Error</code> carrying <code>PurchaseCancelledError</code>, because the activity maps that code before returning.</p>
<p>To launch it with a listener or custom purchase logic attached, use the options form:</p>
<pre><code>@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
fun showPaywall(offering: Offering?) {
    launcher.launchWithOptions(
        PaywallActivityLaunchOptions.Builder()
            .setOffering(offering)
            .setListener(paywallListener)
            .build(),
    )
}</code></pre>
<p>The opt in is required only because of <code>setListener</code> and <code>setPurchaseLogic</code> on this particular builder. <code>Paywall()</code>, <code>PaywallOptions</code>, <code>PaywallListener</code>, and <code>PaywallPurchaseLogic</code> themselves need none. There is also <code>launchIfNeededWithOptions</code>, which takes a required entitlement identifier or a <code>shouldDisplayBlock</code> and skips presentation when the user already has access.</p>
<p>One caveat shows up only in the field. The SDK passes the listener and purchase logic to the activity through an in memory store rather than through the intent, because neither is serializable. If the process dies while the paywall is open, the SDK logs that they were lost, finishes the activity, and returns <code>PaywallResult.Cancelled</code>. Do not treat that result as a signal that the user declined. This applies only when you attached one of them. A paywall launched without either carries no in memory key and recreates normally.</p>
<h3><strong>Custom variables</strong></h3>
<p>A dashboard paywall can contain <code>{{ custom.player_name }}</code> placeholders, and you supply the values:</p>
<pre><code>PaywallOptions.Builder(dismissRequest = { dismiss() })
    .setCustomVariables(
        mapOf(
            &quot;player_name&quot; to CustomVariableValue.String(&quot;John&quot;),
            &quot;level&quot; to CustomVariableValue.Number(42),
            &quot;is_premium&quot; to CustomVariableValue.Boolean(true),
        ),
    )
    .build()</code></pre>
<p>Both <code>{{ custom.key }}</code> and <code>{{ $custom.key }}</code> resolve. Resolution order is the value you passed, then the dashboard default, then an empty string with a warning logged. Keys must start with a letter and contain only letters, digits, and underscores, and the SDK drops invalid keys with a warning rather than surfacing an error, so check logcat if a placeholder renders empty. One exception: <code>PaywallDialogOptions.Builder.setCustomVariables</code> skips the validator, so a bad key there is never logged and simply resolves to empty.</p>
<h2><strong>Owning the transaction with </strong><code><strong>PaywallPurchaseLogic</strong></code></h2>
<p>This is the case where your own code completes the Google Play transaction and RevenueCat supplies only the paywall and the backend. You configure <code>PurchasesAreCompletedBy.MY_APP</code> and hand the paywall your purchase code.</p>
<p>The interface has two suspend methods. The purchase side receives the activity and a params object:</p>
<pre><code>class MyPurchaseLogic(
    private val billing: MyBillingClient,
) : PaywallPurchaseLogic {

    override suspend fun performPurchase(
        activity: Activity,
        params: PaywallPurchaseLogicParams,
    ): PurchaseLogicResult {
        return when (val outcome = billing.purchase(activity, params.rcPackage)) {
            is Outcome.Success -&gt; PurchaseLogicResult.Success
            is Outcome.Cancelled -&gt; PurchaseLogicResult.Cancellation
            is Outcome.Failed -&gt; PurchaseLogicResult.Error(outcome.error)
        }
    }</code></pre>
<p>Restore in this mode means telling RevenueCat what Google Play already knows, which syncPurchases does, so the implementation is usually thin:</p>
<pre><code>    override suspend fun performRestore(customerInfo: CustomerInfo): PurchaseLogicResult {
        return try {
            Purchases.sharedInstance.awaitSyncPurchases()
            PurchaseLogicResult.Success
        } catch (e: PurchasesException) {
            PurchaseLogicResult.Error(e.error)
        }
    }
}</code></pre>
<p>If your billing code is callback based, extend <code>PaywallPurchaseLogicWithCallback</code> and implement <code>performPurchaseWithCompletion</code> and <code>performRestoreWithCompletion</code> instead. The base class bridges them for you.</p>
<p>The result type has three cases and the exact names are easy to miss. It is <code>PurchaseLogicResult.Cancellation</code>, not <code>Cancelled</code>, and the error case carries a nullable <code>errorDetails</code>:</p>
<ul>
<li><code><strong>Success</strong></code> triggers <code>syncPurchases</code>. From <code>performPurchase</code> it also dismisses the paywall. From <code>performRestore</code> it dismisses only if a <code>shouldDisplayBlock</code> was set and no longer matches.</li>
<li><code><strong>Cancellation</strong></code> tracks a cancel event and leaves the paywall open on the purchase path. On the restore path it is silently ignored.</li>
<li><code><strong>Error(errorDetails)</strong></code> shows an error dialog only if you provide the error. <code>PurchaseLogicResult.Error()</code> with no argument does nothing at all: no dialog, no tracking. Always pass the error.</li>
</ul>
<p><code>params</code> is a <code>PaywallPurchaseLogicParams</code>, which is what makes this interface usable for plan changes. It carries <code>rcPackage</code>, <code>subscriptionOption</code>, <code>oldProductId</code>, and <code>replacementMode</code>, so a product change configured on the dashboard paywall reaches your code with enough information to reproduce it. One type detail will bite you on the next line: <code>replacementMode</code> is typed <code>ReplacementMode?</code>, the interface that both <code>StoreReplacementMode</code> and the deprecated <code>GoogleReplacementMode</code> implement. There is no public converter, so narrow it yourself with <code>params.replacementMode as? StoreReplacementMode</code> before passing it to a <code>PurchaseParams.Builder</code>.</p>
<p>The older <code>PurchaseLogic</code> and <code>PurchaseLogicWithCallback</code> types are deprecated in favor of these, because their <code>performPurchase</code> took only a <code>Package</code> and had nowhere to put the product change details.</p>
<p>Three behaviors of this mode need internalizing before you ship it:</p>
<ol>
<li><strong>The SDK no longer acknowledges purchases</strong>, so your code must. The KDoc on <code>PurchasesAreCompletedBy.MY_APP</code> states the consequence: failing to acknowledge within 3 days leads Google Play to automatically refund the user.</li>
<li><code><strong>MY_APP</strong></code><strong> with a null purchase logic does not fall back to normal behavior.</strong> <code>validateState</code> puts the paywall into an error state that renders instead of your paywall.</li>
<li><strong>Four listener callbacks stop firing.</strong> <code>onPurchaseStarted</code>, <code>onPurchaseCompleted</code>, <code>onRestoreStarted</code>, and <code>onRestoreCompleted</code> live only on the <code>REVENUECAT</code> branch. The initiated hooks still run because they execute before the branch, and <code>onPurchaseError</code>, <code>onPurchaseCancelled</code>, and <code>onRestoreError</code> can still fire because they come from a <code>catch</code> that wraps both branches. Anything you were doing in <code>onPurchaseCompleted</code> has to move into your <code>performPurchase</code> success path.</li>
</ol>
<h2><strong>Choosing an approach</strong></h2>
<ul>
<li><strong>Full design control, standard purchase handling</strong>: <code>PurchaseParams</code> with <code>purchaseWith</code> or <code>awaitPurchase</code>.</li>
<li><strong>Dashboard paywall embedded in your layout</strong>: <code>Paywall</code> with <code>PaywallListener</code>.</li>
<li><strong>Dashboard paywall as its own screen, returning a result</strong>: <code>PaywallActivityLauncher</code> with <code>PaywallResult</code>.</li>
<li><strong>Dashboard paywall, your own billing code</strong>: <code>PurchasesAreCompletedBy.MY_APP</code> with <code>PaywallPurchaseLogic</code>.</li>
</ul>
<p>The naming differences between these surfaces are not consistent, and the compiler will not always help. <code>PaywallOptions.Builder</code> has <code>setPurchaseLogic</code>, while <code>PaywallDialogOptions.Builder</code> calls the same thing <code>setCustomPurchaseLogic</code>. <code>PaywallResult</code> uses <code>Cancelled</code> while <code>PurchaseLogicResult</code> uses <code>Cancellation</code>. When something does not resolve, check which options class you are holding.</p>
<h2><strong>Conclusion</strong></h2>
<p>The habit worth taking from all of this is to treat the silent defaults as decisions you are making. <code>WITHOUT_PRORATION</code> is what a product change does when you say nothing about proration, and it is the mode that charges an upgrading user nothing today. <code>defaultOption</code> is what gets purchased when you pass a <code>Package</code>, chosen by a documented rule you can predict. Both are reasonable, and both are wrong for some paywalls, so pick them on purpose and write the reason next to the builder call.</p>
<p>The other shift is to stop reading a declined purchase as a failed one. The SDK separates a failure from a decision at every layer, from the boolean on the error callback to the result types the paywall returns to the hooks that let you abort before Google Play ever appears. Your error handling should draw the same line, because the user who backed out of the sheet is still a prospect and does not need to be told that something went wrong.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[BoldVoice took the X off its paywall, and refunds wiped out the extra trial starts]]></title>
      <link>https://www.revenuecat.com/blog/growth/anada-lakra-boldvoice-sub-club-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/anada-lakra-boldvoice-sub-club-podcast-2026</guid>
      <pubDate>Thu, 17 Sep 2026 08:37:11 GMT</pubDate>
      <dc:creator><![CDATA[David Barnard]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Anada Lakra runs BoldVoice at $10M ARR with 10 people, every one of whom, engineers included, sits in two user interviews a week.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/b544ce78a91acc6523f5c86dfafcb4522b5f244d-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>BoldVoice tested two changes that every paywall optimizer has been tempted by. The first was a hard paywall: remove the dismiss button so nobody can proceed without starting a trial. The second was a softer trap: leave the X on the main paywall, but if a user dismisses it, show a percentage-off offer they can't close.</p>
<p><a href="https://www.youtube.com/watch?v=-5zgc14t2gc">Watch on YouTube</a></p>
<p>Both looked like wins. &quot;Both of those look so promising on the trial numbers,&quot; says Anada Lakra, BoldVoice's co-founder and CEO. &quot;And it makes sense, right? People have already put in all the time in the onboarding flow. They actually want to see what this app is all about.&quot; The team's instinct was to ship both to everyone.</p>
<p>Then the refunds arrived. &quot;The refund spiked by way more than the gain in start trials,&quot; Anada says. People who feel forced into a trial ask for their money back the moment the charge hits their bank account — and they leave with a grudge. &quot;Anything that kind of forces or tricks people into starting a trial, you'll pay it when it comes to refunds.&quot;</p>
<p>The fix was a different scoreboard. BoldVoice now judges paywall tests on net revenue after refunds, per exposed user, on a matured cohort — which means waiting at least an extra week for trials to convert and two more for refunds to settle before declaring anything. It's slower, and Anada admits the hardest part isn't discipline but patience: sitting on exciting data for a month or more. Asked at the end of the episode for her worst change of the past year, she doesn't hesitate: &quot;The paywall stuff. Just stop over-optimizing every single word and every button and everything. Optimize them for a little bit and then move on to the actual product.&quot;</p>
<h2><strong>Every employee does two user interviews a week — including the engineers</strong></h2>
<p>BoldVoice's user research isn't a function. It's a calendar rule.</p>
<p>Automated invites go out to a deliberate mix of users — subscribers of two or three years, people who started a trial last week and converted, people who didn't — asking for a 15-minute call. The invite automatically books whichever team members have space in their calendars. &quot;Every single person on the team — myself, my co-founder, our engineers, our growth people — everyone is in at least two user interviews a week,&quot; Anada says.</p>
<p>Two more layers sit on top. A super-user group meets roughly weekly, and engineers demo unshipped features to them, so heavy users shape the product before release. Users also come into the office so the team can watch what they tap and where they linger. &quot;Being able to actually verbally talk to users and actually see them interact with the product, I think is an underrated skill.&quot;</p>
<p>Anada is candid about the bias in this setup. Happy users book the calls. &quot;People who did not have a good experience are going to be less likely to jump on those calls.&quot; For them, BoldVoice uses a three-question survey answerable by replying to an email, and a short flow at cancellation asking what went wrong. Without that correction, she says, you end up making happy people happier and building a product that gets narrower and more technical instead of wider.</p>
<h2><strong>Why BoldVoice charged from day one — and won't copy Duolingo</strong></h2>
<p>BoldVoice launched in the first weeks of Y Combinator's summer 2021 batch and charged from the start: $10 a month, one-week free trial, no experimentation. That was the point. &quot;Charging for the product is a way to already figure out who that ICP is,&quot; Anada says. Willingness to pay correlates with the problem resonating, and it kept the team from chasing feedback from people who would never be customers. The energy went into onboarding and the core product instead of the paywall.</p>
<p>Pricing has since matured — BoldVoice leads with an annual plan at around $150 a year, offers monthly as a fallback, and has a $200-a-year tier with extra features like chat — but the philosophy hasn't moved toward freemium, and Anada is precise about why. &quot;The worst thing that someone can do is take some playbooks and some elements of a playbook from one app, combine it with a playbook of a different app, and create this mishmash strategy.&quot;</p>
<p>Duolingo's freemium base works because it teaches the basics of every language to a huge audience of hobby learners, and monetizing a fraction is enough. BoldVoice intentionally ignores that audience. Its users are intermediate-to-advanced professionals for whom the alternative is expensive one-on-one coaching with an accent coach: &quot;totally different ICP, different value proposition, different willingness to pay.&quot; The same logic ruled out the cliché college-campus launch, which BoldVoice tried. Students had less money, less urgency, and hadn't yet sat in the meeting where a colleague got listened to instead of them.</p>
<p>What BoldVoice has instead is a narrow free taste: dismiss the paywall and you get the speech assessment, the first daily plan, and a coach video, then a seven-day trial to continue on day two. David Barnard calls it a reverse trial. Anada calls it a middle ground that doesn't cost you the onboarding paywall for the people who are already ready to pay.</p>
<h2><strong>One Threads post, 100,000 people in Korea</strong></h2>
<p>BoldVoice's in-house ML team, working with the Hollywood accent coaches who help label its training data, built a tool that guesses where you're from after about ten seconds of speech. They shipped it as an ungated web app — no download, no trial — called the Accent Oracle, and Anada mentioned it in her monthly investor update.</p>
<p>&quot;One of them was an angel investor. It was Korean, and he was like, 'Oh, this is cool. I'll put it on Threads,'&quot; she says. &quot;And then I believe the next day we had 100,000 people in Korea try this thing.&quot;</p>
<p>The lesson she draws isn't &quot;build a viral tool.&quot; It's that you can't engineer virality, but you can add levers to something that already has it: a city-level guess re-spiked the Oracle, and showing users a preview of the fuller results waiting in the app lifted the number who made the jump. A TikTok trend of users crying at low scores gave the brand another unpaid spike — hundreds of millions of views for one creator, by her estimate — with the caveat that humor brings reach, not intent.</p>
<h2><strong>$10M ARR, 10 people, one meeting a week</strong></h2>
<p>Anada’s explanation for roughly $1M of ARR per employee is unglamorous. Hire founder-type doers who don't need direction. Hold one company-wide meeting, on Mondays, and leave the rest of the week for building and talking to users. Default users to the annual plan so paid acquisition pays back within the trial window and every channel's return is legible the same week.</p>
<p>And let everyone ship. &quot;Everyone in the company is using Claude. For the first time in my life, I'm actually shipping PRs myself, which is crazy. I'm not a technical person.&quot; The designer takes Figma work through to prototypes that are about 80% of the way to production before engineers touch them.</p>
<p>The part that keeps the small team defensible is the part that took longest to build. &quot;Still to this day, you cannot talk to ChatGPT or Claude or whatever have you and get this kind of feedback that BoldVoice gives you,&quot; Anada says — a deliberate bet that non-native English pronunciation is too narrow a niche for general models to chase and far too large a market to ignore.</p>
<p>In <a href="https://www.youtube.com/watch?v=-5zgc14t2gc" target="_blank" rel="noopener noreferrer">the full episode</a>, Anada also explains why BoldVoice is expanding from accent into the &quot;penultimate mile&quot; of spoken fluency, why she considers &quot;accent&quot; an almost anti-viral word, and what a self-imposed six-month term-sheet deadline did for her focus.</p>
<h2>Guest links</h2>
<ul>
<li><a href="https://www.linkedin.com/in/anadalakra/">Anada Lakra on LinkedIn</a></li>
<li><a href="https://www.linkedin.com/company/boldvoice">BoldVoice on LinkedIn</a></li>
<li><a href="https://www.boldvoice.com/">BoldVoice</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Why would someone pay for your app when AI does it for free?]]></title>
      <link>https://www.revenuecat.com/blog/growth/differentiator-against-ai</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/differentiator-against-ai</guid>
      <pubDate>Thu, 17 Sep 2026 08:12:40 GMT</pubDate>
      <dc:creator><![CDATA[Daphne Tideman]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[6 ways to win against LLM chats and the AI apps rushing to copy you]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/9fcc76852ccd574cf31320a02399424ed283ddc5-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>I’m not going to tell you that AI is not coming for you. I know founders wake up at 2am thinking: “<em>Why would anyone pay for my app when AI does it for free?”</em></p>
<p>But I also don’t believe apps will disappear altogether. <a href="https://www.linkedin.com/in/ethan-garr/">Ethan Garr</a>, a growth advisor, shared on a <a href="https://www.youtube.com/watch?v=zvibfgjmWfs">Sub Club Live about building with AI</a> that a friend had said apps would be gone within six months. Not what you want to hear when you work in the app space. Yet here we are more than six months later: apps are still around, and more are being launched than ever before (<a href="https://www.revenuecat.com/state-of-subscription-apps/">7x more since 2022, to be specific</a>). So I don’t believe they’ll disappear. Yes, LLM chats are replacing certain apps. Yes, the space is changing faster than ever. But the data suggests there is still hope, and I’m willing to cling to that data.</p>
<p>Especially as I’ve found you can win the game if you focus on the areas where an LLM chat cannot beat your app.</p>
<p><em>&quot;...since March we’ve seen a significant spike in student interest in ChatGPT. We now believe it’s having an impact on our new customer growth rate.&quot;</em></p>
<p>When CEO Dan Rosensweig of Chegg, a homework-help subscription service, <a href="http://fortune.com/2023/05/02/chegg-shares-tumble-students-fleeing-chatgpt-a-i/">said this during an earnings call in 2023</a>, the impact hit hard:</p>
<ul>
<li>The company’s stock fell 50%</li>
<li>By 2025, <a href="http://cnbc.com/2025/10/27/chegg-slashes-45percent-of-workforce-blames-new-realities-of-ai.html">45% of their workforce were made redundant</a></li>
<li>By Q2 2026, the <a href="http://businesswire.com/news/home/20260806916599/en/">freefall continued</a> and they attempted to pivot away from homework toward an employability platform</li>
</ul>
<p>Every step of the way, the blame was the same: AI.</p>
<p>Now take Duolingo: ChatGPT, and most LLMs for that matter, can also tutor you, for free. Google has layered live translation into its products — only a few months ago, I did a full strategy session with a subscription app startup in Portuguese (even though I don’t speak a word of Portuguese). The experience was honestly mind-blowing. I’m curious what I’m like in Portuguese.</p>
<p>Yet Duolingo isn’t floundering:</p>
<ul>
<li><a href="http://investors.duolingo.com/static-files/3c8277ee-bc94-4f5d-9b77-0db3e46f88b8">Q2 2026 revenue was up 18%</a></li>
<li>DAU rose 23% to 58.7 million</li>
<li>Paid subscribers increased 17% to 12.7 million</li>
</ul>
<p>By contrast, Duolingo’s CEO Luis von Ahn <a href="https://www.google.com/url?q=https://fortune.com/2025/08/27/duolingo-existential-crisis-ai-google-translate-language-learning-live-translation/&amp;sa=D&amp;source=docs&amp;ust=1789606406843987&amp;usg=AOvVaw1jQnNb8TpuRNtpWbeqmhYA">seems largely unimpressed by AI competitors</a>:</p>
<p>“Just having conversations in French on something like ChatGPT gets pretty boring after a while. It doesn’t keep you there. We keep you on task with all the gamification.”</p>
<p>So what explains the difference between the two? Why is one being, frankly, obliterated by AI, while the other’s owl is gleefully celebrating its success?</p>
<p>It’s about the app itself: while both companies create educational content, Chegg’s product is essentially a one-shot answer: a lookup experience without enough built around it in terms of data, habit, and workflow. <a href="https://www.revenuecat.com/blog/growth/cem-kansu-duolingo-sub-club-podcast-2026">Duolingo</a>, meanwhile, has spent 14 years building gamification systems and habit-forming product design that are much harder to replicate.</p>
<h2>People will pay for AI apps, they just won’t stay</h2>
<p>One thing worth separating before we dive into the numbers is what we mean by an ‘AI app’, because we often talk about these all under AI:</p>
<ul>
<li>An LLM chat is ChatGPT, Claude, Gemini</li>
<li>An AI app is a subscription app with AI as a central feature inside it, like Cal AI or Rosebud (and quite possibly yours)</li>
</ul>
<p>The first is the free competitor keeping you up at night. The second might be your own app. The data below is about the second.</p>
<p>AI-powered apps are casually out-monetizing and outperforming non-AI apps, according to RevenueCat’s <a href="https://www.revenuecat.com/blog/growth/subscription-app-trends-benchmarks-2026/">State of Subscription Apps 2026</a>:</p>
<ul>
<li>41% more revenue per payer</li>
<li>52% better trial conversion</li>
</ul>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ec6d4280ae252655b8ac7dafa36f307051cafe8b-1079x764.png" alt=""/></figure>
<p>But there is a flip side to this:</p>
<ul>
<li>12 month retention of 21.1% vs. 30.7%</li>
<li>Refunds are about 20% higher</li>
</ul>
<p>So <a href="https://www.revenuecat.com/blog/growth/ai-app-retention-study">consumers are willing to pay for AI apps, but the harder challenge is getting them to keep paying</a>. </p>
<p><a href="https://www.faceapp.com/">FaceApp</a> used AI to apply an age filter, allowing you to see an aged version of yourself (why anyone would want that is beyond me). It peaked with <a href="https://www.businessofapps.com/data/faceapp-statistics/">downloads growing by 2000%</a>, only to see a 75% drop the next month because of privacy concerns and the novelty wearing off. From 2019 to 2020, growth stagnated, with ARR remaining around $25 million (not a bad number to be stuck at, but a change in growth pace for sure).

FaceApp survived the challenges, but had to optimize the app to focus on a broader use case, from photorealistic beauty (e.g. applying makeup) to selfie retouching. Broadening their use case helped them better retain users — and in <a href="https://www.businessofapps.com/data/faceapp-statistics/">2025 they hit $150 ARR.</a></p>
<p>So the question in this article’s title is secretly the wrong one: people <em>will</em> pay. They’re already paying more — and faster — for AI-powered apps than for almost anything else right now.</p>
<p>The real question is <strong>why will they still be paying in month six?</strong> That’s a product question, not a marketing one, and it’s the question the rest of this article sets out to answer. (It’s also a much nicer question to wake up to at 2 am.)</p>
<p><strong>AI alone isn't enough to win customers permanently</strong>, and Sora proves it: when the company that builds the model can't keep people, the model was never what kept them. Which is also why I don't think the other AI apps or copycat AI apps in your category are your biggest competitors.</p>
<h2>ChatGPT is the biggest ‘window’ ever built</h2>
<p>A while ago, I was standing in the kitchen, checking several weather apps on my phone, trying to decide whether to walk the dog now. I turned around to find my father-in-law looking at me like I’d lost it: &quot;Why are you doing that? Just look out the window.&quot;</p>
<p>Weather apps’ competitors aren’t other apps; they are looking out the window, asking around, or even just going to stand outside.</p>
<p>In the world of apps, your window is ChatGPT, Claude, or whatever other LLM chat of your choice. The free models, for now, can do a lot, and we see growing usage in categories of apps that support them, e.g, <a href="https://www.pewresearch.org/internet/2026/06/17/americans-and-ai-2026-chatbots-smart-devices-and-views-on-impact/">one in five US chatbot users ask for medical advice, and as many again about diet and fitness</a>.</p>
<p>The second, more meta way it is your window competitor is the ability to help you and others build faster. App launches went from about 2,000 to 14,700+ per month in four years; the volume is huge.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3b5aa03170ea86244228ae897fcce78ae15b09b3-1078x778.jpg" alt=""/></figure>
<p>But in that, we still see 69% of subscription revenue going to pre-2020 apps, versus just 3% to apps launched in 2025 and 2026. <a href="http://subclub.com/episode/why-app-economy-disruption-wont-happen-as-fast-as-everyone-thinks-eric-seufert">Eric Seufert argues that</a> while the cost of building is lower, the cost of distribution has increased because apps are all chasing the same attention. Rik Haandrikman echoes this: <a href="https://www.revenuecat.com/blog/engineering/building-apps-existing-audience">distribution is a moat.</a></p>
<p>Those who are holding on to that 69% know what a weather app can do that a window can’t, what their app can do that an LLM can’t. Those who can’t explain the differentiator of their app are the ones slowly dying under the weight of AI's growth.</p>
<h2>Anatomy of the apps AI has actually killed</h2>
<p>This is not a random murder spree; AI’s victims share something in common (I clearly need to stop reading Swedish noir novels). The apps replaced by LLM chats have one or more of these four things in common:</p>
<ol>
<li>No structure</li>
<li>No memory</li>
<li>No habit</li>
<li>No precision advantage</li>
</ol>
<p>Most apps being replaced are a single question or task and an answer. That was Chegg’s real problem: strip away the branding, and the product was a prompt with a subscription attached. <a href="https://stackoverflow.com/questions">Stack Overflow</a>, a question-and-answer platform for developers, is on the same path, with <a href="https://www.similarweb.com/blog/insights/ai-news/stack-overflow-chatgpt/">traffic falling around 6% every month since early 2022</a>. Why? An LLM gives you the same answer instantly, in a conversation, without posting a question and hoping a stranger replies.</p>
<p>Dan Layfield, Founder of Subscription Index, <a href="https://subclub.com/episode/the-subscription-growth-formula-churn-math-retention-wins-and-smart-product-bets-dan-layfield-subscription-index">made a point on Sub Club</a> that explains the pattern: <strong>your retention is dictated by how long the user has the problem you solve</strong>. Phone plans are retained for decades because the problem never goes away. An answer-lookup problem dies the second the answer arrives — and now the answer arrives in three seconds, for free. Chegg's real problem was never ChatGPT; it was that the problem they solved only lasted one homework question at a time.</p>
<p>The uncomfortable truth: <strong>if your app’s value fits in one prompt and one reply, you are in the blast radius.</strong></p>
<h2>The Blank Box Test (and six things a blank box cannot do)</h2>
<p>I have a challenge for you called the Blank Box Test. It’ll only take two minutes:</p>
<ol>
<li>Open your LLM chat of choice</li>
<li>Write out your user’s problem in their words</li>
<li>Analyze the results vs. what your app offers</li>
</ol>
<p>Hopefully, you’ll see one of six advantages appear that you can lean into further. If not, your app is replaceable. So consider each of these six areas and see which could help you effectively beat an LLM. With all of these, yes, there are ways an LLM could do these things, but a general chat doing all six — or even one of them — to a very high level for everyone’s daily needs? Unlikely.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9a984b6f5cd6eab7f51a1ee60c8fc495b25fed98-2048x1091.jpg" alt=""/></figure>
<p>And to be clear, this isn't AI apps vs. non-AI apps. Cal AI, Rosebud, and Tolan are all examples that lean heavily on AI. The difference is that <em>AI isn't what they're selling you</em> — rather, it’s part of their infrastructure that helps them lean into one of the six differentiators against LLMs. That’s why I’ve also intentionally shared several non-AI examples.</p>
<h3>1. Structure</h3>
<p>I use <a href="https://www.deliciouslyella.com/membership">Deliciously Ella</a> and <a href="https://www.mob.co.uk/">Mob Kitchen</a> for recipe inspiration. Until I had what I thought was a genius idea: why not use Claude to build a weekly meal plan based on what I already have in the fridge, my dietary requirements, the foods I love, and the kinds of food that work well for me as someone with ADHD and an intense sweet tooth? A perfect, personalized plan.</p>
<p>So I did exactly that. I used it for a few weeks, but eventually went back to my usual apps because I missed the beautiful visuals of seeing what I was going to cook. I missed feeling inspired and getting excited about new recipes. The checklists and text in Claude just weren’t doing that.</p>
<p>While I probably could have forced Claude to create images and build everything into a visual weekly dashboard, the structure of the apps is what gives them their value. I can browse them on the go, discover recipes as they catch my eye, and build my shopping list along the way. It’s not worth it to me to rebuild that, and I’d argue that for most average consumers it wouldn’t be.</p>
<p>I also think that’s why apps like <a href="https://www.runna.com/">Runna</a> are still winning against LLMs. For some people, a quick plan generated by an LLM will be enough for their daily run. But Runna gives you the full calendar, guidance during your run, and the exact structure of every workout you can follow live. <strong>It turns a plan into an experience</strong>, and that’s where the added value lies.</p>
<h3>2. Memory</h3>
<p>Luckily, the days when our LLM chats acted like Dory from <em>Finding Nemo</em> are gone. Building structures like second brains (a persistent, semantically searchable digital knowledge with AI) can allow your LLM to have so much more memory than ever before. But you still have to get it to use that data in some cases to make your experience better, and not all data sources are easy for the average AI user to connect to an LLM.</p>
<p>Meanwhile, your app can use the accumulation of user data so that by month six it is so much better than it was in month one, in ways you may not have even thought of. Memory + judgment of the right prompts are powerful.</p>
<p>This is the investment part of the <a href="https://www.nirandfar.com/how-to-manufacture-desire/">Nir Eyal Habit Loop</a>: getting users to invest in your app. If you do so, ensure that the investment pays back, and you’ll be able to add value to their experience, like <a href="https://www.rosebud.app/">Rosebud</a>, an AI journaling app that collects your data over time and uses it to better reflect and learn with you.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a85a410504734c832482c0b1e42d4a875761208b-2000x1125.webp" alt=""/></figure>
<h3>3. Habit</h3>
<p>Habit comes down to accountability: being able to push someone to take action, whether that’s through streaks, reminders, or other <a href="https://www.revenuecat.com/blog/growth/gamification-in-apps-complete-guide">forms of gamification</a>.</p>
<p>Duolingo is a perfect example. You can ask an LLM to help you build a habit out of something, and it can tell you the theory behind habituation. Having it embedded in your app in a beautiful, intuitive way, with all those reminders and nudges already built out — it’s technically possible with AI, but not something the average user is willing to do themselves.</p>
<p>The numbers behind those nudges are not small either. When Duolingo introduced leaderboards, <a href="https://www.lennysnewsletter.com/p/how-duolingo-reignited-user-growth">Jorge Mazal, their former CPO, shared</a> that overall learning time went up 17% and the number of highly engaged learners tripled.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/351f9bb3cd253e48f847a32371cc8053c661ae42-920x820.png" alt=""/></figure>
<p>Once a streak passes 10 days, the odds of someone quitting drop sharply. An LLM can explain habit theory to you beautifully — but it won’t cause a terrifying owl to chase you at 9pm because your streak is about to die.</p>
<h3>4. Precision</h3>
<p><a href="https://www.calai.app/">Cal AI</a> launched in the middle of the rise of LLM chats in May 2024. Within a year, its teenage founders were claiming around <a href="https://techcrunch.com/2026/03/02/myfitnesspal-has-acquired-cal-ai-the-viral-calorie-app-built-by-teens/">$2 million in monthly revenue, and by the time MyFitnessPal acquired it, it had passed 15 million downloads</a>. The acquisition also gave them MyFitnessPal's food database of 20 million foods and 68,500 brands, allowing them to redesign and improve the product.</p>
<p>You could argue that their product is just a simple question-and-answer tool, but the precision with which they can tell you what is in your food, thanks to all that data and insight, is much better than asking ChatGPT. Especially if you combine it with their structure for organizing and tracking your calories and intake. Gives us hope, right? If two teenagers can beat LLM chatbots, so can we.</p>
<p><a href="https://merlin.allaboutbirds.org/">Merlin</a>, a bird-sound app, is another great example of this. They have a curated acoustic library built from decades of <a href="https://ebird.org/home">eBird</a> and Macaulay Library data and have <a href="https://www.birds.cornell.edu/home/the-magic-of-merlin">10 million active users</a>. That dataset and the precision to recognize bird sounds effectively are their strengths.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/e00e768546945b919006f64a7df522e127e849cd-700x560.webp" alt=""/></figure>
<h3>5. Connection</h3>
<p>Trust, tone, and brand feel go a long way in building a connection. Look at <a href="https://www.tolans.com/">Tolan</a>, the AI companion. It builds a personality that stands out and gives a feeling of connection. <a href="https://www.revenuecat.com/blog/growth/ajay-mehta-sub-club-podcast-2025">Its founder argues</a> that doing this well, with voice, memory, and character, is exactly what LLM Chats are not set up to focus on.</p>
<p>But connection goes further than an app that knows you. It’s about belonging: Duolingo’s leagues, Strava’s clubs, the run crew that only exists because the app brought them together. People don’t cancel things that feel like a part of them. <a href="https://www.revenuecat.com/blog/growth/solve-app-problems-emotionally">Emotional connection is a strong driver of retention</a>. I also find it telling that <a href="https://techcrunch.com/2025/08/12/ai-companion-apps-on-track-to-pull-in-120m-in-2025/">AI companion apps are on track to pull in $120 million</a> even though chatting with an AI is free: people pay for a relationship, not a reply.</p>
<p>In a world flooded with vibe-coded apps, those that take the time to build connection and an experience stand out.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/3499d378643f190b8afee4e7522c60afa4b18a8a-528x587.jpg" alt=""/></figure>
<h3>6. Digital and physical</h3>
<p>I also believe there’s room for another advantage in subscription apps that combine physical and digital to deliver value. I use a subscription app called <a href="https://plantwithwillow.co.uk/">Willow</a> to better understand what my plants need and know when to water them, when to move them to a sunnier or less sunny spot, and more.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/239524ddcde2111866ec02a7470a239b2bcd0802-1024x682.png" alt=""/></figure>
<p>They can do this better than an LLM chat by itself because they have sensors to feel and gather this data. While we can connect LLMs to other data sources, I believe there’s room for the physical advantage of naturally creating combined experiences with reality to add extra value.</p>
<h3>The power of multiple moats</h3>
<p>The combination of a few of these has allowed even ‘wrapper’ apps to survive and thrive. Take <a href="https://www.costarastrology.com/">Co-Star</a>, an astrology app: readings are written by a mix of AI and human writers, and Midjourney thought its packaging and ritual were worth <a href="https://techcrunch.com/2026/07/24/midjourney-acquired-the-astrology-app-co-star/">acquiring in 2026</a>. Yet users pay anyway: for the packaging, the birth-chart personalization, and the daily push ritual. That is a combination of structure and habit helping them win.</p>
<h2>What this means for what you build, say, and charge</h2>
<p>Firstly, you need to understand which of these six differentiators are available to your category and deliberately invest in them, figuring out what can truly make you different from an LLM chat or a quickly-created AI competitor. It will help you speak to your most loyal users and understand: if they are using LLMs, <a href="https://www.revenuecat.com/blog/growth/product-market-fit-subscription-apps/">why are they still choosing you</a>? If 95% of your audience still sees that value and understands it, and it’s not just down to brand loyalty, that helps you work out which of these matters most.</p>
<p>With distribution being the challenge now, your existing audience is a moat in itself. Investing in <a href="https://www.revenuecat.com/blog/growth/premium-win-lifecycle-messaging">retaining users through the initial experience and onboarding</a>, by building in guidance and value, is going to help you win.</p>
<p>From there, communication comes down to marketing and sharing outcomes that a blank box can’t promise. You want to sell the job you helped them achieve, not the engine. I’m not the only one who cringes at the label ‘AI-powered’ — Irrational Labs conducted <a href="http://growthunhinged.com/p/ai-messaging-study">a study of 767 software users</a> in which AI-powered features actually <em>lowered</em> the perceived value and did nothing to justify the higher price. <strong>AI is a tool, not an outcome.</strong></p>
<p>Finally, don’t risk getting into a race to the bottom. Yes, competition is fierce, but <a href="http://revenuecat.com/blog/growth/ai-subscription-app-pricing">competing against the $0 competitor won’t help you win</a>. Price isn't a long-term differentiator, and we see that in the <a href="https://www.revenuecat.com/blog/growth/subscription-app-trends-benchmarks-2026">State of Subscription Apps report</a> too: high-priced cohorts hold at 6x the LTV of lower-priced cohorts, and premium positioning signals the differential you’ve built. You just need to make sure you deliver that value for that price. Your <a href="https://www.revenuecat.com/blog/growth/freemium-tier-design">free tier</a> is competing with that blank box too, since it is the more limited experience, so make sure your free experience shows off the same advantages.</p>
<h2>The irony of it all</h2>
<p>We’ve talked about LLMs like ChatGPT as competitors. We talked about them helping your competitors build, but there’s an additional challenge; they are also your distribution channel. You can’t completely ignore them or move away from them. They need to know your app in order to suggest it to users. And they are undeniably useful — <a href="http://lennysnewsletter.com/p/why-chatgpt-will-be-the-next-big-growth-channel-brian-balfour">Brian Balfour recommends integrating them early</a> in your app and if you can, letting their ecosystem help you win while you strategically differentiate.</p>
<p>Your users will continue to pay for you if your app knows them (memory), structures them (opinionated workflow), holds them to it (habit), gets the details right (precision), feels like somewhere they belong (connection), and helps them get more value out of the world around them (physical and digital). Run the two-minute Blank Box Test this week if you haven’t, and start building the things a blank box cannot do.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Rico Can Now Take Action]]></title>
      <link>https://www.revenuecat.com/blog/engineering/rico-can-now-take-action</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/engineering/rico-can-now-take-action</guid>
      <pubDate>Mon, 14 Sep 2026 23:01:57 GMT</pubDate>
      <dc:creator><![CDATA[Austin Blake]]></dc:creator>
      <category><![CDATA[Engineering]]></category>
      <description><![CDATA[Rico can now make the changes for you, not just suggest them. I asked it to build a paywall experiment off my default paywall, and it created the Offerings, the Packages, and a running test — no dashboard required.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/571b903d51db2352b9af89b81c719664e6601b73-1774x887.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>I told Rico to build a paywall experiment off my default paywall. It created the new Offerings, the Packages, and the second paywall. Even better, it checked with me at each step and handed me a running test. Best of all? I never opened the dashboard.</p>
<p>That's it. That’s the update. Rico used to answer questions about your app business and suggest what to do next. Now, it can just go get the work done.</p>
<p><a href="https://www.youtube.com/watch?v=C2xkxGBwHZo">Watch on YouTube</a></p>
<h2>Rico can change your setup for you</h2>
<p>With your approval, Rico can create and manage products, Offerings, Packages, and Entitlements, and build or edit paywalls directly in RevenueCat.</p>
<p>That includes real store products, not just RevenueCat-side config. Rico can create and update the subscriptions that live in App Store Connect and Google Play. These features rolled out to everyone last week, so go check out your account right now.</p>
<h2>Experiments are the part I'd try first</h2>
<p>Every dev I talk to says the same thing about experiments: they’re easy to come up with, but a pain to set up. You have to duplicate the paywall, name everything, wire up new Offerings and Packages, keep track of which variant is which, and only then can you finally hit start.</p>
<p><strong>Rico can do that whole chain now.</strong> Ask it to test a new price or a new paywall against your current one and it builds out everything the test needs. Rico can create, update, start, stop, pause, and resume experiments, so the follow-up work is also just asking.</p>
<h2>You still approve everything</h2>
<p>Every proposed change requires your approval before anything happens. Rico is limited by the access level of the collaborator using it, so it can't do anything you couldn't do yourself in the dashboard. Project admins can go further and limit what Rico is allowed to do across all collaborators from project settings.</p>
<p>You can limit Rico to three levels of access: Read &amp; write with permission (to enable everything we’ve talked about here), Read only (to use Rico only for gaining insights to your data, but not for changing anything), and Disabled (will completely turn off all Rico integrations for this project).</p>
<h2>It's on your phone</h2>
<p>RevenueCat Mobile 2.0 brought Rico to iOS alongside the Paywalls AI Editor. Ask questions about your app business, attach a screenshot, approve Rico's actions, or generate a new paywall, all from your phone.</p>
<p>What’s more, Siri and App Shortcuts can now pull up MRR, revenue, active subscriptions, and trials without opening the app at all.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/aaa64c52c30d1af74a89de6f7abe61b3f2a0ec02-1179x1175.png" alt="Using Siri Shortcuts to view data in RevenueCat."/></figure>
<p>The Android release with Rico is in testing now.</p>
<h2>Go ask Rico</h2>
<p>The <a href="https://www.revenuecat.com/docs/tools/rico">Rico docs</a> have the full list of what Rico can and can't touch — or of course, you can just ask Rico. Have fun with it! Use Rico to learn more about what’s working or not in your app monetization scheme, then ask it to fix it. I’m looking forward to hearing about what you do with Rico.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Apple has paid David Barnard every month since 2008. He never made "tech money" doing it.]]></title>
      <link>https://www.revenuecat.com/blog/growth/david-barnard-launch-center-pro-launched-podcast-2026</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/david-barnard-launch-center-pro-launched-podcast-2026</guid>
      <pubDate>Thu, 10 Sep 2026 13:05:00 GMT</pubDate>
      
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[David Barnard built one of the most-loved iPhone apps of its era and has still never made more as an indie developer than he does as an employee.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/2b2d5f7b2f4b5e6da9537ca99178abd21c5487b1-1600x840.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>In June 2008, David had an app ready for the App Store's opening day and no way to ship it. He'd applied for a developer account within hours of the form going live, but Apple was processing applications slowly, and he'd skipped WWDC that year because spending roughly 20% of his family's $20,000 investment on a trip to San Francisco felt irresponsible. Attendees, it turned out, were getting accounts approved on the spot.</p>
<p><a href="https://www.youtube.com/watch?v=olIJliFfjNk">Watch on YouTube</a></p>
<p>So he did the only thing left. He went through the session slides, found the Apple evangelists who'd printed their email addresses on them, and wrote to six of them. Not a demand, he says, more a plea: &quot;Please, I beg of you, tell me what I need to do.&quot; He led with how much he loved the platform, and linked to the newspaper photo of himself first in line for the iPhone in San Antonio.</p>
<p>One of Apple's most senior evangelists didn't email back. He called, and they talked for about three hours. He promised to get David an account. It still didn't come through in time. Trip Cubby, a mileage log for taxes and reimbursements, missed the July 10 launch and shipped in August.</p>
<p>David is clear-eyed about why the call happened at all. Apple wanted developers who cared about the platform, and his email showed one. &quot;If I had said, like, I'm broke and I'm desperate, that's a very different email.&quot; It's the same rule he applies to pitching journalists and, today, to booking podcast guests: pitch what matters to the person reading, not what you need from them.</p>
<h2><strong>His dad refused the bridge loan, so they sold the Honda Accord</strong></h2>
<p>The first month on the store brought in about $5,000, which sounds fine until you remember Apple hadn't yet made clear when it would pay, and each region had a payout minimum over $100. David had already run out of money. So he asked his dad, a commercial real estate investor, for a bridge loan against the receivable.</p>
<p>His dad said no, and gave him a reason David hadn't considered: &quot;this is essentially a payday loan.&quot; As David recounts it: &quot;If I give you this 5,000 early, then by the time October comes around, you're going to need another 5,000, and then you're just going to be in the cycle of needing money every month.&quot; It was September 2008. His dad, property-rich and cash-poor as the market cracked, was broke too.</p>
<p>David and his wife sold her Honda Accord for four or five thousand dollars, became a one-car family, and made it to the first Apple check. He calls the missed launch and the &quot;crappiness of everything&quot; formative: &quot;Had it been day one, my whole life story probably would have looked different and not necessarily for the better.&quot;</p>
<h2><strong>Camera+ taught him the search trick that made Mirror</strong></h2>
<p>David never ran paid marketing. &quot;I've never done paid in 18 years.&quot; What he did instead was watch the top 100 obsessively, and around 2011 he became fixated on a question: why had the smart team behind Camera+ given their app such a bad name? &quot;Camera plus is not a brand. Like, people are going to type camera and then spell the word plus.&quot;</p>
<p>The answer was that they were smarter than he was. Apple's search gave a huge boost to exact-match names, and it ignored symbols. Type &quot;camera&quot; and Camera+ was an exact match. David estimates the app made $10 to 20 million on the back of that, and it sat near the top of the results for years.</p>
<p>He had a mirror app, built in a weekend by repointing the kaleidoscope code of an earlier app at the iPhone 4's new front-facing camera. It was called Mirror by App Cubby. He renamed it to the single word &quot;Mirror&quot; plus an obscure Unicode symbol that looked roughly like a mirror, chosen because &quot;Mirror+&quot; and &quot;Mirror++&quot; were already taken and he didn't want to be quite that obvious. Downloads jumped almost immediately and stayed there: &quot;a pretty tight range between, I think like seven and 10,000 downloads a day, every single day.&quot;</p>
<p>He sold Mirror in 2017 for over $400,000, having made over $1 million on it. It's still the top result for &quot;mirror.&quot; He's a little embarrassed by how well a mirror app did, and also correct that it was a good app, with a full-screen view, zoom, and a white border that brightened the screen in low light.</p>
<p>Charlie's metaphor: search traffic is a raging river, and if you can put a pipe in it, you siphon off a steady flow you can build a business on. David's point is that more surviving indie developers than admit it have exactly that pipe underneath them, whether by luck or design.</p>
<h2><strong>Launch Center Pro went to Phil Schiller, ended up inside Shortcuts, and never paid a tech salary</strong></h2>
<p>Launch Center began as a hack. Persistent notifications could be made to stick in Notification Center, so David and his partner Justin built a quick launcher that lived there, using URL schemes to open apps with context: call your wife, jump straight to Instagram's camera. Their tagline was &quot;Launch actions, not apps.&quot; Apple rejected the notification trick. They shipped the in-app launcher anyway, Federico Viticci championed it, and Launch Center Pro followed, with a grid of tiles and a tap-and-drag gesture for folders.</p>
<p>Getting Launch Center Pro approved, David heard later from an Apple employee, went all the way to Phil Schiller, because of the rule against mimicking the home screen. He's grateful, and also frustrated: &quot;the fact that the app that really kicked all of this off had to go all the way to a senior VP to get approved is in some ways kind of ridiculous.&quot; Kicked off what? Workflow. The Workflow team had asked to integrate; David replied that Launch Center Pro might compete on their core product; they decided to compete instead. Workflow became Shortcuts.</p>
<p>Then there's the JSON file. Launch Center Pro had spent hundreds of hours building a database of URL schemes, much of it submitted by developers through a form. It sat in a public S3 bucket. David later found the same file, structure and all, in the Workflow binary, and after Apple's acquisition, in the Shortcuts binary. &quot;Things that I typed into a JSON file were part of what Apple shipped in one of their system apps.&quot; He considered the copyright question, decided it wasn't a road worth going down, and wears it as a badge of honor.</p>
<p>Now the money. Launch Center Pro was featured by Apple, covered by TechCrunch and The Verge, and sat in people's docks for years at $3 or $5, with $0.99 sales. &quot;There were plenty of people who only ever paid us a dollar for something that they put in their dock and used every single day, multiple times a day.&quot; At the peak, he and Justin each made maybe $150K in a year. Over its life the app grossed a little over $1 million, gross, before Apple's 30%. Across 18 years David estimates he's paid Apple well over $1 million in commission. And the line the episode opens on: he never made more as an indie developer than he does as a RevenueCat employee.</p>
<p>There's one decision he'd take back: not shipping a paid update around iOS 10, when Justin was pushing for it and David didn't think they had enough to justify it. The subscription switch years later, iPhone-only, landed on a community that had moved on. And there was the Zapier deal, early enough that Zapier could only offer stock. Justin didn't want stock in &quot;some Silicon Valley company.&quot; By David's back-of-napkin math, it would be worth $50 to $100 million today.</p>
<p>Charlie puts the point plainly for anyone listening who thinks the well-known indie devs must be rich: &quot;there's a very wide variance on the amount of money people are making, and it's not connected to how well known they are in the community.&quot;</p>
<h2><strong>Eighteen years in, he finally shipped an app himself</strong></h2>
<p>David never learned to code. He bought the Hillegass Cocoa book in 2008, got a quarter of the way through, and hired a contractor at $50 an hour. Every app since has meant a partner or a contractor and the question that comes with them: is this feature worth a dollar-for-dollar return on a programmer's time? He's never run an onboarding A/B test on his own apps. Paywall tests only happened because RevenueCat made them easy.</p>
<p>This spring he vibe-coded Grit Method, a habit tracker for character built from a paper journal he'd made for his kids years ago, with four daily prompts: do a hard thing, avoid a trap, be grateful, help others. He did the design with AI tools, added the staggered animations and back-swipe polish a contractor would never have been asked for, and had a blast. &quot;It's been really fun to be able to just YOLO prompts and get a build like minutes later instead of like days later.&quot;</p>
<p>Then he did what he tells everyone not to do. It's made close to $2,000. He spent $2,000 on a UGC platform. He hasn't submitted it to Apple for featuring. &quot;I just fell into all the same traps that I preach against on Twitter,&quot; he says. &quot;I did nothing. I did none of it.&quot; He was too burnt out from building, and within days he'd started vibe-coding the next app instead. He has two more Xcode projects open right now.</p>
<p>Also <a href="https://www.youtube.com/watch?v=olIJliFfjNk" target="_blank" rel="noopener noreferrer">in this episode</a>: what he'd write into a partnership agreement after watching a 50/50 split slowly unwind, the sequencing mistake that held back Weather Up, why he thinks Launched is dessert and Sub Club is vegetables, and how <em>The Design of Everyday Things</em> ruined every cup holder for him except the one in his Kia EV9. If you make it to the end, reply to the episode post with one word: &quot;Usability!&quot;</p>
<h2><strong>Guest links</strong></h2>
<ul>
<li><a href="https://www.subclub.com">Sub Club podcast</a></li>
<li><a href="https://www.youtube.com/@SubClubPodcast">Sub Club on YouTube</a></li>
<li><a href="https://x.com/drbarnard">David Barnard on X</a></li>
<li><a href="https://apps.apple.com/us/app/weather-up-accuweather-data/id1196015787">Weather Up</a></li>
<li><a href="https://apps.apple.com/us/app/grit-method-show-up-daily/id6772582252">Grit Method</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Filling the silence: the post-subscription mistake that quietly causes churn]]></title>
      <link>https://www.revenuecat.com/blog/growth/premium-win-lifecycle-messaging</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/premium-win-lifecycle-messaging</guid>
      <pubDate>Thu, 10 Sep 2026 13:00:07 GMT</pubDate>
      <dc:creator><![CDATA[Alice Muir Kocourková]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[Getting new subscribers to their first premium win — before they start looking for the cancel button]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/44454700c5e67a5d77a032ea888ccc9f8fa858f5-1600x850.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Here's something most subscription app teams don't want to hear: the moment a user converts is also the moment many of them start drifting toward cancellation.</p>
<p>According to the <a href="https://www.revenuecat.com/state-of-subscription-apps/">State of Subscription Apps 2026</a>, 84% of 3-day trial cancellations and 64% of 7-day trial <a href="https://www.revenuecat.com/blog/growth/post-purchase-screen">cancellations occur between Day 0 and Day 1</a>. What’s more, nearly half of monthly subscribers, and the majority of yearly subscribers, <strong>do not renew even once</strong>.</p>
<p>By the end of this article, you'll have a clear picture of why that early churn happens — and a practical framework for preventing it. We'll cover the onboarding gap that quietly costs apps revenue every month, why new subscribers need to be treated like new <em>users</em>, and how to design a simple lifecycle journey that gets paying customers to their first 'premium win' — before buyer's remorse has a chance to set in.</p>
<h2>Stop treating conversion as the finish line</h2>
<p>Most subscription apps treat conversion as the finish line. But really it's the start of the retention journey. For many new subscribers, it's also where things start to go wrong.</p>
<p>Think about how much effort goes into getting a user to subscribe. Teams spend weeks, sometimes months, designing onboarding flows, refining paywall timing, testing trial journeys, tweaking upgrade messaging. Then the user finally converts… and the silence is deafening.</p>
<p>For new users, the app suddenly going quiet is a drastic 180. They were being nurtured towards the subscription they just paid for — and suddenly nothing. For app teams, that silence feels logical: the user paid, they're clearly engaged. Why interrupt them?</p>
<p>But <strong>subscribing doesn't mean the user actually </strong><em><strong>understands</strong></em><strong> what they've just unlocked</strong>. They might have converted based on a single feature they glimpsed during a trial, a discount that felt too good to pass up, or just a vague sense that this app might be useful. What they haven't done (yet) is experience the product as a paying customer. And without a little guidance, many of them will cancel before they ever do.</p>
<h3>Why do users cancel early?</h3>
<p>Early cancellations are rarely about product quality. They're almost always about <strong>the gap between what a user expected</strong> when they signed up, and <strong>what they actually experienced</strong> in those first few days. <a href="https://www.revenuecat.com/blog/growth/hard-paywall-activation-journey">That gap is where buyer's remorse lives</a>. And silence from the product team is what lets it take root.</p>
<p>There's another force making this worse: <strong>subscription fatigue</strong>. People are drowning in recurring charges — music, fitness, productivity, entertainment — and many have started turning off auto-renew the moment they subscribe to something new, just to feel in control. </p>
<p>It's a defensive habit born from too many forgotten subscriptions quietly draining bank accounts. Which means your new subscriber may have already mentally scheduled their exit before they've even opened the app a second time. <a href="https://www.revenuecat.com/blog/growth/first-renewal-churn">The window to prove your product is worth keeping isn't weeks — it's days</a>.</p>
<h2>Re-onboard existing subscribers like they're new users</h2>
<p>This is what's sometimes called the post-subscription gap: the window between a user converting and a <a href="https://www.revenuecat.com/blog/growth/subscription-app-expand-value">user genuinely getting value from what they paid for</a>.</p>
<p>It tends to show up in three connected ways:</p>
<h3>1. Upgraders don’t find the premium features</h3>
<p>This sounds almost too simple to be a real problem, but it is. And if you’re working with a freemium model, it’s quite possible the silence after conversion is what’s quietly pushing subscribers away.</p>
<p>Users who have already gone through free onboarding learn how to use the <em>free</em> version of your app. When they upgrade, nobody shows them what's changed. So they keep using the product the same way they always did — as if they're still on the free tier. The premium features exist, but the subscriber never finds them, and eventually cancels feeling like they never got much value when they started paying.</p>
<h3>2. Most apps stop communicating once a user converts</h3>
<p>After all that effort to nurture a potential subscriber, silence isn't a neutral choice — it's a missed opportunity. <a href="https://subclub.com/episode/how-to-boost-retention-with-subscription-lifecycle-messaging-alice-muir-phiture">When lifecycle messaging disappears</a>, users lack the product enablement to benefit from their upgrade, and often forget they even subscribed. They may question whether it was worth it, and cancel when renewal rolls around because they can't quite remember why they signed up.</p>
<h3>3. “They’ve already been onboarded” is the wrong mental model</h3>
<p>The mental model many teams use (&quot;they've already been through onboarding, they don't need more&quot;) is a big mistake. It also misses the nuance between different types of subscriber: an experienced free user who upgrades doesn’t need the same input as instant or zero-touch buyers. But that doesn’t mean they don’t need any attention — they’ve just entered into a completely different relationship with your product. They deserve to be welcomed and shown around, just like new subscribers; simply in a different way.</p>
<h2>What premium re-onboarding actually looks like</h2>
<p>Re-onboarding for new subscribers isn't about repeating everything from free onboarding. It's about specifically welcoming people into the premium experience — making it immediately obvious what they've unlocked and how to start using it.</p>
<p>Slack does this well. When a user starts a Pro trial, they immediately receive an email that doesn't just say 'your trial has started' — it tells them exactly what's changed. The headline feature gets its own moment ('Message &amp; file history unlocked'), followed by a clear list of everything else now available: Slack Connect channels, group huddles, unlimited canvases. There's no ambiguity about what the upgrade means. And crucially, the call to action isn't 'explore your account' — it's 'Explore in Slack', pointing users straight back into the product.</p>
<p>That's the whole job:</p>
<ol>
<li>Confirm the decision</li>
<li>Show what's unlocked</li>
<li>Direct to first action</li>
</ol>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/820dca534ee05b0a32181efee6b35d20d66dd4d5-677x1594.png" alt=""/></figure>
<p>Monday.com does something similar. When a user starts a trial, they immediately receive a message encouraging them to dive back in and create their first workspace. They also point to next steps for further guidance, like tutorial videos, blog content and access to support.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/058916b6b857db647532524cabc51657777ad481-680x1243.png" alt=""/></figure>
<p>The job of post-subscription messaging is to answer three questions for your new subscriber:</p>
<ol>
<li><strong>Where are the premium features?</strong> Show them, don't assume they'll find them.</li>
<li><strong>How do I actually use them?</strong> A list of features <a href="https://subclub.com/episode/the-art-of-driving-retention-through-product-ben-gammon-ladder">is not the same as a guide to a first action</a>.</li>
<li><strong>Did I make the right call?</strong> This is the question every new subscriber is quietly asking. Your job is to reassure them the answer is yes (and quickly).</li>
</ol>
<p><strong>The goal is to move someone from &quot;I paid for this&quot; to &quot;I just did something I couldn't do before&quot; as fast as possible. </strong>That first premium success — sometimes called a ‘premium win’ — is the moment a subscription stops feeling like an expense, and starts feeling like a habit.</p>
<h2>Define your premium win and build toward it</h2>
<p>Every product has its own version of a premium win, and figuring out what yours is should be one of the first things your retention team works on.</p>
<ul>
<li><strong>Strava</strong>’s premium win is seeing your performance data and training insights — the kind of analysis that makes a serious runner feel like they have a coach in their pocket</li>
<li><strong>Calm</strong>’s premium win is completing a sleep story or unlocking a full meditation course after a few free sessions have already built the habit</li>
<li><strong>Quizlet</strong>’s premium win is the moment a student gets access to Test Mode after experiencing the study tools for free</li>
</ul>
<p>What all of these have in common is that they're concrete, experiential moments — not a feature list, not a marketing promise, but an actual thing the user did that they couldn't do before. They’re <a href="https://www.revenuecat.com/blog/growth/hard-paywall-activation-journey">proof of the meaningful value your app promised</a>.</p>
<p>A simple way to think about your post-subscription messaging is to design it like a seven-day journey, but for people who just converted:</p>
<ul>
<li>Day zero is a warm welcome that tells them exactly what to do first</li>
<li>Day one or two is a nudge toward that specific premium win</li>
<li>Day three is a check-in where you celebrate what they've done and point them to what's next</li>
<li>By day five or six, you're spotlighting a feature based on what they've actually engaged with, not a generic list</li>
</ul>
<p>Duolingo does a version of this brilliantly with their weekly progress reports, which don't just remind users to practice — they show users what they've already accomplished, making the subscription feel like proof of progress rather than just a recurring charge.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/0e6889621f7b86ed4b8eee07b2defca7f825d083-1305x900.png" alt=""/></figure>
<h2>Four examples of lifecycle messaging to keep value visible long-term</h2>
<p>The post-subscription gap is most dangerous in the first week or two. But the underlying problem — subscribers gradually forgetting why they're paying — doesn't go away after that. It just becomes more hidden.</p>
<p>This is where longer-term <a href="https://www.revenuecat.com/blog/growth/lifecycle-marketing-campaigns-optimize-revenue">lifecycle messaging</a> earns its keep.</p>
<p>Think of it as <strong>resurfacing value</strong>:</p>
<ul>
<li>Helping users connect to real outcomes rather than abstract features</li>
<li>Reflecting their progress back to them</li>
<li>Reminding them why they started</li>
</ul>
<h3>Lifesum: sharing feature updates</h3>
<p>Lifesum sends messages to users when they improve a core feature — not just to announce the update, but to give subscribers a reason to re-engage with something they might have been using on autopilot.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/b83aac2ea011ab4645abab729a82e64306ef8e89-355x897.png" alt=""/></figure>
<h3>Netflix: personalization and recommendation</h3>
<p>Streaming apps like Netflix and HBO Max take a <a href="https://www.revenuecat.com/blog/engineering/the-ultimate-guide-to-subscriber-segmentation-for-apps">personalized approach</a>, sending recommendations for what to watch next based on subscribers’ viewing history. It's a simple idea, but it solves the friction point of decision fatigue. When the next step is obvious and feels relevant, users take it. When they have to think about what to do, they often don't bother — and habit breaks are exactly where churn starts.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/9f115bee69aef9d2516f436103662ca598a4f114-1408x901.png" alt=""/></figure>
<h3>Dropbox: feature spotlight</h3>
<p>Feature spotlights work particularly well for subscribers who haven't explored everything they've paid for. A targeted message that says &quot;hey, you've never tried X, and based on how you use the app, it might be your new favorite thing&quot; is infinitely more useful than a generic &quot;here's everything we offer&quot;. It's also a retention intervention — that untouched feature might just be what would have justified the subscription when they hit renewal.</p>
<p>One example is Dropbox, who reach out to people using Dropbox to save and share images, inviting them to try their new image search feature. The email quickly conveys why it’s relevant for the user, what the benefits of the undiscovered feature are, and where to go next.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/a865ded7df87a47e083d7ade12d423af1ef72db0-680x1472.png" alt=""/></figure>
<h3>Strava: user identity reinforcement</h3>
<p>Finally, don't underestimate the power of identity reinforcement. Strava tells users when they earn a ‘Local Legend’ badge by becoming the most frequent or fast rider/runner on a specific route. It highlights an achievement linked to the community, and gives them a reason to keep checking the app and building that habit. Strava even notifies users when they’re knocked off the top spot, creating an engagement loop between the entire user base competing with one another.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/1a870be066890199c7be9622037556cfa16ddaab-763x800.png" alt=""/></figure>
<p>People stick with products that reflect who they want to be. Subscription products that make users feel like they belong to something tend to retain far better than those that treat subscribers purely as revenue lines.</p>
<h2>If they’re engaged, just focus on the moments that matter</h2>
<p>It would be wrong to end without saying this: more messaging isn’t the answer. While filling the silence is effective, it only works if there is still breathing space. The goal isn't to fill every day with a notification — it's to show up at the right moments with the right thing.</p>
<p>If your subscribers are actively engaged with your product, the best thing lifecycle messaging can do is get out of the way:</p>
<ul>
<li>Suppress messages when users are already in a healthy habit</li>
<li>Avoid interruptions during focused sessions</li>
<li>Step back when the product is working</li>
</ul>
<p>These are all signs of a mature retention program, not a passive one.</p>
<p>The target is <strong>the moments when value is at risk of being forgotte</strong>n: right after someone converts, when <a href="https://www.revenuecat.com/blog/growth/how-to-spot-churn-before-it-happens/">engagement starts to slip</a>, when renewal is approaching. Those are the windows where a well-timed message can genuinely change the outcome.</p>
<p>Silence at the right time is fine. Silence right after someone hands over their credit card details is where early churn gets built.</p>
<aside class="tip"><strong>Start speaking to users at the right moment </strong><p>Alice’s StartApp School course reveals what to say (and when) across onboarding, trials, subscribers, drifting users, and win-backs — using segmentation, timing, and metrics to drive revenue.</p>
<p><a href="https://www.startapp.school/courses/lifecycle-marketing-for-apps">Enroll for free →</a>
</p></aside>
<h2>Treat conversion as the beginning of the relationship</h2>
<p>The shift in thinking here isn't complicated, even if building it out takes time. Treat conversion as the beginning of a new relationship, not the end of a funnel. Ask yourself: what does a new subscriber need to experience in their first week to feel confident they made the right call?</p>
<p>Start small. A warm premium welcome, a prompt toward the first premium win, and a week-one check-in is already meaningfully better than the silence most apps currently offer. Measure early cancellation rates in the first 30 days. Track whether new subscribers are actually reaching premium features in their first week. See what moves, and take it from there.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Give your next campaign a better path to purchase with RevenueCat Funnels]]></title>
      <link>https://www.revenuecat.com/blog/growth/give-your-next-campaign-a-better-path-to-purchase-with-revenuecat-funnels</link>
      <guid isPermaLink="false">https://www.revenuecat.com/blog/growth/give-your-next-campaign-a-better-path-to-purchase-with-revenuecat-funnels</guid>
      <pubDate>Wed, 09 Sep 2026 20:57:22 GMT</pubDate>
      <dc:creator><![CDATA[Courtney Touchstone]]></dc:creator>
      <category><![CDATA[Growth]]></category>
      <description><![CDATA[RevenueCat Funnels now offers smarter AI editing, ready-made bottom sheet and loader components, Firebase Authentication, and custom code to help turn campaign traffic into guided journeys that convert.]]></description>
      <enclosure url="https://cdn.sanity.io/images/c3qnx9b0/production/a22ed4fe1102834eb6056ec037d1ec5b883770ca-1600x800.png" length="0" type="image/*"/>
      <content:encoded><![CDATA[<p>Paid ads and creator partnerships introduce your app to potential customers before they reach an app store. A <a href="http://revenuecat.com/blog/growth/web-to-app-funnels/">web-to-app funnel</a> lets you continue that conversation with a journey built around the message that brought them there, optimized for conversion.</p>
<p>You can explain your app’s value, learn what each visitor wants, guide them toward a relevant offer, and let them purchase before opening the app.</p>
<p>For developers, the opportunity is straightforward: test whether a guided web journey can bring in more paying customers without adding a full web growth stack to your roadmap.</p>
<p><a href="https://www.revenuecat.com/docs/tools/funnels">RevenueCat Funnels</a> keeps that test focused. Build and host the journey in RevenueCat, connect it to the products and entitlements you already manage, and measure how campaign traffic moves from its first click toward purchase.</p>
<p>Several recent updates make it faster to get from an idea to a useful first test.</p>
<h2><strong>Get to a credible first design faster with AI</strong></h2>
<p>A blank screen creates work before it creates information. You need to choose a structure, write the copy, arrange the components, and make the result look like it belongs to your app.</p>
<p>RevenueCat’s AI Editor helps you get through that starting work faster. Describe the funnel screens you want in natural language, and the editor can create or modify it directly in the dashboard. You can also provide a screenshot or a design specification to steer the result.</p>
<p>The latest editor is better at:</p>
<ul>
<li>Creating polished starting points from open-ended prompts</li>
<li>Handling complex layout and content changes</li>
<li>Interpreting requests that don’t specify every design decision</li>
<li>Rewriting copy and adjusting typography, spacing, colors, and imagery</li>
<li>Adding, removing, and rearranging components</li>
</ul>
<p>You still control the result in the visual editor. AI gets you to a credible draft faster; you decide what’s ready to test.</p>
<p><a href="https://www.revenuecat.com/docs/tools/funnels/creating-funnels">Learn more about building and editing Funnels &gt;</a></p>
<h2><strong>Add familiar interactions without configuring them from scratch</strong></h2>
<p>A useful funnel often needs more than static text and a purchase button. It may need to compare plans, prepare a recommendation, or make a short processing step feel intentional.</p>
<p>RevenueCat Funnels now includes a dedicated bottom sheet component with a ready-to-customize ‘View all plans’ example. You can keep the main screen focused on a recommended offer while still letting visitors inspect the other available plans.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/f79cb327df2b9f2e0b19a78ac3f7abbc75cc3fa1-1145x1374.gif" alt="bottom sheet component"/></figure>
<p>New loader components help you create processing and recommendation screens with:</p>
<ul>
<li>Circular or linear progress animations</li>
<li>Live percentage indicators</li>
<li>Changing status messages</li>
<li>Multi-stage loading sequences</li>
<li>Components that appear at a specified point</li>
</ul>
<p>A timer-complete trigger can automatically move the visitor to the next step. Alternatively, you can reveal a button and let them continue when they’re ready.</p>
<figure><img src="https://cdn.sanity.io/images/c3qnx9b0/production/ec2cef13b2c39f6107a89a8eace07d18b04b18f8-400x795.gif" alt="loader component"/></figure>
<p>These additions reduce the amount of layout and interaction work required to make a funnel feel complete.</p>
<p><a href="https://www.revenuecat.com/docs/tools/funnels/creating-funnels#loader-components">See how loader components work &gt;</a></p>
<h2><strong>Use your existing Firebase Authentication setup</strong></h2>
<p>If your app uses Firebase Authentication, new customers can now create an account without leaving the funnel or passing through a separate login page. This gives you an email for lifecycle messaging if they don’t purchase and ensures customers who do purchase can sign in to the same account when they open your app.</p>
<p>Add an authentication step, connect your Firebase project, and choose between:</p>
<ul>
<li><strong>Google sign-in:</strong> visitors authenticate through a Google popup inside the funnel</li>
<li><strong>Email and password:</strong> visitors use a customizable login and sign-up screen, with optional email verification</li>
</ul>
<p>Firebase connections are saved at the RevenueCat project level and can be reused across all funnels.</p>
<p>Visitors who already have an authenticated session automatically advance through the step. Everyone else can create an account or sign in, then continue through the journey and arrive at checkout with a consistent identity.</p>
<p><a href="https://www.revenuecat.com/docs/tools/funnels/creating-funnels#using-firebase-authentication">Configure Firebase Authentication in RevenueCat Funnels &gt;</a></p>
<h2><strong>Create your own custom components</strong></h2>
<p>The standard components and AI editor will cover most first funnel experiments, but if you need to go further, the new custom component lets you (or your agent) build a component directly from web code.</p>
<p>You can package HTML, CSS, and JavaScript and place it inside a funnel screen. Custom components can support:</p>
<ul>
<li>Lottie or Rive animations</li>
<li>Interactive comparison tables</li>
<li>Accordions and custom layouts</li>
<li>Calculators, charts, or counters</li>
<li>Canvas and WebGL experiences</li>
<li>Other app-specific visuals and interactions</li>
</ul>
<p>Download the starter project, customize it yourself or give the folder to a coding agent, and upload the completed ZIP file. RevenueCat validates and hosts the bundle.</p>
<p>Packages, purchase buttons, and other purchase-critical elements remain native to RevenueCat. You use custom code for the part that differentiates the experience while keeping the surrounding journey inside the funnel editor.</p>
<p><a href="https://www.revenuecat.com/docs/tools/paywalls/creating-paywalls/components#custom-component">Learn how custom components work &gt;</a></p>
<h2><strong>Give campaign traffic a clearer path to purchase</strong></h2>
<p>Together, these updates make the time you spend in RevenueCat Funnels go further: AI gets you to a strong first draft, ready-made components cover common interactions, Firebase connects your existing authentication, and custom components let you extend the experience with your own HTML, CSS, and JavaScript.</p>
<p>If you’re already sending traffic from paid ads or creator partnerships, funnels give you a focused next step: turn those clicks into a guided journey from campaign promise to purchase.</p>
<p><a href="https://app.revenuecat.com/">Create your first funnel in RevenueCat &gt;</a></p>]]></content:encoded>
    </item>
  </channel>
</rss>