Tutorial: Email Tracking Notifications

In this tutorial, we'll create a sample application that receives EasyPost Tracker updates via webhooks and sends an email notifications with SendGrid.

The EasyPost Tracking API makes it simple to get EasyPost tracking updates with webhooks. Your application can be notified and take action as a tracking update happens. In this tutorial, we'll create a Sinatra application with the EasyPost Ruby Client that can receive webhooks from EasyPost. Then, we'll use the SendGrid API to send an email to a test customer after a package gets delivered.

Although we'll use Sinatra (and Ruby) in this example application, this functionality could be integrated into any app written with Ruby, PHP, Java, and other languages with EasyPost's official client libraries.

Before You Start

  1. Sign up for an EasyPost account or log in to an existing account.
  2. Read the Getting Started Guide.
  3. Check out the Tracking Guide to get a feel for how Tracking works with EasyPost.
  4. Make sure you have Ruby and Bundler installed.

Step 1: Get Your Keys

The first thing we need is an API key from EasyPost. This is an example application, so we're going to use the test API key (of course, in a production application, you should use your production key.) You can find your test key on the API Keys page.

In order to send Email from our application, we'll use the SendGrid API. Go to sendgrid.com, and sign up for an account. Once you're in, click the "API Keys" button under "Settings" and click on create API keys to create one and copy the "API Key".

SendGrid API Keys Page

Next, open your favorite code editor and create a new file called .env. Fill in the test EasyPost and SendGrid API Keys, so it looks something like this.



Step 2: Creating the App

2.1 Declare Dependencies

We'll start out by creating a Gemfile with the dependencies we will need. This will be a new file titled Gemfile in the project root directory. We can get away with a pretty minimal list, so let's add in the easypost and sendgrid gems to communicate with the respective APIs, the sinatra gem for our web application, and the dotenv gem to load our configuration values in. Your Gemfile should look like this:

source 'https://rubygems.org'
gem 'easypost'
gem 'sendgrid-ruby'
gem 'sinatra'
gem 'dotenv'


At this point, we can go ahead and install our dependencies by running bundle install, so let's do that.

2.2 Create a Basic Application

Next, we can start writing our web application. Create a file called app.rb and save it in the project root directory. Here are the first few lines:

require 'sinatra/base'
require 'easypost'
require 'sendgrid-ruby'
require 'tilt/erb'
require 'pry'
require 'dotenv'

class App < Sinatra::Base
  set :show_exceptions, :after_handler
  configure :development, :test do
  configure do
    EasyPost.api_key = ENV['EASYPOST_API_KEY']
    set :sendgrid, SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])


In these first couple of lines, we import the dependencies we installed, the app is defined (we're using Sinatra's modular style) and the config variables are loaded. We then instantiate the EasyPost and SendGrid clients with the keys from our .env

2.3 Receive Event Notifications

Now comes the meat of the application. Let us add the post method to the class App in app.rb.

class App < Sinatra::Base
  set :show_exceptions, :after_handler
  configure :development, :test do
  configure do
    EasyPost.api_key = ENV['EASYPOST_API_KEY']
    set :sendgrid, SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])

  post '/easypost-webhook' do
    request_string = request.body.read
    parsed_request = JSON.parse(request_string)

    if parsed_request['object'] == 'Event' && parsed_request['description'] == 'tracker.updated'
      event = EasyPost::Event.receive(request_string)
      tracker = event.result

      message = "Hey, this is FunCompany."
      if tracker.status == 'delivered'
        message += "Your package has arrived! "
        message += "There's an update on your package: "

      td = tracker.tracking_details.reverse.find{ |tracking_detail| tracking_detail.status == tracker.status }
      message += "#{tracker.carrier} says: #{td.message} in #{td.tracking_location.city}." if td.present?

      from = SendGrid::Email.new(email: 'test@fromaddress.com')
      subject = 'Hello World from the SendGrid Ruby Library!'
      to = SendGrid::Email.new(email: 'customer@gmail.com')
      content = SendGrid::Content.new(type: 'text/plain', value: message)
      mail = SendGrid::Mail.new(from, subject, to, content)

      output = settings.sendgrid.client.mail._("send").post(request_body: mail.to_json)

      [200, {}, "Email update was sent to the customer!"]
      [200, {}, "Not a Tracker event, so nothing to do here for now..."]

  run! if app_file == $0


We know from the Webhooks Guide that incoming webhooks are POST requests, so we route /easypost-webhook with the POST method. We also know from the Tracking API that webhooks for tracked packages come in an Event object, so we create one with the EasyPost Ruby client.

Next, we compose a message to be sent to the customer. In this example, we have some logic dependent on the status of the shipment, and create a message which includes the most recent tracking information. Because the order of tracking details can vary, we iterate through the list of tracking details until we find one that matches the status of the Tracker.

Lastly, we send an Email using SendGrid with the message that we just generated.

Step 3: Starting the App

It's time to get the app started and see if we can get an email notification!

Open your shell, and run bundle install and then run $ ruby app.rb. The app is now running on port 4567, but there's one problem - since it's running locally, EasyPost has no way to access it! In a production application, our app would be running on a remote server, but there's no need to set one up for this example. Instead, we use a tool called ngrok that can temporarily tunnel our app to the outside world.

Visit the ngrok website to install ngrok on your computer, then open a new shell. Simply run $ ngrok http 4567, and ngrok will take care of all of the heavy lifting. You should see a URL that looks something like https://something.ngrok.io in ngrok's output – copy it.

All that's left is to tell EasyPost where to send our webhooks. You can do this from the webhooks page, or by running the following cURL command (replace xxxxxxxx.ngrok.io with your ngrok URL and insert your actual EasyPost test API key):

$ curl -X POST easypost.com/api/v2/webhooks -d 'webhook[url]=http://xxxxxxxx.ngrok.io/easypost-webhook&webhook[mode]=test' -u 'your-test-api-key:'


It's time to see the results of our hard work! EasyPost will send us a webhook after we create a test Tracker. We use a test tracking code, EZ2000000002 (you can find the full list of test tracking codes here):

$ curl -X POST https://easypost.com/v2/trackers -d 'tracker[tracking_code]=EZ2000000002' -u 'your-test-api-key:'


In a few minutes, you should get a customized Email when an update occurs on the test Tracker:

Sample Tracking Notification Email

Awesome! If you're interested, try playing with message customization using attributes listed in the Tracking API.