Track open emails with Ruby on Rails
Goal
In the application I work with the most these days, sending emails and making sure they’re received by recipients is a critical part of our business.
We have to first ensure delivery (that would be another good topic to write about), and we also want to know whether a user has opened their emails, clicked on them, how much time it took them to do so.
TL;DR: I made a gem to leverage email relays (Sendgrid, Postmark…) open tracking systems and integrate this data into your Rails app, it’s available here.
Possible approaches to track opened emails with Ruby on Rails
Email is a relatively old protocol, and hasn’t really been designed for all the use cases that we have today. This is why it is full of hacky practices that work (most of the time), but don’t feel “clean” in a programmer’s mind.
Our challenge for today is: “How to track email opens and clicks?”
A common way to do this, is to embed a very small image, known as “a pixel tracker” in the content of the email. This image will get loaded whenever the email is open. When the image gets loaded, the server hosting the image can register a click event. To track which email it comes from, the image needs to contain a query param to identify the email, such as https://myserver.com/email_image.jpg?email_id=987sf979223k2hk232398
This is, to my knowledge, how most email open tracking is done, now let’s see how we can do this in Rails.
- DIY solution: you can build such system with little difficulty. You need to store a few information in a table, and add a 1x1 pixel image to each email you send out
- Use a gem: there’s a gem that already does this for you, and has additional features built-in
- Use your provider’s api to get back events in your app
In this article, we’re going to dive into the third option. Chances are, you’re using a provider such as Sendgrid, or Postmark, to send your emails, and you aren’t maintaining your email infrastructure.
These providers have already built infrastructure and reporting around email events (clicks, opens, but also bounces or spam reports).
I decided to leverage what they’d already done, and thought that I probably wouldn’t be able to achieve a similar result in a resonable amount of time. Also, I’m very interested by events such as bounces, so I would have had to build an integration with these providers anyways.
Email relays generally offer an API, and usually webhooks can be configured as well to be kept in the loop about what’s happening to the emails you send.
All we need is a bit of code to pass along the information required (such as an ID that we keep stored in the app) when sending the email, and making sure that we can retrieve it when we receive webhooks.
Tracking email opens from Sendgrid in Ruby
When getting started, I was using Sendgrid in the app I’m working with the most, and therefore built the integration for it first.
It’s relatively straightforward, as we just need to parse a payload, which can contain multiple events.
Tracking email opens from Postmark in Ruby
Later on, we switched to Postmark to send emails, for increased deliverability (it seems at least, from our testing).
Here again, processing events is quite straightforward, we’re simply recording the data in our EmailEvents
table.
Use cases
We use this for a few things
- Troubleshooting
- Support
- Calculating stats like “response time” or “opening rate” of certain emails, which is an important metric that we reuse in different parts of our app
You might wonder “but, isn’t this data reported in Sendgrid or Postmark already”?
That’s right, it is already there. There are a few drawbacks though. In the case of Sendgrid, the interface is a bit wobbly, and loading times are quite an issue. Depending on your plan with them, you might as well run into retention issues.
The biggest problem though, is that this information is not visible directly in your app. For us, it’s very useful when doing support for example, and it’s a way better experience if the person doing support doesn’t have to open another tab with our email provider, trying to find the right email… It may also be better to leave access to our email provider only to the people that really require it.
Parting words
I turned this bit of code that listens to webhooks from different providers into a gem, which you can find on github. Feel free to use it to track opened emails in your Rails app, and give some feedback (issues, PR) on the project as well.