Guides

Gaining Insights and Driving Personalization with Flows

This page describes the process to build a personalized user journey based on user insights collected inside that journey

In today’s saturated app market, the first few moments of a user's journey are make-or-break. Personalizing onboarding isn’t just a best practice - it’s a competitive necessity. By asking questions, segmenting users based on their answers, and aligning the experience with their goals using the Jobs-To-Be-Done (JTBD) framework, product teams can deeply connect with users' intent from the start.

Instead of guiding everyone through a one-size-fits-all journey, a personalized flow adapts the experience: showing the right value proposition, surfacing the right features, and triggering the right messages at the right time. This not only boosts activation and engagement but also dramatically increases conversion into paying, loyal subscribers.

74% of users expect apps to be personalized to their needs and interests.

Creating personalized sequences of Screens and onboarding, that collects user insights and surface their jobs to be done is a key element to engage them, convert them into loyal subscribers and retain them.

This is precisely the role Flows are designed to fulfill.

Flows empower marketers and product teams to craft modular, dynamic onboarding and engagement journeys -without writing code. With visual control over decision trees, contextual screens, and logic-driven paths, Flows make it easy to:

  • Craft entire sequences of Screens designed with the Purchasely Screen Composer
  • Ask relevant questions (e.g., goals, motivations, usage preferences),
  • Personalize content and screens in real time,
  • Align messaging with user intent and JTBD insights,
  • Optimize conversion paths and continuously test variants.

The result? A tailored native onboarding experience that feels smart, and deeply relevant - accelerating time-to-value and reducing churn from day one.


Build sequences of Screens with Flows

The Flows feature is part of an advanced module that may not be included in your current Purchasely plan.

If you're interested in enabling this feature, please contact your Customer Success Manager. We’ll be happy to walk you through the upgrade options and help you get started.

To leverage Flows, your app must be running SDK version v5.3.0 or later.


General overlook

Here is an example of a personalized user journey built for Headspace a leading app in meditation and well-being.

This flow was imagined and designed by [Irrational Labs](https://irrationallabs.com/) - the leading behavioral science consultancy for designing better choices and aims to increase the user engagement during the free trial and the conversion to paid, by collecting user insights, personalizing the user journey and recommending the relevant contents for each user

This flow was imagined and designed by Irrational Labs - the leading behavioral science consultancy for designing better choices and aims to increase the user engagement during the free trial and the conversion to paid, by collecting user insights, personalizing the user journey and recommending the relevant contents for each user

This Headspace Flow consists of a series of carefully crafted questions. The user’s responses are used to personalize their experience by assigning them to a profile that reflects their goals, preferences, or challenges, allowing the app to recommend content that best matches their specific needs

This Headspace Flow consists of a series of carefully crafted questions. The user’s responses are used to personalize their experience by assigning them to a profile that reflects their goals, preferences, or challenges, allowing the app to recommend content that best matches their specific needs


How to build a Flow?

Let’s explore how to build a Flow with Purchasely.

The Flow Composer lets you:

  1. drag and drop Screens built with the Purchasely Screen Composer,
  2. organize them inside a canvas
  3. link them together with Transitions

Transitions allow you to link a component from a source Screen to the next Screen in the Flow. They can be defined for any interactive component - such as buttons, links, pickers or call-to-action elements - as long as they have an active action (i.e., an action other than “none” or "close"). These transitions allow you to override the default action configured at the Screen level. This makes it possible to reuse the same Screen across multiple Flows while customizing behavior as needed, significantly reducing duplication and improving maintainability.

The components names that appear in the cartridges next to the Screen in the Flow Composer are the names defined for each component in the Screen structure (in the left column of the Screen Composer).

You can rename a component by double-clicking on it in the Screen structure.

You can remap both the main action and the second action of a component (including the Close button of the Screen) but the action associated in the Screen composer needs to be different from "none" or "close"

Both the Main action and the Second action of a component can be re-mapped / overridden with a transition only if they are associated with one of the following values:

  • Open Screen
  • Open Placement
  • Deeplink
  • Web Page

For the sake of clarity, if they are associated any other action value than the ones listed above, they are ignored.

🚧

I can't see a cartridge matching my Screen component

When drag & droping a Screen into the Flow canvas, if you don't see the cartridge corresponding to one of the Screen Components, it's probably because the component isn't mapped with one of the authorized actions.

In this case, simply edit the Screen by clicking on its name in the Flow canvas, then map the component with a compatible action (Open Screen, Open Placement, Web Page or Deeplink) and save.

A Transition can be associated with a "Type",

The transition type can take the following values:

  • Push
  • Modal
  • Drawer
  • Pop-in
  • Full screen

Here is how each Transition type looks like:

For drawers and popin, you can set the desired height:

To ensure a seamless user experience, the Flow Composer associate the background color of the destination Screen to the Transition (for both light mode and dark mode), but you can change that color if needed.


Integrating Flows into your app

Once created, Flows can be associated with a Placement, an A/B Test or a Campaign.

Mapping a Flow with a Placement


Associating a Flow with a Campaign

The Campaigns feature lets you create powerful no-code automations that will display a Purchasely Screen for a particular Audience at the App start.

Flows can be associated with Campaigns to combine both feature: the ability to display a customizable sequence of Screen following the app start.

This integration will automatically take the Display mode into consideration.

Running an A/B Test between 2 Flows

You can configure an A/B Test between 2 Flows or 1 Flow vs a single Screen.


UI / SDK events & Server events (e.g.: SUBSCRIPTION_STARTED, TRIAL_STARTED, TRIAL_CONVERTED and all subsequent lifecycle events such asSUBSCRIPTION_RENEWED, RENEWAL_DEACTIVATED, SUBSCRIPTION_TERMINATED) generated through a Flow will cary the flow_id .


Implementing Flows into your app

There are several ways to display a Flow inside of the app:

  1. Pre-fetching the Flow and using the display() method
  2. Manually integrating the Flow inside of your app
  3. Using the Flow deeplink

1. Prefetching the Flow and using the display() method

Inside of the app, Flows associated with a Placement can be pre-fetched (more information on pre-fetching) and then showed to the user using the display() method (new method coming along with the v5.3.0 of the SDK)

Purchasely.fetchPresentation(for: "onboarding", fetchCompletion: { presentation, error in
      guard error == nil,
            let presentation = presentation else { return }
                                                                  
     // Calling display() to launch the flow
		 // Source UIViewController is optional 
     presentation.display(from: myUIViewController) 
})
Purchasely.fetchPresentation(placementId = "onboarding") { presentation, error ->
  if(error != null) {
    Log.d("Purchasely", "Error fetching Screen", error)
    return@fetchPresentation
  }
  
  // Calling display() to launch the flow 
  presentation?.display(context) ?: run {
    Toast.makeText(context, "Failed to display Screen", Toast.LENGTH_SHORT).show()
    return@fetchPresentation
  }

The main benefit of this method is that it is simple and automatically takes the Display Mode configured to the Flow into consideration for displaying the first Screen of the Flow.

The display mode defines how the first Screen of the Flow should open

The display mode defines how the first Screen of the Flow should open

Here is how they look like:

Notes:

  • The Display mode Push is only available on iOS, if the parent view already contains a navigation bar.
    On Android or without a navigation bar, the Display mode will fallback to Modal

📘

Difference between the Flow display mode and the Screen transition type

When configuring a Flow, it’s important to distinguish between the Flow Display Mode and the Transition Type. Each plays a distinct role in how screens appear within your app.

Flow Display Mode

The Display Mode determines how the first Screen of the Flow is presented within the app.

It defines the integration behavior between the Flow and the parent view managed by your app—such as whether the Flow is shown as a modal, fullscreen, embedded view, etc.

This setting is configured at the Flow level and applies only to how the Flow starts.

Screen Transition Type

The Transition Type defines how navigation occurs between Screens within the Flow.

It controls the animation or visual behavior when moving from one Screen to the next (e.g., slide, fade, instant).

You can configure a default Transition Type at the Flow level, and optionally override it for individual Transitions to customize specific paths in the user journey.


2. Manually integrating the Flow inside of your app (Only on Android)

If you want to integrate the Flow manually into your app / parent view, you should check the attribute displayMode carried by the PLYPresentation returned by the pre-fetching

Purchasely.fetchPresentation(placementId = "onboarding") { presentation, error ->
  error?.let {
    Toast.makeText(context, "Error fetching presentation", Toast.LENGTH_SHORT).show()
    return@fetchPresentation
  }

  presentation?.let {
    val fragment = it.getFragment()

    when(it.displayMode?.type) {
      PLYTransitionType.PUSH -> {
        // Handle push transition
        Log.d("Purchasely", "Display it as a push transition")
      }
      PLYTransitionType.FULLSCREEN -> {
        // Handle pop transition
        Log.d("Purchasely", "Display it as a fullscreen transition")
      }
      PLYTransitionType.MODAL -> {
        // Handle modal transition
        Log.d("Purchasely", "Display it as a modal transition")
      }
      PLYTransitionType.DRAWER -> {
        // Handle drawer transition
        val heightPercentage = it.displayMode?.heightPercentage
        Log.d("Purchasely", "Display it as a drawer transition with height percentage: $heightPercentage")
      }
      PLYTransitionType.POPIN -> {
        // Handle pop-in transition
        val heightPercentage = it.displayMode?.heightPercentage
        Log.d("Purchasely", "Display it as a pop-in transition with height percentage: $heightPercentage")
      }
      null -> {
        // Handle no transition
      }
    }
  } ?: run {
    Toast.makeText(context, "Presentation is null", Toast.LENGTH_SHORT).show()
    return@fetchPresentation
  }
}

3. Using the Flow deeplink

To use this method, you need to have implemented Deeplink management

You can trigger the display of a Flow using its deeplink and the UIHandler


extension myClass: PLYUIHandler {
    func display(presentation: PLYPresentation, from sourceController: UIViewController?, proceed: @escaping () -> ()) {
      presentation.display(from: nil)
			// calling proceed() would also work
    }
}
Purchasely.uiHandler = object : PLYUIHandler {
  override fun onPresentation(presentation: PLYPresentation, proceed: () -> Unit) {
    // Display the flow
    presentation.display(context)
  }
}

In this case, the Display mode is automatically taken into consideration.

Going further

Flows can be personalized based on user insights.

📚 Follow the guide to learn more: Tailoring Flows to the user insights


What’s Next

In the next section, we will explain how to tailor a Flow to the user insights