Email communication with our users is an important part of Skilltype’s technology stack. It is also one that the engineering team takes great care to get right. Not all emails are treated the same, however. Skilltype Engineering puts emails in two buckets, transactional and non-transactional:
- Transactional emails provide essential information related to specific user actions. These include password resets, inviting colleagues, and platform features like the weekly digest for recommended content.
- Non-transactional emails span several areas, including new release updates and announcements, townhall event invitations, and other engagement-related content.
This post focuses on transactional emails and offers a technical view of the components involved in getting notifications to our users’ inboxes.
From Request to Inbox
We’ll use the diagram below to step through the sequence of system actions that take place for a user-initiated request such as a password reset.
After submitting their email address on the password reset page, the following steps take place:
- The frontend relays the request to its API backend validates the input and invokes an AWS Lambda function (Sender in the diagram above), passing in the required parameters. We’ll cover Skilltype’s combination of stateful and stateless components in a future architecture-related post.
- Before the Sender function processes the request it ensures that the email it’s been asked to relay has not already been sent to the user. It does so by computing a hash of the email and checking if that value already exists in a DynamoDB table used to keep track of sent messages.
- Amazon Pinpoint provides the actual email-sending technology that the Sender function calls on once it’s ready to dispatch the email. We use Pinpoint (and not SES directly) for the event stream it’s able to let us tap into in order to uncover any deliverability issues. More on this soon.
- Once the user receives and follows the link to reset their password, the journey is complete.
When it came time to evaluate options for our transactional email needs, we considered Amazon’s Simple Email Service first. It’s been a tried and tested part of our architecture and with the desire to stick with AWS services wherever possible, it was an obvious choice until we looked into Amazon Pinpoint.
Pinpoint itself uses the same underlying architecture as SES so we did not have any concerns about reliability. In short, the feature that stood out between the two was the Pinpoint Event Stream. By setting up a Kinesis Stream to receive Pinpoint events such as sends, receives, bounces, clickthroughs, and the like, we were able to get some much-needed observability into our email deliverability performance.
We make use of a dedicated Lambda as our handler for each event Pinpoint relays through the Kinesis stream (see Handler in our diagram above). To make this information easy to consume for our non-technical staff, we normalize the data into a relational database table inside our network and grant read-only access to it via one of our favorite data tools, Metabase (see screenshot below).
With this data, we’re able to tell when emails are received, opened, or bounced. This information comes in handy when our users contact us for support. For example, it’s not uncommon for us to work with the IT department within our customers’ organizations. This ensures our emails are allowed to reach inboxes once we’ve identified the issue using our event stream data.
Sending transactional emails to our users is a critical part of our business and a task that Skilltype’s engineering team takes to heart. We use a mixture of stateful, stateless, and event-driven components to ensure emails reach our customers. As well as, capture just the right amount of analytics to aid our Customer Success team in troubleshooting deliverability issues.
We’ve only scratched the surface of our overall technology architecture. Stay tuned for future posts that dive deeper into how we operate the platform.