One of the most effective means of growing an audience is through email referrals. The rise of newsletters like Morning Brew and companies like Harry’s showcase that offering a tiered rewards structure for referring friends can grow a list to astronomical numbers. That is, provided your newsletter or product is actually something people want. The following describes how you can build a simple version of this program yourself using Ruby on Rails API.
At a high level, subscribers will be added to the database when a subscriber is successfully subscribed to our mailchimp list. This allows us to check these against our existing subscribers as well as control sends through our unsubscribe list as well.
Now the call to mailchimp could be done inside our Rails API controller or a model hook, or it could live on our web server and we can make the call to our referral program API in a promise when the mailchimp subscription succeeds. I opted for the latter approach for two reasons:
Upon signing up, subscribers are issued a
user_key is a unique key where a subscriber can check their progress toward a reward and find their unique share link that will allow them to receive attribution for referrals. The
share_key is a unique key that gets appended to a subscribers’ referral url that they will share with friends.
share_key of another user is seen in a new subscription that will queue up a validation email. When the link in that validation email is clicked (ensuring it is a real email address) that referral will get attributed to the referee.
When a subscribers referral count reaches five, they are sent an email letting them know they have won a reward and to add their address to have it sent to them. When that subscriber adds their address this triggers an alert to the team to fulfill their reward.
When the reward is fulfilled that user is notified via email that their prize has shipped.
Now for those of you thinking, but what if they have two email addresses?You could just make five unique email addresses to get the prize! This is where business sense has to step in and say, if you are trying to give away a Ferrari, this is not the program for you. If you want a coffee mug or hoodie so bad that you will go through the process of creating five email addresses and referring them, then that is a risk we were willing to accept and reward. In general it’s hard enough to get people to open your newsletters, no less go through all the hoops of gaming the system.
Without further ado let’s get going…
First spin up a rails app in API only mode.
rails new referral_program_api --api
To learn more about why I like using rails for this type of thing check out their documentation: Using Rails for API-only Applications — Ruby on Rails Guides.
And that’s it we now have our API ready to go.
For this we only need to models, one for Subscribers the other for Referrals.
Let’s start by creating the subscribers. We can do so with our rails generator commands.
rails g model subscriber
You can also add all of the attributes to this command as well, however I find it a bit easier to add them to the migration file afterwards, but that’s just a personal preference. So head on over to your
/db/migrate/<timestamp>_create_subscribers.rb file and create the following attributes.
Now we can hop over to our
/app/models/subscriber.rb file where we can setup some rules and callbacks to generate our unique keys for subscribers.
The first thing we are doing here is converting all email addresses to lowercase and then setting up our association to the referrals model with the line
has_many :referrals.... This is called a self join and will allow us to associate our referrals to a subscriber all in one table.
We then create a few simple validations for
We then use the
before_create callback to assign a unique
share_key and user_key to the subscriber. These are generated using a private function that makes sure that each of these keys are unique independently. The likelihood that two keys are the same using the
SecureRandom.urlsafe_base64(9) function is highly unlikely, but as a safeguard we do this in a loop and in the case that the key is already used we generate another.
Now that we have a solid subscriber model we can go ahead and create our controller.
Now that we have the proper model in place we can setup our endpoints and controller.
Run the command:
rails g controller subscriber
Now head over to /app/controllers/subscriber_controller.rb and we can create actions for
| Action | Description |
| Show | For fetching the subscriber information along with associated referrals. |
| Create | For creating a new subscriber, can take in a
referrer_key parameter, which triggers a confirmation email to the subscriber to confirm the email is truly valid. |
| Confirm | For confirming an email address and giving credit to the referrer via
referrer_id field. |
| Update | For updating the subscriber, used for when prize is won for entering address, triggering notifications to the fulfillment team. |
We are not going to be using a delete action here. In general we flag users as unsubscribed in email but we like to never forget them. We don’t have to create any unsubscribe flags in this program since this is still going to be managed by mailchimp and all subscribes and referrals will be passing through their management system before ever hitting our API.
We want to be sure to only allow the parameters we have set forth at this stage. You’ll notice that we are not allowing parameters that are not going to be updatable by the user and we have included one additional parameter of
referrer_key, which will have the
share_key of the referring subscriber if this is a referral.
It’s important to remember that all of our subscribers, whether they are referred to us or not need to pass through the referral program to be properly assigned a
Here we’re going to be adding a few more private function to our controller in order to set the subscriber and referrer prior to our actions. I’m using ... for brevity here, this builds upon the boilerplate laid out above.
These functions set our subscriber and referrer. We are also adding some
before_action hooks here to set these instance variables in the functions we need them in.
Now that we have our base actions setup we can head over to
/config/routes.rb and define our routes. I like to setup the actions as follows:
:id field in this case is going to be our
user_key so as users can only be looked up by their unique key that will only be sent to their personal emails. Again, we are going to take privacy seriously here and even if a user key was somehow compromised the only thing that we are going to return is the users’ email address, user key, and share key. Sensitive information like address and name are going to be used only or our internal administrators and do not need to be accessible at any endpoint.
Now let’s fill in our actions.
Here, we’ve filled in our controllers except for three key notification points.
To complete these we will need to setup some mailers in a later section.
We also add two more private functions here to
get_referral_count of a referee, and
has_complete_address? to see if a subscriber has entered a complete address.
At this point we have to setup our connections with mailchimp in two places.
share_keyback to mailchimp as merge_vars.
If you do not want to use mailchimp or gibbon you may skip the next section and integrate whichever email service provider and transactional emails service you are comfortable with.
I find Gibbon to be the best gem for interacting with mailchimp via a rails application. We’ll also be using the figaro gem to allow for environment variables so we don’t commit our mailchimp api keys to source code.
Add the following to your
application.yml file in your
Then head over to Mailchimp and get your API key and list id. Be advised these are in two different locations inside of Mailchimp. I also like to setup a variable used to signal that we are in the development or staging environment for testing purposes.
gibbon.rb file inside
Inside this file you will setup the connection to Mailchimp via the Gibbon gem when the rails server starts. Hence, being in the initializers folder.
At this point you can fire up your rails server and you should see the above line printed to the console letting you know our connection to Mailchimp is up and running.
We can now add Gibbon to our subscriber model callbacks in order to send the
user_key back to mailchimp. But first, we have to allow for these in our subscriber list.
If you go to your mailchimp audiences tab and select the list we used for the list id above you can click settings and select Audience fields and |MERGE| tags. Here you will be able to add two new fields with the labels Share Key and User Key and the tag names
USERKEY to be used inside templates.
We can now use gibbon to send over the proper values in the
The key thing here is that if our connection to mailchimp fails for whatever reason, we are still subscribing the user to be corrected later. Inside of the
rescue statement a pro tip would be to put your preferred error alerting system to alert you of an issue sending the keys over to mailchimp without affecting the user experience.
Next we can setup mandrill, mailchimp’s transactional email service. I’m not going to go into how to set this up on the mailchimp side in this tutorial but mailchimp has great help docs and it takes about five minutes to connect mandrill to you mailchimp account.
Inside you rails app you are going to add the
mandrill-api package to your Gemfile.
Pull up your
/config/application.yml file again and add the following environment variables.
The password and username can be obtained inside of mandrill for use in this file.
Next we will create our
base_mandrill_mailer that will setup all of our default values for our notification emails to inherit.
And add the following:
Be sure to fill in your from and reply to email values above.
Now we can create two mailers, one for confirmation emails the other for rewards notifications.
touch ./mailers/confirmation_mailer.rb ./mailers/reward_mailer.rb
You’ll notice that these mailers reference some templates that are not in mandrill yet. You are correct. What we have to do next is go over to mailchimp and create four templates inside their editor named the following:
Notify Fulfillment Team
I like to keep them simple and inside each of these templates you need to use your
merge_vars to create links to your front end that will pass the proper keys to your application.
We’ll just be focusing on the API layer since use of merge vars and how this interacts with the front end of the site can vary based upon configurations.
At last we can add our email notifications to our controller actions.
Now we can head back into our subscriber controller and add the following notifications.
We now have all that we need to fire up the program. There’s just one mailer that we created that we did not use. That’s the one notifying the user that their order has shipped. For brevity I decided to leave out how you want to manage that since that can vary by what you want to do.
What I decided to do is send the orders over to Airtable and have a field that marks them as pending. I then run a cron job that checks the table each day and if fulfillments are marked as fulfilled since that last run we send an email letting the subscriber know their reward has shipped.
This is the final part of our api. While we don’t want users to have to log in to view their progress and to get their share links, we don’t just want these endpoints accessible to the public.
What we are going to do here is just integrate basic token based authentication with our API, using a key we generate and store in our
application.yml file. On our server this is going to be a part of our configuration file that should not be shared with anyone. When we implemented the figaro gem before this
application.yml file should be a part of our
.gitignore and never checked into source control.
First let’s jump over to our
/app/controllers/application_controller.rb file and add the following.
Here we are using Action Controller’s built in http token authentication method. We setup a
before_action hook that ensures that any requested resource in any controller passes through our
We then create a set of
protected methods, which are similar to private methods except these are accessible by any subclass of our main
In our authenticate_token method we just do a simple comparison to check if the token is equal to the token in our environment variables.
To generate our token I like to just use a variant of our
generate_token function in our controller in the rails console.
Open a new terminal, navigate to your rails project, and run the command
Enter the following in the rails console:
Anytime you run this you will end up with a string that looks something like this:
Take the unique key you have generated and add it to your
application.yml file as
SECRET_KEY_BASE like so:
Now if you try to access a resource you should get an unauthorized response.
In order to get a valid response again you need to pass the header
Authorization: “Token token= c3OcKh1J8a8ich9bQC0TNsdP7LdCqcWL57cJKoch4a0”
Make sure that you are always doing this over
https so that this is encrypted. When you build out your front end you must ensure that this key is NOT exposed on the client nor checked into version control like github. If you are using a node based front end, make sure to use a package like
DOTENV which gives you similar functionality to figaro for ruby. You can then create a route in your node server to handle these responses and keeps this key hidden from your front end code.
And that’s all folks. Our API is complete and you can deploy this to heroku or wherever you like to deploy your rails apps to. This is just an API and you can design the front end however you like. As always appreciate any feedback on the approach. Happy coding.