Skip to main content
A paywall is a flow with a purchase attached, so you build it in the normal editor. This page covers the parts that are specific to paywalls: the Offers blocks, binding plans to packages, dynamic pricing, the preview modes, and the extra publish checks.
Before you startPaywalls build on the standard FlowPilot setup, plus RevenueCat:
  • A FlowPilot workspace, an app inside it, and an SDK API key for that app.
  • A RevenueCat account with your products, entitlements, and an offering configured for your app.
  • Paywalls enabled for your workspace. If you do not see a Paywalls item in the dashboard sidebar, reach out and we will turn it on.

Create a paywall

1

Start a new paywall

Open the Paywalls section in the sidebar and choose New paywall. This creates a flow of type paywall, seeded with a starter layout and its monetization settings already turned on.
2

Set the offering

In the flow settings, under Monetization, pick which RevenueCat offering this paywall shows. The default is your current offering, so changing your featured offering in RevenueCat updates the paywall without a republish. You can also pin a specific offering by identifier.

Monetization settings

The paywall inspector holds the settings that make a flow behave like a paywall. You reach it from the flow settings panel.
SettingWhat it does
OfferingCurrent, or a pinned offering by identifier. Determines which packages are available.
Required entitlementThe entitlement this paywall unlocks (for example pro). Used for gating and to skip already-subscribed users.
Skip if entitledWhen on, the paywall is not shown to a user who already holds the required entitlement. On by default.
Gating modesoft (dismissible, continues on close), hard (closing leaves the feature locked, your app decides), or none (informational). Defaults to soft.
Close behaviorWhether to show the close control, and an optional delay before it appears.
Default planWhich package is pre-selected when the paywall opens.
Plan orderThe order your bound packages appear in.
Legal linksYour Terms and Privacy URLs, surfaced by the legal footer.
Restore copyThe message shown if a restore finds no purchase.

Add Offers blocks

Paywall-specific building blocks live in the editor’s Offers category. They are compositions of standard components, so they render on every SDK with no extra work, but they carry the purchase wiring for you.

Plan picker

A group of selectable plan cards that behaves like a single-select question. Add or remove plans, bind each to a package, set the order, and mark a best-value badge.

Plan option

A single selectable plan card: name, price, period, a trial line that shows only when the user is eligible, and a savings badge slot. Tapping it selects that package.

Purchase CTA

The primary button. It runs the purchase, shows a busy state while it is in flight, and closes the flow on success by default.

Restore

A text button that restores a previous purchase. App Review requires one somewhere in the flow.
There is also a Legal footer block that renders your Terms and Privacy links plus the auto-renewal disclosure, bound to the URLs in your monetization settings. Everything else on the screen, the headline, benefit list, hero image, animations, is normal content.

Bind plans to packages

Each plan card points at one package from your synced catalog. Select a plan card, open its controller, and pick the package. When you rebind a plan, FlowPilot rewrites its price bindings and its selection wiring in one step, and keeps the plan order in sync, so you do not chase references by hand.
TODO: screenshot of a plan option controller showing the package picker fed by the synced catalog.

Dynamic, store-truth pricing

Plan cards never contain a typed-in price. They read live values from the store through variables in the paywall namespace, so what a user sees is exactly what they will be charged, in their currency, after any eligible trial. The most useful values:
VariableMeaning
paywall.selected.priceLocalized price of the currently selected plan.
paywall.selected.periodTextLocalized billing period of the selected plan, for example month.
paywall.package.<id>.priceLocalized price of a specific package.
paywall.package.<id>.trialTextTrial text, for example 7 days free, empty when the user is not eligible.
paywall.productsStateloading, loaded, or failed, for skeleton and retry states.
paywall.purchaseStateDrives the CTA’s busy and disabled state during a purchase.
Use them in copy the same way you use any variable, wrapped in interpolation. For example, a CTA label of Continue for {{paywall.selected.price}} updates as the user switches plans.
Never type a currency symbol and a number into paywall copy. Apple requires the billed price to come from the store, and the publish validator blocks a paywall that hardcodes one. Bind to a paywall price variable instead.

Preview every state

Prices, trials, and purchase outcomes only exist at runtime, so the editor gives you a paywall preview to exercise them. Open the paywall preview control in the phone toolbar and switch between:
  • Products: loading, loaded, or failed to load.
  • Trial: eligible or not eligible.
  • Purchase result on tap: success, cancelled, pending, or failure.
  • Restore result: found or not found.
  • Entitlement: subscribed or not, to preview the skip-if-entitled path.
  • Locale: sample currencies including a right-to-left layout, to catch overflow and truncation.
Preview prices are drawn from your synced catalog and are clearly watermarked as simulated. They are never published and never shown to a user.

Publish

Publishing a paywall runs the normal publish flow plus a set of paywall checks. If any fail, publishing is blocked until you fix them. The main ones:
  • A purchase CTA is present.
  • A restore action exists somewhere in the flow.
  • Legal links (Terms and Privacy) are set.
  • No hardcoded prices anywhere in the paywall’s text.
  • Every bound package exists in your catalog and is not removed.
These checks exist so the paywall you ship is compliant by construction. See App Store review for the full compliance story.

Common mistakes

  • Typing a price into a headline or button. Bind to paywall.selected.price or a paywall.package.<id>.price variable so the store’s real, localized price shows.
  • Forgetting the restore button. App Review will reject a subscription paywall with no way to restore. Add the restore block.
  • Leaving trial copy unconditional. A “7 days free” line must be bound to the trial variable so it only appears for users who are actually eligible.
  • Not previewing the failed-products state. If products fail to load on device, your paywall still has to look right. Preview the loading and failed states.