Building a great product is a good starting point for success, but it's seldom enough to generate meaningful traction. There are many channels we can use for distribution: organic search, social media, paid advertising, and PR, among others.
However, if we find success with any of these channels, user acquisition costs usually get higher with time, and that can become a real issue if we're caught off-guard. Adding a referral system is a good way to acquire a portion of our users for a lower price.
In this article, we will learn how to add a referral system to a Rails app using the Refer gem to improve our chances of success.
Let's start by understanding why adding a referral system is desired. Jump to the application setup if you already know about them.
Why add a referral system
The main reason to add a referral system to any product is to reduce customer acquisition costs.
It doesn't matter what our main acquisition channel is, as we go through our product adoption lifecycle, the probability of customer acquisition getting more expensive is high.
Our early customers are more inclined to respond to our early marketing efforts than users who don't: they are already aware of the issues we solve, and they are maybe even aware of our product.
And, while a referral system doesn't solve the customer acquisition costs alone, it gives users an opportunity to recommend our product to people that might be interested while simultaneously earning a reward.
Considering this, a good referral program is:
- A virtuous cycle: users should preferably refer users that might actually benefit from our product.
- Attractive but low cost: the reward users get for referring other users should be attractive to them while at the same time not breaking the bank for us.
- Frictionless: every inch of friction we add to our referral program is going to worsen our results. We want to give users the ability to refer other users as simply as possible.
How does it work
For a referral system to work, we need to be able to track who referred whom reliably.
In a web application, the best way we have to achieve this is to use cookies, which are a semi-permanent, device-contained storage solution.
To achieve a referral, a user has to share a link that includes a given code with an acquaintance who visits our site. Because the link contains the referral code as a query parameter, we store that code in a cookie in the referred user's browser.

Then, as the referee has the referrer's code stored in the browser, after completing a signup our application reads the code from the cookies, looks for the referring user, and tracks the referral by persisting it to the database and associating it with the user who owns the code.

The last step of the process is to provide a reward to the referrer after the referee performs an action in our application.
This step is highly dependent on the business goals. Maybe we want to encourage account creation, or maybe we want to provide the reward after the referee purchases our product, starts a free trial, or does something else of interest for the business.
What we will build
For this tutorial, we will build upon AvoCasts, the fantasy application we built for the multistep forms with Wicked tutorial, by adding a referral system.
We will be using the Refer gem, and our system will have the following features:
- A user can refer other users by sharing a link.
- A successful referral is produced when a new user creates an account. Normally, we may want the referral to be successful after a transaction or business goal is produced.
- The referral should have an attribution window. If a set amount of time passes after a user was referred, we won't count the referral.
- A given user can only refer a limited amount of users. For our case, let's say it's 5.
- We will give a reward to both users only after the referred user completes an action.
- Users can access their referral stats in a dashboard within the application.
- Stakeholders can access referral stats in a dashboard.
- Referring should be frictionless: the referral view should include features to copy and share the referral codes. ## Application setup We will assume we have an existing application that we want to add a referral system to.
If you already have auth setup, skip this step. Otherwise, we will add it with the Rails auth generator:
bin/rails generate authentication
This will add two database-backed models: User
and Session
. We will add the referral associations to the user.
Next, let's add the refer gem to our Gemfile and install it:
bundle add refer && bundle install
With the gem installed, let's take a look at how it works and what it does behind the scenes:
The Refer gem
This command adds three migrations that add the following database tables:

The refer_referrals
table is responsible for keeping track of who referred whom: it associates a referrer with a referee. For our example application, they would both be users, but because the association is polymorphic, we could have a User
as the referrer and an Author
as the referee.
The refer_referral_codes
keeps track of the codes that the referrer has. Even though it's common for a user to have a single referral code, sometimes we need to have many to track the origin of the referral. For example, the user might want to see which channel performs better for a referral campaign.
The refer_visits
table holds information about the visits that had a code
query parameter. They can be useful to analyze why a specific code was successful or not.
The command also adds a has_referrals
call to the User
model, which is the same as including the Refer::Model
concern, which adds the following associations to the model where it's included:
has_many :referral_codes, as: :referrer, class_name: "Refer::ReferralCode", dependent: :destroy
has_many :referrals, as: :referrer, class_name: "Refer::Referral", dependent: :destroy
has_one :referral, as: :referee, class_name: "Refer::Referral", dependent: :destroy
delegate :referrer, to: :referral, allow_nil: true
Referral cookie
The method in charge of adding the referral cookie to the referee's browser is set_referral_cookie
.
This method is added to the ApplicationController
when installing the gem and is a wrapper around the set_refer_cookie
method in a before action:
class_methods do
def set_referral_cookie(param_name: Refer.param_name, cookie_name: Refer.cookie_name, **options)
before_action -> { set_refer_cookie(param_name: param_name, cookie_name: cookie_name) }, **options
end
end
We can customize the name of the parameter, which is set to code
by default, the name of the cookie
and whether we want to track the visits that are associated with the code:
def set_refer_cookie(param_name: Refer.param_name, cookie_name: Refer.cookie_name, code: nil, track_visit: Refer.track_visits)
code ||= params[param_name]
return if code.blank?
cookies[cookie_name] = Refer.cookie(code) if Refer.overwrite_cookie || cookies[cookie_name].blank?
ReferralCode.find_by(code: code)&.track_visit(request) if track_visit
end
Even if the logic is simple and doesn't execute anything if the parameter is not present or it's empty, we can move the set_referral_cookie
into a specific controller:
class PagesController < ApplicationController
set_referral_cookie
end
The cookie
method on the Refer
class is what gets stored in the cookie, and it returns something like this:
{:value=>"7byZBXjN", :expires=>2025-08-14 01:11:06.580137000 UTC +00:00}
The expiration date is set to 30 days after the method is executed, but we can customize it with the cookie_length
method:
# config/initializers/refer.rb
Refer.cookie_length = 15.days
If there's a code in the cookies, the method performs a query to try to find one and, if it does, it tracks a visit to the code.
We can also customize the code generation by modifying the code_generator
attribute with a lambda that receives our generator:
# config/initializers/refer.rb
Refer.code_generator = -> (referrer) { "#{referrer.id}-#{SecureRandom.hex(6)}".upcase } #=> "13-507B9DF87033"
By default, the generator uses SecureRandom.alphanumeric(8)
which is fine, but your application might need something different.
User referral
The actual user referral is performed by the Refer.refer
class method which receives a code and a user instance that represents the referee:
referee = User.last
code = "123456"
Refer.refer(code, referee) # Creates a Refer::Referral
This method finds the ReferralCode
associated with the code and creates a new Refer:Referral
which keeps track of the referrer and referee association like we explained above.
There's an alternative Refer.refer!
method that takes the same arguments but returns an AlreadyReferred
error if the referee user has already been referred by somebody else. This is especially useful if we're not creating the referral at the time of the user sign-up.
Now that we understand how the gem works, let's add a referral feature to our application:
Adding referrals
Let's start by running the gem's installation command which adds the previously mentioned set_referral_cookie
method to the ApplicationController
:
bin/rails generate refer:install
Then, we run the command to add referrals to our User
model:
bin/rails generate refer:model User
Next, we run the migrations to create all the tables:
bin/rails db:migrate
Now, let's add a callback to create a ReferralCode
on user creation:
class User < ApplicationRecord
after_create :create_referral_code
private
def create_referral_code
referral_codes.create
end
end
To keep our logic simple, we can also add a referral_code
method to the user, which is responsible for returning the latest code:
def referral_code
referral_code.last.try(:code)
end
If we're starting from an application that already has users, which is pretty probable, we can create a migration to add codes for existing users:
bin/rails generate migration create_referral_codes_for_existing_users
And add the following to the migration:
class CreateReferralCodesForExistingUsers < ActiveRecord::Migration[8.0]
def up
User.find_each do |user|
user.referral_codes.create unless user.referral_codes.any?
end
end
def down
User.find_each do |user|
user.referral_codes.destroy_all
end
end
end
Now, after running the migration, every user in our application should have an associated referral code that we can use.
Refer a friend view
This view is intended to educate users about our referral program and the steps involved with it.
Let's start by adding a route and a controller for this view:
get "refer-a-friend", to: "pages#refer_a_friend", as: :refer_a_friend
class PagesController < ApplicationController
def refer_a_friend
end
end
Then, we add the view code to show the steps for the referral process, a section where users can copy the referral link, and a section for users to send invitations by email.
For brevity purposes, we won't paste the whole view code, but you can check it in the AvoCasts repository. The result for the view looks like this:

The main goal for this view is to give our users a concise way to understand the referral process and make it easy for them to share their referral codes with their friends.
To avoid hardcoding the URL, let's add a helper method that receives a User
instance and produces the referral code:
module UsersHelper
def referral_link(user)
root_url(code: user.referral_code)
end
end
Next, to make the Send by email feature work, let's start by adding a route:
# config/routes.rb
resources :referral_emails, only: [:create]
Then, let's add the corresponding controller:
class ReferralEmailsController < ApplicationController
def create
emails = params[:emails].split(",")
emails.each do |email|
FriendReferralMailer.referral_email(current_user, email).deliver_later
end
end
end
Now, let's add the mailer:
class FriendReferralMailer < ApplicationMailer
def referral_email(referrer, referee_email)
@referrer = referrer
@referee_email = referee_email
mail(to: @referee_email, subject: "Hey, #{@user.name} wants you to know about AvoCasts")
end
end
The user dashboard
Stakeholder dashboard
Summary
Adding a referral system is a good way to lower customer acquisition costs while also providing value to referrers and new users.
Within the Rails world, keeping track of user referrals and who referred whom can be achieved using cookies and a couple of models.
However, solutions like the Refer gem make our lives easier by providing most of the work for us.