> For the complete documentation index, see [llms.txt](https://docs.amply.tools/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amply.tools/recipes/paywall-versioning.md).

# 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 `PaywallRequested` fired 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-offer` to users with `country` in `["US","CA"]`, `paywall-long-copy` to users with `install_date` after a given date, and leave `paywall-control` as 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 `Cancel` on the planned end date. The change propagates on the next session. Use `Draft` state 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:

  ```typescript
  await Amply.track({ name: 'PaywallViewed', properties: { variant: 'winter' }});
  await Amply.track({ name: 'PurchaseCompleted', properties: { variant: 'winter', product: sku }});
  ```

## How it runs

1. User opens the app. Session starts.
2. Amply evaluates the paywall campaigns. The user's `country` is `"US"`, so the narrower `paywall-winter-offer` audience matches; the broader `paywall-control` audience would also match but is only selected when no narrower variant does.
3. Amply fires `yourapp://paywall/winter`. The app presents the winter variant.
4. App tracks `PaywallViewed` with `variant: "winter"`. If the user converts, `PurchaseCompleted` carries the same variant tag.
5. The growth team compares conversion across `variant` values in their analytics tool.
6. 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](/user-guide/campaigns.md) — the campaign types used here
* [Testing and rollout](/user-guide/testing-and-rollout.md) — using Draft state and instant rollback
* [Campaign delivery](/concepts/campaign-delivery.md) — how session-start and event-triggered delivery differ
* [Tracking events](/developer-guide/tracking-events.md) — tagging purchase events with the variant
* [Post-trial recovery](/recipes/post-trial-recovery.md) — a related recipe that also swaps paywalls by context
* [AI-assisted integration](/getting-started/ai-assisted-integration.md) — describe this campaign in plain language and have your AI assistant build it
