How Michelin runs the Better Motion loyalty program with Voucherify?
0
Days
0
Hours
0
Minutes
0
Seconds
Show me how
2022-10-19 5:00 pm
arrow pointing left
go to TECH
Technology
Programmatic Emails with API-first Providers
Mike Sedzielewski
Mike Sedzielewski
January 11, 2018
Share it on Twitter
Share it on Facebook
Share it on LinkedIn
Share it on Twitter
Share it on Facebook
Share it on LinkedIn

Programmatic Emails with API-first Providers

Every e-commerce business sends lots of emails. Be it part of a marketing campaign or payment processing, almost every operation or process is often wrapped up with an email message. However, email server configuration and maintenance is complex; it’s actually a separate field of study. When push comes to shove and the first users come to the platform, you really don’t want to figure out how to keep your email infrastructure up and running. You clearly don’t want to worry about email deliverability either.

Two types of emails

The online marketplace we’re building, but also pretty every online company out there, needs 2 types of emails to communicate with users and other stakeholders:

Marketing emails:

  • Content promotion & offers.
  • Sales emails & communication.

Transactional emails:

  • Commerce receipts and shipment notifications.
  • Account updates.
  • Password changes.
  • Order status updates.

In the case of an early stage business like Manufaktura, the first category of emails fits with the solutions provided by products like MailChimp or GetResponse. They offer a simple way to roll-out and monitor marketing campaigns run via the email channel. The mechanics of marketing emails are quite common for an early stage startup so, tapping into one of the mentioned products will do. In this article, however, we want to focus on transactional emails.

The type and frequency of transactional emails differ from company to company, from process to process. That’s why we need to find a way to send highly customizable messages in the first place. Additionally, sooner rather than later you’ll learn you also should figure out how to make email templates and content modification approachable for marketers and the customer service team.

The answers to these issues come with the programmatic email service providers. Let’s explore this approach step by step.

API-first email providers

The email service providers (ESP) like SendGrid, Mandrill or SparkPost abstract the email server configuration magic for you. They expose the email functionality behind a simple REST API. All you have to do is authenticate and use call corresponding endpoints as in this snippet:

{{CODE}}

const sgMail = require('@sendgrid/mail');

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const msg = {

 to: 'test@example.com',

 from: 'test@example.com',

 subject: 'Sending with SendGrid is Fun',

 text: 'and easy to do anywhere, even with Node.js',

 html: '<strong>and easy to do anywhere, even with Node.js</strong>',

};

sgMail.send(msg);

{{ENDCODE}}

With the use of this tool, you can forget, or at least put off, worrying about related issues such as:

  • Deliverability rate - ESPs have dedicated teams focused on this matter only
  • Reporting - out of the box you get bounce-, open-, click-rate summaries
  • Bulk send out - sending billions of codes a month rest assured they know how to handle your batch of emails
  • Email filtering - ESPs automatically stop sending emails to bounces, blocked mailboxes, spam reporters or misspelled emails
  • Unsubscribed list management
  • Custom domain setup

But the utmost benefit of using ESP, especially in the early stage, is the fact it’s cheap. You can go through the pricing summary put together by Zapier to see it’s a matter of less than 100 dollars a month, often offered with substantive free quotas on top of that.

Every ESP has its pros and cons, but they mostly offer the same service. When choosing your provider, apart from the free quota, you should also take a look at SDKs quality, other marketing features, UI - all the stuff that influences the speed of onboarding. In the early stage of your platform, you can change the provider pretty easily after all.

In Voucherify we use SES for internal emails, but our clients can connect their Mandrill or SendGrid accounts to send coupon emails on their behalf.

In this tutorial, we’ll give SendGrid a try.

What to look for when sending an email?

The email send out is a straightforward thing. As you can see above, it boils down to just a few lines of code. But there are some good practices and also low hanging fruits at the same time; you can introduce an email engine which reduces your headaches in future.

Fallback

Every SaaS/IaaS provider faces issues someday (even AWS). Therefore, it’s wise to connect some form of fallback provider. The effort isn’t big - just watch the responses you get from the primary ESP and call another one if there’s an error.

Although, this solution will protect you from minor hiccups, sometimes your provider is hit by a massive outage. In this case, it’s reasonable to have your email service configured in a way that allows for a quick change of ESP.

It’s also worth noting that the consequences of random errors from your ESP can be also reduced by implementing a simple retry policy.

BCC

In the first days of your emails, when the traffic is low, but every delivered message adds up to the overall customer experience, it makes sense to ensure emails fly as expected. Add you or your team to BCC to get 99.9% confirmation all emails go through nicely.

Test mode

For test purposes, you might not want to utilize your free quota. Consider setting up different accounts for test/dev mode - it can even be a Gmail account. Or, perhaps you might want to skip the email altogether.

Take a look at this example of email service. The code is prepared for introducing the best practices we just mentioned (fallback, bcc, test mode)

{{CODE}}

class SendgridService {

   constructor(config) {

       this.client = sendgrid(config.sendgrid.api_key);

   }

   send(options) {

       return new Promise((resolve, reject) => {

           this.client.send(options, function(error, result) {

               if (error) {

                   console.error("(send) Failure - Message: %j Error: %j", options, error);

                   return reject(error);

               }

               console.log("(send) Success - Message: %j Result: %j", options, result);

               return resolve(result);

           });

       });

   }

}

class EmailService {

   constructor(options, config = Config) {

       this.config   = config;

       this.provider = new Sendgrid(options, config);

   }

   send(options, record_info) {

       if (_.contains([ "sandbox", "dev" ], this.config.env.name)) {

           options.to = this.config.email.test_recipient;

           options.bcc = undefined;

           // other test related stuff

       }

       console.log("Sending email from: %s to: %s", options.from, options.to);

       return this.provider.send(options)

       .then(result => {

        // some log/operation in case of success

           return result;

       })

       .catch(error => {

        // some log/operation in case of error, can be fallback or retry

           return Promise.reject(error);

       });

   }

};

{{ENDCODE}}

Content modification

When you’re starting out and the requirements aren’t set in stone, assume that the email’s design and text will be modified, a lot. You’d better be prepared for that.

The first step to handle frequently-changing content is supported out of the box - you just keep the templates in separate HTML files and change the “options.html” parameter to point to the respective path. The HTML email is then rendered by SendGrid and the send out code remains intact. But let’s take this one step forward.

For the time being, when marketing wants to modify a copy they have to ask you. You don’t want to let them change it themselves because they can break the HTML template. How to by-pass this problem so marketers or customer service folks can manage the content - like they do in MailChimp for example?

Luckily, we have a cheap and easy to use solution. With the help of Contentful (another API-first tool, it’s a headless CMS) and our open source application, you can make the email templates edition available to copywriters. This is how it works:

  • Copywriters create/edit email copies in Contentful editor. They do this in so-called “draft mode”. It’s only about text - they can’t modify the HTML template in any way.
  • Before they actually push the message out to production, they can preview the final version of the email. This is achieved by visiting Contentful-emails web app, which renders a copy from Contentful based on the current HTML template.
  • If all is fine, the copy goes to production.
  • In case they want to update any copy, they just change the status to draft and experiment again, meanwhile the old version still works fine on production.

You can find the full description of this tool in this article.

When to send emails?

When it comes to online marketplaces, there are dozens of situations when you must or should send an email. We can’t list them here because it’s too business-specific. However, you can assume some emails will be 100% sent when an order changes its status. As you remember from our previous articles, at Manufaktura orders are managed by Salesforce. Now, in our last post we’ve shown that every time the order status is changed, we notify (HTTP request) our external application about this fact. Then, the application handles this callout and this is a good place to put the email send out functionality, see the example for pending-to-start status update:

{{CODE}}

var Logger          = require("./../../../../logger");

var email_service   = require("./../../../../services/email");

var logger          = new Logger({ prefix: "order/state/pending-validation" });

module.exports = function(request, response) {

   var order = request.body.order;

   var account = request.body.account;

   if (!order || !order.Id || !account || !account.Id) {

       logger.error("Order or Account is not defined | Data: %j", order.body);

       return response.status(200).end();

   }

   logger.info("Order: %s Account: %s", order.Id, account.Id);

   response.status(200).end();

   email_service.send({

       "from": {

           "email" : "bandro@rspective.pl",

           "name"  : "Manufaktura Team"

       },

       "personalizations": [{

           "substitutions": {

               "[[customerName]]"  : account.Name || "",

               "[[orderNumber]]"   : order.OrderNumber || ""

           },

           "to": [{

               "email" : account.email__c || "team@voucherify.io",

               "name"  : account.Name

           }]

       }],

       "subject": "Welcome",

       "template_id": "f534796e-c338-4f01-9242-bfa7ec070b38"

   })

       .then(result => {

           logger.info("Email sent - Order: %s Account: %s Result: %j", order.Id, account.Id, result);

       })

       .catch(error => {

           logger.error("Email not sent - Order: %s Account: %s Message: %s Error: %j Stack: %j", order.Id, account.Id, error, error, error && error.stack);

       });

};

{{ENDCODE}}

This structure is easily extendable, you just need to add another callout handler for each status in the application.

Bonus – deliverability issues solved

We have a story to share. For one of our projects, the email deliverability was an utmost priority. If the user hasn’t been onboarded correctly, the business would be severely hurt. We used Mandrill for our emails. Although it was properly configured from the outset (confirmed with the Mandrill support), some users didn’t get the invitations for example. In fact, it’s not that they didn’t receive it. They landed in SPAM, they weren’t delivered at all. To make matters worse, there were no rules for this kind of incident. Both time and the affected domain were just random.

We suspected that the problem occurred because we had used Mandrill Shared Email servers (one IP used by multiple users) and this configuration might decrease our rating when it comes to SPAM. But this was a very early stage and we hadn’t spammed.

We decided to switch to SendGrid but it didn’t help. As a last resort, we decided to buy a dedicated IP in Mandrill and it worked like a charm. But the funny thing is that Mandrill support team didn’t encourage this because in their eyes it would decrease our deliverability for a small-volume user, as we were.

Also, the problem was hard to spot in the first place because Mandrill deliverability report doesn’t show if the mail is delivered but only that it’s been sent successfully. We had to dig deeper in the UI to find the list of SMTP events and go through the forest of details to figure out if messages had hit the target.

Summary

Our online marketplace - Manufaktura - has been equipped with a powerful communication channel. We approached this feature according to rules we set out in the first article - by focusing on speed and maintainability. Things our software needs in the frequently changing business environment.

The API-first email service providers allowed us to build an email machine with just a couple of lines and the Contentful-emails application helped the marketing and customer service team iterate on the content without bothering developers.

Now, when we have emails working we can go to payments!

{{CTA}}

Join the future of API-first software

Join Voucherify

{{ENDCTA}}

Share it on Twitter
Share it on Facebook
Share it on LinkedIn

Join our newsletter

By registering, you confirm you have read and agree to the Subscription Agreement, and to the storing and processing of your personal data by Voucherify as described in the Privacy Policy.
Before you send us your data, you must become acquainted with the Privacy Policy where you will find information on the personal data controller, your rights and our obligations, the purpose for which your data are processed and any other information which relates to the protection and security of your personal data.
Thank you for subscribing to our newsletter!
Something went wrong while submitting the form.
Close

We’re constantly growing

and we need your help!