- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
The Nylas CLI is an amazing tool that can be used for a lot of different things, but did you know we can even test Webhooks without the need of an external web server? Before we get started, let’s review what webhooks are, and why they are important.
Webhooks are notifications triggered by certain events like receiving an email, opening a link inside an email, when an event has been created or deleted and more. Webhooks are important because they are submitted to our applications when something important happens, without the need of our application having the need of pulling information every “x” amount of time.
A webhook is triggered by the server and it’s delivered to your application without your application having to request it. Instead of having to make multiple requests, your application can wait until it receives a webhook. This makes applications more efficient and most important, faster.
When sending an email or creating an event, it’s important to know if the message was opened, a click inside the messages was clicked or if an event was modified or deleted. Having all this information will help us to make better decisions.
Before getting started with the Nylas CLI, we need to install it.
For details on how to install and use the CLI, we can read the blog post Working with the Nylas CLI.
In order to test Webhooks on the CLI, we need to create a Webhook Tunnel. This will register a temporary Webhook for us and will keep it open for around 2 hours. It will also provide us with a Web Server and will manage all the Webhook details so we don’t have to worry about anything.
We need to type (The print flag is just so that we can see the output on the CLI)
$ nylas webhook tunnel --print
And just like that, our webhook is created, our web server is ready and we only need to trigger some events and see what happens. We can just send an email to ourselves.
Here’s the confirmation for the email reception:
And check the CLI for the webhook notification
Awesome, huh? We got one notification because we got a new email, and another one because we opened the email. What will happen if we delete the email? Yes, we will get another notification.
But, the information is a little bit hard to read, especially if we get a lot of notifications. What we can do is get some help from a Ruby web application, so the webhook information is more clear.
Chances are that we already have Ruby installed on our machines. We can check our version by doing the following (Note that we support Ruby 2.3 and onwards).
$ ruby -v Ruby 2.6.8p205 (2021–07-07 revision 67951) [Universal.arm64-darwin21]
If we don’t happen to have Ruby installed, we can read the blog post How to Send Emails with the Nylas Ruby SDK where everything is nicely explained.
As we want to create a Ruby web application, our best option is to use Sinatra, one of the most popular Micro Frameworks in the Ruby world. We might need to install two additional gems which are webrick (To handle web server) and json (To handle json).
$ gem install sinatra $ gem install webrick $ gem install json
Once installed, we’re ready to go.
We will call this file WebHooksTest.rb
# Import your dependencies require 'sinatra' require 'json' # Create a home page and print out the information post '/' do data = JSON.parse(request.body.read) # Print on the console data.each { |deltas| puts JSON.pretty_generate(deltas) } # Send information back to the caller "#{JSON.pretty_generate(data)}" end
When we run this script, it will receive input from the Nylas CLI and print it in a more readable way.
We can run this script from the terminal by using:
$ ruby WebHooksTest.rb
Our Web Server is alive on http://127.0.0.1:4567/ but it doesn’t have anything to show and it might give us an error if we open it on the browser as we build it in order to work with the CLI.
Now, we have everything ready and we can call our Sinatra Web Application from the Nylas CLI to test webhooks using Ruby.
We can type
$ nylas webhook tunnel -f http://127.0.0.1:4567/
The Webhook tunnel will now forward the results to our Sinatra Web Application console.
If we send ourselves a new message, then we will see this on the console.
And it will also look nicer on the CLI as we’re using Ruby to format the response coming from the webhook.
And while this might not look like much, it’s simply the foundation of what’s coming next.
We may be wondering, “Haven’t we used Ruby already to test our webhooks?” and the answer would be “Yes, we did. But we did it locally”. Now it’s time to move to the outside world.
What we are going to do is create a Sinatra Web Application and upload it to Heroku, so it’s freely accessible. This web application is going to use SQLite to store the Webhook information and display it on the browser. While using SQLite on Heroku is not recommended because it will reset itself after a day or every time there’s a new deployment, it’s the easiest way to do some testing and it can be migrated later on to Postgresql if needed.
If we don’t have a Heroku account, we can sign up for one here. Once that’s done, we will need to install the Heroku CLI. I found that the best option is to use Brew.
$ brew tap heroku/brew && brew install heroku
Once that’s done, simply log into the Heroku CLI.
$ heroku login
Before we can move on, there are some additional gems that we need to install. The first one, OpenSSL, can generate some problems, so here’s how to overcome them.
$ openssl version OpenSSL 3.0.1 14 Dec 2021 (Library: OpenSSL 3.0.1 14 Dec 2021)
If we have OpenSSL 3.0.1 installed, then we’re not going to be able to install the gem as Ruby OpenSSL doesn’t play well with OpenSSL 3.0.1
What we need to do is install OpenSSL 1.1 like this
$ brew install [email protected]
We might have it installed already. We need to force our system to use that version of OpenSSL
$ brew link [email protected] --force
With that all done, we can install the gem
$ gem install openssl
And we can go back to OpenSSL 3.0.1 if we want
$ brew link openssl --force
There are some extra gems that we need to install, so here they are
$ gem install sqlite3 $ gem install bundler:2.1.2 #We need this specific version $ gem install sinatra-contrib
Now, we need to create a file called app.rb
# Import your dependencies require 'sinatra' require "sinatra/config_file" require 'nylas' require 'sqlite3' require 'openssl' require 'json' require 'time' # Nylas need to check that our app is active get '/webhook' do if params.include? "challenge" "#{params['challenge']}" end end post '/webhook' do # We need to verify that the signature comes from Nylas is_genuine = verify_signature(message = request.body.read, key = ENV['CLIENT_SECRET'], signature = request.env['HTTP_X_NYLAS_SIGNATURE']) if !is_genuine status 401 "Signature verification failed!" end nylas = Nylas::API.new( app_id: ENV['CLIENT_ID'], app_secret: ENV['CLIENT_SECRET'], access_token: ENV['ACCESS_TOKEN'] ) # We read the webhook information and store it on the database request.body.rewind data = JSON.parse(request.body.read) connection = get_db_connection() if data["deltas"][0]["type"] == "message.created" or data["deltas"][0]["type"] == "message.update" message = nylas.messages.find(data["deltas"][0]["object_data"]["id"]) elsif data["deltas"][0]["type"] == "message.opened" or data["deltas"][0]["type"] == "message.link_clicked" message = nylas.messages.find(data["deltas"][0]["object_data"]["metadata"]["message_id"]) end email_to = "" message.to.each{ | emails | email_to = email_to + ", " + emails.email } connection.execute("INSERT INTO webhooks (id, date, title, from_, to_, type) VALUES (?, ?, ?, ?, ?, ?)", [data["deltas"][0]["object_data"]["id"], Time.at(data["deltas"][0]["date"]).to_s, message.subject, message.from.first.email, email_to.delete_prefix(', '), data["deltas"][0]["type"]]) connection.close() status 200 "Webhook received" end # We display the database content get '/' do connection = get_db_connection() erb :main, :locals => {:webhooks => connection.execute( "select * from webhooks" )} end # We connect to the database def get_db_connection() connection = SQLite3::Database.open "database.db" end # We generate a signature with our client secret and compare it with the one from Nylas def verify_signature(message, key, signature) digest = OpenSSL::Digest.new('sha256') digest = OpenSSL::HMAC.hexdigest(digest, key, message) secure_compare(digest, signature) end # We compare the keys to see if they are the same def secure_compare(a, b) return false if a.empty? || b.empty? || a.bytesize != b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end
We will notice right away that the code that we are using is more complex than our initial application, and that’s because when Nylas sent us a Webhook, it expects our application to send a response back in order to validate that the service is working properly. Also, we want to validate the signature to make sure that it’s coming from Nylas and not someone else. Using Ruby we can easily fix that and validate the upcoming Webhook.
We also need to create a subfolder called views with one file called main.erb:
<style> .webhook { padding: 10px; margin: 5px; background-color: #f3f3f3 } </style> <title>Nylas Webhooks</title> <h1>Webhooks</h1> <% webhooks.each do |item| %> <div class='webhook'> <p><b>Id:</b> <%= item[0] %> | Date:</b> <%= item[1] %> | <b>Title:</b> <%= item[2] %> | <b>From:</b> <%= item[3] %> | <b>To</b> <%= item[4] %> | <b>Type</b> <%= item[5] %> </p> </div> <% end %>
As we’re going to use SQLite we need to create a schema and a table:
DROP TABLE IF EXISTS webhooks; CREATE TABLE webhooks (id TEXT NOT NULL, date TEXT NOT NULL, title TEXT NOT NULL, from_ TEXT NOT NULL, to_ TEXT NOT NULL, type TEXT NOT NULL);
Running this script using ruby init_db.rb will generate the database.db file we need for our application:
require "sqlite3" connection = SQLite3::Database.open "database.db" File.foreach("schema.sql") { |command| connection.execute(command) } connection.close()
The first thing we need to do is create a file called Gemfile, which will hold the information of the gems we need to install:
source 'https://rubygems.org' gem 'sinatra' gem 'nylas' gem 'webrick' gem 'sinatra-contrib' gem 'sqlite3' gem 'openssl' gem 'json' gem 'time'
Then, on the terminal we need to type:
$ bundle _2.1.2_ install
This will generate a file called Gemfile.lock and if we’re using a Mac, we need to add this as well
$ bundle _2.1.2_ lock --add-platform x86_64-linux
As we’re going to use SQLite, we need to create a folder called config with a file called database.yml and enter the following
development: adapter: sqlite3 database: db/mydata.sqlite3 production: adapter: sqlite3 database: db/mydata.sqlite3
Finally, we can initialize our git repository
$ git init $ echo .env >> .gitignore $ git add . $ git commit -m “Initializing Git repository”
We must not forget to commit our changes. Otherwise, they will be ignored.
Finally, we’re going to create our Heroku application:
$ heroku create blagruby-webhooks
Application names in Heroku must be unique, so you can change blag with your own name.
Now…in an ideal world, we could just deploy our application and let the webhooks come in…but…Heroku doesn’t allow us to use SQLite for Ruby applications…which is strange because we can for Python applications…but for Ruby, there are many missing components and the SQLite gem needs to be compiled.
Happily, there’s a way to do it, otherwise this section wouldn’t even make sense.
We need to run this command before pushing our changes:
$ heroku config:set BUILDPACK_URL=https://github.com/whynot/heroku-buildpack-ruby-with-sqlite3.git
This will create a custom backpack with all the missing files needed to successfully compile the SQLite gem.
Since we’re going to need to access our Nylas Tokens, we don’t want to use/push the .env file into Heroku, so we’re going to use Heroku Environment Variables instead.
We need to go to our Heroku Dashboard and select our application. Then go to Settings
And select Reveal Config Vars.
This will open the Config Vars so that we can update them
We can find this information on our Nylas Dashboard.
With all that ready, we can push our changes and deploy our application:
$ git push heroku master
We can now open our web application by typing:
$ heroku open
It looks somehow empty, but that’s because we don’t have any webhook notifications yet. For that, we need to create a webhook first.
If we go to our Nylas Dashboard and choose Webhooks
We can press Create a webhook
Here is the most important piece, as we need to provide the URL of our Heroku Flask Server and choose the Triggers that we want to use.
For this example we’re going to choose the first three Message Triggers.
Notice that message.link_clicked only works when an email is sent from a paid account, so the free trial will not work.
When we press Create Webhook, we can see our webhook listed.
Now, if we send an email to our connected account, we will see that the event is saved and displayed on our Sinatra application. Notice that I haven’t implemented auto refresh, so we need to manually refresh the page.
We will see the first webhook reflected on the webpage.
Awesome!
So the next step is to send an email from a paid account so that we can track when the email is opened and more important when a link is clicked. We call this file SendEmailTracking.rb
#!/usr/bin/env ruby require 'dotenv/load' require 'nylas' nylas = Nylas::API.new( app_id: ENV["CLIENT_ID"], app_secret: ENV["CLIENT_SECRET"], access_token: ENV["ACCESS_TOKEN"] ) tracking = { "links": true, "opens": true, "thread_replies": true, "payload": "new-payload", } nylas.send!( to: [{ email: [email protected]', name: "Nylas" }], subject: "With Love, from Nylas", body: "This email was sent using the Ruby SDK for the Nylas Email API. Visit <a href='https://www.nylas.com'>Nylas</a> for details", tracking: tracking ).to_h
Notice that the link needs to be between link tags.
Here’s the email that we received.
We open it.
And click on the link.
Using webhooks and ruby becomes easier when combined with Nylas.
If you want to learn more about Webhooks, please go to our documentation.
Don’t miss the action, watch our LiveStream Coding with Nylas:
Blag aka Alvaro Tejada Galindo is a Senior Developer Advocate at Nylas. He loves learning about programming and sharing knowledge with the community. When he’s not coding, he’s spending time with his wife, daughter and son. He loves Punk Music and reading all sorts of books.