Paywall versioning
Swap paywall variants, target them to specific cohorts, and roll back the same day — no App Store or Play Store submission.
Use this when you want to test seasonal pricing, headline copy, or a new paywall layout without blocking on a release train. Don't use this when the variant needs new native UI that is not already shipped in the build; remote config cannot introduce new screens, only pick between ones that exist.
Goal
Paywall changes that live in app code are expensive to iterate on: two weeks of review, a rollback means another submission, and you cannot target a variant to a specific cohort. This recipe ships the paywall as a remote-selectable deeplink so the dashboard decides which variant a given user sees and when.
Setup
In the dashboard
Create one campaign per variant:
paywall-control,paywall-winter-offer,paywall-long-copy.Trigger: session start, or on a custom event like
PaywallRequestedfired from a feature gate.Target audience: the free cohort (
subscription_status != "pro").Split variants by audience filter rather than a percentage split — for example, send
paywall-winter-offerto users withcountryin["US","CA"],paywall-long-copyto users withinstall_dateafter a given date, and leavepaywall-controlas the broadest-audience fallback that catches every free user not matched by a narrower variant.Action: fire the deeplink for that variant (
yourapp://paywall/control,yourapp://paywall/winter,yourapp://paywall/long-copy).To retire a seasonal variant, move its campaign state to
Cancelon the planned end date. The change propagates on the next session. UseDraftstate to stage a variant that is not yet live.
In the app (engineering hand-off)
Ship every variant screen in the build before you promote the campaign. Remote config selects between them; it cannot create them.
Register a deeplink handler for
yourapp://paywall/<variant>that resolves<variant>to the matching view controller.Track the outcome so variants are comparable:
await Amply.track({ name: 'PaywallViewed', properties: { variant: 'winter' }}); await Amply.track({ name: 'PurchaseCompleted', properties: { variant: 'winter', product: sku }});
How it runs
User opens the app. Session starts.
Amply evaluates the paywall campaigns. The user's
countryis"US", so the narrowerpaywall-winter-offeraudience matches; the broaderpaywall-controlaudience would also match but is only selected when no narrower variant does.Amply fires
yourapp://paywall/winter. The app presents the winter variant.App tracks
PaywallViewedwithvariant: "winter". If the user converts,PurchaseCompletedcarries the same variant tag.The growth team compares conversion across
variantvalues in their analytics tool.When the winter window closes, the operator moves that campaign to
Cancel. Amply stops firing it. Users fall back to the control variant.
Metrics to watch
View-to-purchase rate per variant. This is the headline number.
Average revenue per paying user per variant — protects against a variant that converts more people into a worse plan.
Trial-start rate per variant, if the product has a trial.
Refund and cancel rate per variant over the following 30 days. A variant that converts well but refunds high is a net loss.
Related
Campaigns — the campaign types used here
Testing and rollout — using Draft state and instant rollback
Campaign delivery — how session-start and event-triggered delivery differ
Tracking events — tagging purchase events with the variant
Post-trial recovery — a related recipe that also swaps paywalls by context
AI-assisted integration — describe this campaign in plain language and have your AI assistant build it
Last updated