Track Opened Emails In Rails

article logo

Tracking opened emails is a technique that, if used responsibly, can provide utility to both you and your users.
Here a few use cases where it makes sense to me:

  • Appointment updates. If your appointment in a car dealership was rescheduled an email is sent. If the email is not read in the next couple of hours a repeat email can be sent or a CS agent may follow up with a call.
  • Uber-style fulfillment, where contractors are notified of lucrative jobs available to them. If nobody claims a job, a CS agent can go through users who are known not to have read the email.
  • Providing estimates to event organizers how many people are going to show up based a on number of opened emails.

In this article, we are going to implement such a system for transactional emails using Rails and Postmark (a service that sends emails). We are going to use the Postmark gem.

Usage

First of all, let's see how the code usage of the tool we are building is going to look like:

def new_blog_post(blog_post, subscriber)
    store_message(
      email_name: 'new_blog_post',
      entity: blog_post,
      user: subscriber
    )

    mail(
      to: subscriber.email,
      subject: "New Post: #{blog_post.title}",
      track_opens: 'true'
    )
end
all_emails = blog_post.sent_emails.count
opened_emails = blog_post.sent_emails.opened.count
open_rate = opened_emails.fdiv(all_emails)

What is a tracking pixel?

By adding track_opens: 'true' you are instructing Postmark to include a unique 1x1 image in the email. When a user opens that email the image gets loaded, which notifies the Postmark that the email was opened. It may not always work depending on an email client and user's settings.

Implementation

Setup

To track email status we are going to create records in a database table:

The next step is to define a callback that's going to be executed any time an email is sent, for that we need to add the following code to initializers:

Mailer

To pass these params from our mailer we need to define the store_message method.
With this code any time we successfully send an email we are also going to create a record in the sent_emails table.

Webhooks

The next step is to start accepting webhooks from Postmark.

Let's define a webhook for opening an email, then do a similar thing for bounced emails.

And controller code that's going to handle these webhooks:

Here's how these services are implemented:

That is all, use responsibly, and thank you for reading!

p.s. you can see the full example repo here.

Popular posts from this blog

HTTP server in Ruby 3 - Fibers & Ractors

Next.js: restrict pages to authenticated users

Migration locks for TypeORM