How We Scale our Onboarding

Before being able to explore the Lunar Way universe, newcomers have to sign up in our app. In many cases signup flows are fairly simple; ask for a name, an email and there you go — you are ready to start. In the world of banking, however, a signup flow is a different kind of beast.

In this blog post we will shed light on some of the challenges of signing users up at scale. We will also walk you through how we developed a fully dynamic signup flow that complies with KYC regulations in multiple countries, while enabling A/B-testing and rapid scaling.

So let’s get started.

The Challenges

When we launched the first beta of Lunar Way in early 2016, we collected only a few pieces of information during our signup process — phone number, email address, photo ID and PIN code. While this was sufficient to go live with our PoC, we are a company operating in the financial sector, and hence subject to KYC- and AML-regulations that require us to collect and verify a significant amount of information from our users.

Initially, we went live with a single product for personal users in Denmark, but in 2018 we launched our business product. Different KYC- and AML-information had to be be collected for personal- and business users, which introduced more complexity into our signup flow.

Further complicating the process, we went live in Sweden a few months ago and Norway just last week. Due to differences in our product across the markets we operate in, varying requirements apply from market to market (as well as different regulations).

An example of a permutation of screens and sub-screens in the Danish sign up flow

While collecting the right information from users can be complicated all by itself, an equally challenging aspect is to ensure that the information they provide is sufficient and compliant.

Early on we learned that this is not easy — at all!

Every aspect of a signup flow — from the wording on each screen, to poor camera quality of devices — have caused our aspiring new users to submit insufficient or noncompliant information.

The ability for us to assist users when this happens is crucial for a successful onboarding experience and hence a way to reach out to and re-collect that information, is very important.

Finally, as a data-driven company, short feedback loops and the ability to perform experiments real-time are essential for us in order to create the best possible product. Thus, conducting experiments with ways to minimize drop-off during our signup process quickly became a priority.

To summarise, we faced the following challenges with our initial approach:

  • Inflexible design not well suited for scaling
  • Risk of losing customers while recollecting insufficient or noncompliant information
  • No support for A/B testing or experiments

Our Solution

When dealing with a complex problem it sometimes helps to take a step back and focus on the core of the problem. After numerous of whiteboard sessions with little to no progress, this is exactly what we did and it helped us realise that a signup flow, conceptually and regardless of context, is pretty straightforward:

  • Collect some data
  • Post it to a backend
  • Validate data
  • React appropriately to the response

With this realisation in mind, we concluded that this is exactly what we needed to do: Collect information and post it to our servers.

Our solution to the core problem turned out to be equally simple:

  • Let the backend dictate which steps to present to the user
  • Collect information for each step
  • Post the collected information to the backend
  • React appropriately

We dubbed this series of actions a “Flow”, derived from the dictionary’s definition of the term:

“Flow: a steady, continuous stream or supply of something”

Where “something” in our context refers to the information that must be collected.

At Lunar Way we truly believe that embracing a native mobile experience enables us to create the best possible product. Motivated by that belief, embedding webviews into our app or redirecting potential users to a web signup was not a path we were willing to take. However, moving as much logic as possible from our clients (i.e. apps) to our backend was key in this design.

Flow Architecture

A Flow is a list of sections, which each contain a list of steps:

  • Step (screen): Defines some input (or information) to collect from (or show to) the user. Each step is represented by a unique identifier.
  • Section: A list of related/coherent steps that can be grouped together.
  • Flow: A list of sections with information to collect
    "flowConfig": {
      "id": "abcd1234",
      "sections": [
          "screens": [
              "id": terms",
              "data": [
                "key": "privacyPolicyUrl", 
                "value": ""
              "id": "phoneNumber",
              "data": [
                "key": "countryCode", 
                "value": "+45"

A simplified version of a flow configuration model ☝️

In addition to the core parts, the backend provides each step with its own key-value store with the information required to display the screen. E.g., input validation regexes, links to documents to show or simply the text to show. This makes the Flow extremely dynamic in the sense that the backend manages the composition of every single screen. The clients merely show and collect the information they instructed asked to.

Furthermore, steps have no conception of the context in which they are shown — e.g., preceding or forthcoming steps. This allows us to mutate the order of which the steps are presented and even which exact steps to present to a given user.

This provides a solid mechanism to compose and conduct experiments, gather feedback, gain insights and implement changes during production.

The only tasks the clients have to accomplish is to map each step identifier to appropriate UI (i.e., view controllers and views on iOS and Android, respectively), collect data and post it to the backend.

How It Works

Putting all the pieces together, this is how our signup flow works today:

  • A client asks for a given Flow (eg Swedish personal or Danish business signup)
  • The Flow is presented and data is collected
  • The collected data is posted to our backend

In response, the client receives a new Flow reflecting the result of the data validation. Examples of responses could be:

  • A waiting state Flow (e.g., a single step with a screen showing “_your application is being processed_ob”)
  • A completion Flow (e.g., a few steps with welcome texts, getting started guides etc)
  • A “redo” Flow (i.e., a Flow with only the steps that failed validation and must be performed again)

One iteration of a flow: In response to posting the collected data, a new flow is returned.

In fact, the response doesn’t matter because the clients will simply display whichever Flow they receive in response. Only once all validation is passed the client will allow the new user to enter the app and get started.

Conclusion — Finding the Right Tool for the Job

We began this blog post by describing a number of challenges we had faced with signing up our users:

  • Our initial approach did not scale (across countries, products, different requirements etc)
  • High risk of losing customers who provide insufficient or noncompliant information
  • No support for experiments or A/B-testing, thereby leaving us in the dark

With our Flow, adding a new screen is as simple as mapping a new identifier to some UI to be presented. Individual screens (such as email, phone number etc) can easily be reused between flows. Showing different flows for different users, products or countries is controlled entirely by the backend and requires (in theory) no app development. This means that we can compose permutations of flows for any context, which makes it very simple to introduce a new signup flow — e.g., for a new country or product.

Posting a Flow returns a new Flow in response for the client to present, which means that we can keep collecting subsets of information recursively, until all the provided input passes validation. Validation can happen synchronously as well as asynchronously (e.g., when involvement from a human being is required). This, for instance, allows us to ask people to provide very specific (missing or insufficient) information instead of requiring them to sign up from scratch.

Since the clients have no knowledge of what they are collecting, nor in which order they are collecting it, the backend is in charge. This provides a simple mechanism for A/B testing and for building experiments with everything from the wording on a particular step to which particular steps to show (and in which order).

When scaling even the simplest tasks, complexity sneaks up on you and before we knew it, we had troubles taming the beast called signing up.

By introducing the Flow, we managed to solve the issues we experienced while allowing rapid scale, optimizing conversion and enabling experiments. Although our Flow system is significantly more complex than the original solution, it quickly turned out to be the right tool for the job.

What is Lunar Way?

Lunar Way is a fintech company motivated by rethinking the experience of banking, and the way people perceive money and spending in general. That is why we are using the most innovative and smart technology in order to create the banking solution for tomorrow directly in our app. Read more on

comments powered by Disqus