Where do your customers come from? Building a simple solution for marketing attribution

There are many different channels you can use to acquire new customers. If you want to understand which ones work for your business, you have to start from understanding where your customers come from.

Let’s start with a definition of a marketing channel: simply put it is some means of finding customers. It can be all sorts of things, your Twitter account, a blog, or an ad in a local newspaper.

A couple of weeks ago we started to run tests to validate different marketing channels. We wanted to have an understanding of how well each of those channels is doing, what’s bringing us a lot of customers, and what’s useless.

What do I need to track?

For each new account created in Probe, we want to know how this account found us. We decided to go with a simple text label indicating where the customer came from, for example, email_campaign_2020_09 or blog. We already have an Accounts table in our database to store account-related information. We can thus simply extend it by adding a new column named marketing_ref identifying the marketing channel the customer originated from.

How to track it?

There are 3 main steps we have to do:

  1. Adding marketing_ref to the URL
  2. Saving the marketing_ref value for the web page visitor
  3. Passing the marketing_ref value to the backend when a visitor creates a new trial account

Adding marketing_ref to the URL

When we post a link to our web page we want to add a marketing_ref parameter with a text label that will help us identify channels later on. For example – when creating Google Ads campaigns we would enter https://getprobe.io?marketing_ref=google_ads. This google_ads label tells us that a visitor came from clicking on our Ad.

Saving marketing_ref value for the web page visitor

Once a new visitor comes to our web page, we have to parse the URL, fetch the value of the marketing_ref parameter then store it. We have added a piece of Javascript code to our landing page that applies this logic to store the marketing_ref using localStorage.

Parsing the URL and returning the value of marketing_ref parameter:

# this code is using https://www.npmjs.com/package/query-string

function getMarketingRef() {
  if (window.location.search) {
    return queryString.parse(window.location.search).marketing_ref
  } else {
    return null

Storing marketing_ref in localStorage. This snippet of code will be run on onDocumentReady event.

const marketingRef = getMarketingRef()
if (marketingRef) {
  localStorage.setItem(‘marketing_ref’, getMarketingRef())

We decided not to overwrite the existing value if no marketing_ref is given. It is an important choice because it determines what happens when the same person visits your website for the second time with a different marketing_ref. Do we want to attribute the signup to the first source, or the latter? We decided that the first one wins.

There could be other solutions like remembering a list of sources that you keep, but that was too complicated for our case.

Passing the marketing_ref value to backend when a visitor creates a new trial account

When a trial is requested, marketing_ref value has to be passed to the backend. The backend creates a new trial account and, along with it, stores the marketing_ref value. The choice of how to associate the marketing_ref value to the account is more subtle as it depends on your database structure. In our case, we have a table called accounts and we simply store the marketing reference in it.

An important assumption here is that we have a one-to-one relationship between a channel and a customer. There is no “blending” of channels – it's either channel A or channel B that was responsible for bringing customers, not a mix.

Here is a snippet of JavaScript code from our web page that creates a new trial account.

# this code is using https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

fetch(‘https://getprobe.io/signup’, {
  method: ‘POST’,
  headers: {
    ‘Content-Type’: ‘application/json’
  body: JSON.stringify({
    email: signupEmail,
    passwords: password,
    marketing_ref: localStorage.getItem(‘marketingRef’)

When creating the customer in our backend system we expect the parameter marketing_ref passed from the landing page. Here is a snippet of Ruby code from our backend responsible for creating a new trial account.

def create
  Account.create!(email: params[:email], password: params[:password], marketing_ref: params[:marketing_ref])

  render json: {‘ok’: true}


Once you implement these 3 steps above you have built a simple marketing attribution model. You will have information about where your customers come from. The next step would be to calculate how much MRR each of these channels brings you. Based on that you can start making informed decisions on where to invest your marketing efforts to get the most out of it.

You might wonder what possible marketing channels are out there? This is a rather broad topic but luckily there are a few great books about it. One we like is Traction by DuckDuckGo founder Gabriel Weinberg. It’s a must-read to educate yourself not only on the available marketing channels and how you can use them but also on the framework you can implement to test many of them.

by Mike, co-founder of Probe Illustration by Icons 8