Get flows & paywalls - Capacitor

What getFlow retrieves
Flows Built in Flow builder — renders natively on device, no WebView
Paywall Builder paywalls All existing Paywall Builder content

After you designed your flow or Paywall Builder paywall, you can display it in your mobile app. The first step is to get the flow or paywall associated with the placement and its view configuration as described below.

Please be aware that this topic refers to flows and Paywall Builder-customized paywalls. If you are implementing your paywalls manually, please refer to the Fetch paywalls and products for remote config paywalls in your mobile app topic.

Want to see a real-world example of how Adapty SDK is integrated into a mobile app? Check out our sample apps, which demonstrate the full setup, including displaying paywalls, making purchases, and other basic functionality.

Before you start displaying flows and paywalls in your mobile app (click to expand)
  1. Create your products in the Adapty Dashboard.
  2. Create a flow/paywall and incorporate the products into it in the Adapty Dashboard.
  3. Create placements and incorporate your flow/paywall into it in the Adapty Dashboard.
  4. Install Adapty SDK in your mobile app.

Fetch flow/paywall

If you’ve designed a flow or paywall using the Flow Builder or Paywall Builder, you don’t need to worry about rendering it in your mobile app code to display it to the user. Such a flow or paywall contains both what should be shown within it and how it should be shown. Nevertheless, you need to get its ID via the placement, its view configuration, and then present it in your mobile app.

Fetch the flow or paywall and create its view as early as possible — ideally well before you present it. The createFlowView method loads the view configuration and starts downloading and caching its images in the background. The earlier you call it, the more time these downloads have to complete. By the time you present the flow or paywall, its configuration and images can already be cached and ready to display.

To get a flow or paywall, use the getFlow method:

try {
  const flow = await adapty.getFlow({
    placementId: 'YOUR_PLACEMENT_ID',
  });
  // the requested flow/paywall
} catch (error) {
  // handle the error
}

Parameters:

ParameterPresenceDescription
placementIdrequiredThe identifier of the desired Placement. This is the value you specified when creating a placement in the Adapty Dashboard.
fetchPolicydefault: 'reload_revalidating_cache_data'

Passed inside the optional params object. By default, SDK will try to load data from the server and will return cached data in case of failure. We recommend this variant because it ensures your users always get the most up-to-date data.

However, if you believe your users deal with unstable internet, consider using 'return_cache_data_else_load' to return cached data if it exists. In this scenario, users might not get the absolute latest data, but they’ll experience faster loading times, no matter how patchy their internet connection is. The cache is updated regularly, so it’s safe to use it during the session to avoid network requests.

Note that the cache remains intact upon restarting the app and is only cleared when the app is reinstalled or through manual cleanup.

Adapty SDK stores paywalls locally in two layers: regularly updated cache described above and fallback paywalls. We also use CDN to fetch paywalls faster and a stand-alone fallback server in case the CDN is unreachable. This system is designed to make sure you always get the latest version of your paywalls while ensuring reliability even in cases where internet connection is scarce.

loadTimeoutMsdefault: 5 sec

Passed inside the optional params object. This value limits the timeout for this method. If the timeout is reached, cached data or local fallback will be returned.

Note that in rare cases this method can timeout slightly later than specified in loadTimeoutMs, since the operation may consist of different requests under the hood.

Don’t hardcode product IDs. The only ID you should hardcode is the placement ID. Flows and paywalls are configured remotely, so the number of products and available offers can change at any time. Your app must handle these changes dynamically—if a paywall returns two products today and three tomorrow, display all of them without code changes.

Response parameters:

ParameterDescription
FlowAn AdaptyFlow object with the flow’s identifiers (id, variationId), name, placement, its paywall variations (paywalls), and any remote configs (remoteConfigs).

Fetch the view configuration

Make sure to enable the Show on device toggle in the builder. If this option isn’t turned on, the view configuration won’t be available to retrieve.

If the placement was designed in the Flow Builder or the Paywall Builder, Adapty renders the UI for you. Create the view with createFlowView, then present the flow or paywall. If the placement is a custom paywall with no Builder UI, handle it as a remote config paywall instead.

In Capacitor SDK, call createFlowView directly — you don’t need to fetch the view configuration first.

The result of the createFlowView method can only be used once. If you need to use it again, call the createFlowView method anew. Calling it twice without recreating may result in an error.

import { createFlowView } from '@adapty/capacitor';

try {
  const view = await createFlowView(flow);
} catch (error) {
  // handle the error
}

Parameters:

ParameterPresenceDescription
flowrequiredAn AdaptyFlow object to obtain a controller for the desired flow/paywall.
customTagsoptionalDefine a dictionary of custom tags and their resolved values. Custom tags serve as placeholders in the content, dynamically replaced with specific strings for personalized content within the flow/paywall. Refer to Custom tags in paywall builder topic for more details.
prefetchProductsoptionalEnable to optimize the display timing of products on the screen. When true AdaptyUI will automatically fetch the necessary products. Default: true.
android.enableSafeAreaoptionalAndroid only (ignored on iOS). Nested under the android key. When true, the flow view applies safe-area paddings. Default: true. The default is suitable for most cases.

If you are using multiple languages, learn how to add a flow localization and how to use locale codes correctly here.

Once you have the view, present the flow/paywall.

Get a flow or paywall for a default audience to fetch it faster

Typically, flows and paywalls are fetched almost instantly, so you don’t need to worry about speeding up this process. However, in cases where you have numerous audiences and placements, and your users have a weak internet connection, fetching a flow or paywall may take longer than you’d like. In such situations, you might want to display a default flow or paywall to ensure a smooth user experience rather than showing nothing at all.

To address this, you can use the getFlowForDefaultAudience method, which fetches the flow or paywall of the specified placement for the All Users audience. However, it’s crucial to understand that the recommended approach is to fetch the flow or paywall by the getFlow method, as detailed in the Fetch flow/paywall section above.

Why we recommend using getFlow

The getFlowForDefaultAudience method comes with a few significant drawbacks:

  • Potential backward compatibility issues: If you need to show different paywalls for different app versions (current and future), you may face challenges. You’ll either have to design paywalls that support the current (legacy) version or accept that users with the current (legacy) version might encounter issues with non-rendered paywalls.
  • Loss of targeting: All users will see the same paywall designed for the All Users audience, which means you lose personalized targeting (including based on countries, marketing attribution or your own custom attributes).

If you’re willing to accept these drawbacks to benefit from faster flow or paywall fetching, use the getFlowForDefaultAudience method as follows. Otherwise stick to getFlow described above.

try {
  const flow = await adapty.getFlowForDefaultAudience({
    placementId: 'YOUR_PLACEMENT_ID',
  });
  // the requested flow/paywall
} catch (error) {
  // handle the error
}
ParameterPresenceDescription
placementIdrequiredThe identifier of the Placement. This is the value you specified when creating a placement in your Adapty Dashboard.
fetchPolicydefault: 'reload_revalidating_cache_data'

Passed inside the optional params object. By default, SDK will try to load data from the server and will return cached data in case of failure. We recommend this variant because it ensures your users always get the most up-to-date data.

However, if you believe your users deal with unstable internet, consider using 'return_cache_data_else_load' to return cached data if it exists. In this scenario, users might not get the absolute latest data, but they’ll experience faster loading times, no matter how patchy their internet connection is. The cache is updated regularly, so it’s safe to use it during the session to avoid network requests.

Note that the cache remains intact upon restarting the app and is only cleared when the app is reinstalled or through manual cleanup.

Customize assets

To customize images and videos in your flow/paywall, implement the custom assets.

Hero images and videos have predefined IDs: hero_image and hero_video. In a custom asset bundle, you target these elements by their IDs and customize their behavior.

For other images and videos, you need to set a custom ID in the Adapty dashboard.

For example, you can:

  • Show a different image or video to some users.
  • Show a local preview image while a remote main image is loading.
  • Show a preview image before running a video.

Here’s an example of how you can provide custom assets via a simple dictionary:

import type { AdaptyCustomAsset } from '@adapty/capacitor';

const customAssets: Record<string, AdaptyCustomAsset> = {
  'custom_image': { type: 'image', relativeAssetPath: 'custom_image.png' },
  'hero_video': {
    type: 'video',
    fileLocation: {
      ios: { fileName: 'custom_video.mp4' },
      android: { relativeAssetPath: 'videos/custom_video.mp4' }
    }
  }
};

const view = await createFlowView(flow, { customAssets });

If an asset is not found, the flow/paywall will fall back to its default appearance.

After you designed the visual part for your paywall with the new Paywall Builder in the Adapty Dashboard, you can display it in your mobile app. The first step in this process is to get the paywall associated with the placement and its view configuration as described below.

Please be aware that this topic refers to Paywall Builder-customized paywalls. For guidance on fetching remote config paywalls, please refer to the Fetch paywalls and products for remote config paywalls in your mobile app topic.

Before you start displaying paywalls in your mobile app (click to expand)
  1. Create your products in the Adapty Dashboard.
  2. Create a paywall and incorporate the products into it in the Adapty Dashboard.
  3. Create placements and incorporate your paywall into it in the Adapty Dashboard.
  4. Install Adapty SDK in your mobile app.

Fetch paywall designed with Paywall Builder

If you’ve designed a paywall using the Paywall Builder, you don’t need to worry about rendering it in your mobile app code to display it to the user. Such a paywall contains both what should be shown within the paywall and how it should be shown. Nevertheless, you need to get its ID via the placement, its view configuration, and then present it in your mobile app.

To ensure optimal performance, it’s crucial to retrieve the paywall and its view configuration as early as possible, allowing sufficient time for images to download before presenting them to the user.

To get a paywall, use the getPaywall method:

try {
  const paywall = await adapty.getPaywall({
    placementId: 'YOUR_PLACEMENT_ID',
    locale: 'en',
  });
  // the requested paywall
} catch (error) {
  // handle the error
}

Parameters:

ParameterPresenceDescription
placementIdrequiredThe identifier of the desired Placement. This is the value you specified when creating a placement in the Adapty Dashboard.
locale

optional

default: en

The identifier of the paywall localization. This parameter is expected to be a language code composed of one or two subtags separated by the minus (-) character. The first subtag is for the language, the second one is for the region.

Example: en means English, pt-br represents the Brazilian Portuguese language.

See Localizations and locale codes for more information on locale codes and how we recommend using them.

paramsoptionalAdditional parameters for fetching the paywall.

Don’t hardcode product IDs. The only ID you should hardcode is the placement ID. Paywalls are configured remotely, so the number of products and available offers can change at any time. Your app must handle these changes dynamically—if a paywall returns two products today and three tomorrow, display all of them without code changes.

Response parameters:

ParameterDescription
PaywallAn AdaptyPaywall object with a list of product IDs, the paywall identifier, remote config, and several other properties.

Fetch the view configuration of paywall designed using Paywall Builder

Make sure to enable the Show on device toggle in the paywall builder. If this option isn’t turned on, the view configuration won’t be available to retrieve.

After fetching the paywall, check if it includes a ViewConfiguration, which indicates that it was created using Paywall Builder. This will guide you on how to display the paywall. If the ViewConfiguration is present, treat it as a Paywall Builder paywall; if not, handle it as a remote config paywall.

In Capacitor SDK, directly call the createPaywallView method without manually fetching the view configuration first.

The result of the createPaywallView method can only be used once. If you need to use it again, call the createPaywallView method anew.

import { adapty, createPaywallView } from '@adapty/capacitor';

if (paywall.hasViewConfiguration) {
  try {
    const view = await createPaywallView(paywall);
  } catch (error) {
    // handle the error
  }
} else {
  // use your custom logic
}

Parameters:

ParameterPresenceDescription
paywallrequiredAn AdaptyPaywall object to obtain a controller for the desired paywall.
customTagsoptionalDefine a dictionary of custom tags and their resolved values. Custom tags serve as placeholders in the paywall content, dynamically replaced with specific strings for personalized content within the paywall. Refer to Custom tags in paywall builder topic for more details.
prefetchProductsoptionalEnable to optimize the display timing of products on the screen. When true AdaptyUI will automatically fetch the necessary products. Default: false.

If you are using multiple languages, learn how to add a Paywall Builder localization and how to use locale codes correctly here.

Once you have the view, present the paywall.

Get a paywall for a default audience to fetch it faster

Typically, paywalls are fetched almost instantly, so you don’t need to worry about speeding up this process. However, in cases where you have numerous audiences and paywalls, and your users have a weak internet connection, fetching a paywall may take longer than you’d like. In such situations, you might want to display a default paywall to ensure a smooth user experience rather than showing no paywall at all.

To address this, you can use the getPaywallForDefaultAudience method, which fetches the paywall of the specified placement for the All Users audience. However, it’s crucial to understand that the recommended approach is to fetch the paywall by the getPaywall method, as detailed in the Fetch Paywall Information section above.

Why we recommend using getPaywall

The getPaywallForDefaultAudience method comes with a few significant drawbacks:

  • Potential backward compatibility issues: If you need to show different paywalls for different app versions (current and future), you’ll either have to design paywalls that support the current (legacy) version or accept that users with the current (legacy) version might encounter issues with non-rendered paywalls.
  • Loss of targeting: All users will see the same paywall designed for the All Users audience, which means you lose personalized targeting (including based on countries, marketing attribution or your own custom attributes).

If you’re willing to accept these drawbacks to benefit from faster paywall fetching, use the getPaywallForDefaultAudience method as follows. Otherwise stick to getPaywall described above.

try {
  const paywall = await adapty.getPaywallForDefaultAudience({
    placementId: 'YOUR_PLACEMENT_ID',
    locale: 'en',
  });
  // the requested paywall
} catch (error) {
  // handle the error
}
ParameterPresenceDescription
placementIdrequiredThe identifier of the Placement. This is the value you specified when creating a placement in your Adapty Dashboard.
locale

optional

default: en

The identifier of the paywall localization. This parameter is expected to be a language code composed of one or more subtags separated by the minus (-) character. The first subtag is for the language, the second one is for the region.

Example: en means English, pt-br represents the Brazilian Portuguese language.

See Localizations and locale codes for more information on locale codes and how we recommend using them.

paramsoptionalAdditional parameters for fetching the paywall.

Customize assets

To customize images and videos in your paywall, implement the custom assets.

Hero images and videos have predefined IDs: hero_image and hero_video. In a custom asset bundle, you target these elements by their IDs and customize their behavior.

For other images and videos, you need to set a custom ID in the Adapty dashboard.

For example, you can:

  • Show a different image or video to some users.
  • Show a local preview image while a remote main image is loading.
  • Show a preview image before running a video.

Here’s an example of how you can provide custom assets via a simple dictionary:

const customAssets: Record<string, AdaptyCustomAsset> = {
  'custom_image': { type: 'image', relativeAssetPath: 'custom_image.png' },
  'hero_video': {
    type: 'video',
    fileLocation: {
      ios: { fileName: 'custom_video.mp4' },
      android: { relativeAssetPath: 'videos/custom_video.mp4' }
    }
  }
};

view = await createPaywallView(paywall, { customAssets });

If an asset is not found, the paywall will fall back to its default appearance.