Unlocking the Mystery: Why Your Templated Emails Aren't Reaching Recipients Despite Successful Sends
Introduction
Robust Email Communication: Harnessing AWS SES for Seamless Sending and Receiving. Email communication has never been easier, thanks to the abundance of high-level APIs available for building email clients. In this blog post, we explore the use of Amazon Web Services (AWS) SES (Simple Email Service) for sending and receiving emails, steering clear of Rust's Lettre crate.
Imagine the potential if WebAssembly could support Tokio's features. We could transform Rust's SES APIs into a WebAssembly library, enabling their seamless use in JavaScript without requiring knowledge of Rust or Rust dependencies. However, for now, our focus is on mastering interaction with the SES service through the command line interface (CLI).
Take a visual tour of what our SES application looks like in action. The SesOps module within the aws_apis Rust library provides high-level APIs that offer a little more robustness compared to AWS SES's plain APIs.
Downloading Binaries
To download the AWS SES (Simple Email Service) CLI (Command Line Interface), follow these steps: While holding the Ctrl key, click on the link to open it in a new tab, or use the mouse scroll button to open it in a new tab
Option 1: From AWS S3 Bucket Link
Option 2: From GitHub Link
Option 3: From Google Drive Link AWS SES CLI on Google Drive
All the links except GitHub include the binary for x86_64-pc-windows-msvc and _86_64-unknown-linux-gnu
. They also have an assets
folder that contains all example templates and associated template data and policies for checking the service.
The GitHub link points to the binary path; you have to download the associated repository, which includes the assets
folder inside the src
directory.
If the binary does not match your system, you have to build it from source. Follow these steps to build and run the binary:
Install Git and Rust, appropriate for your operating system.
Execute the following commands either in the Terminal or Visual Studio Code:
git clone https://github.com/Sanjuvi/Simple-Email-Service-SES-Client.git
cd Simple-Email-Service-SES-Client
cargo build --release # This may take some time
# If you are on Windows
target/release/ses_client
# If you are on a Linux-based system like Ubuntu
./target/release/ses_client
First Step
Even though we don't need to set up SMTP server settings like a username, password, or domain name, you still need credentials to perform these operations. This requirement arises because we are using one of AWS services that isn't free unless you are on a free trial like me. Additionally, these credentials should either have the inline policy below or the managed policy 'AmazonSESFullAccess' attached to them:
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"ses:*"
],
"Resource" : "*"
}
]
}
Or you can use:
arn:aws:iam::aws:policy/AmazonSESFullAccess
Creating an IAM user and attaching either an inline or managed policy to the IAM user or group has never been easier. I have also developed an AWS Identity Access Management (IAM) CLI Client to interact with the AWS IAM Service, and you can find a blog post about it here with more information. However, please note that these tools are not as robust as the SES Client, at least for now. You will have to rely on the placeholder information to correctly use IAM APIs options. The blog post discusses common credential problems and how to debug them in general. I recommend reading that specific part, even if you don't read the entire blog post.
I have also used this CLI tool to simplify obtaining the Data Access Role Arn for the AWS Translate Service to successfully execute the Start Text Translation Job. If you are interested in building deep learning applications in Rust and using the CLI, you can read about it in this blog post.
When choosing the region, it's important to select the appropriate one. You may notice high latency, for example, if you set the region to us-west-2
while you are actually in India. In that case, you should use ap-south-1
to reduce the distance and improve performance. Note that the region is not only involved in latency issues; resources like contact lists and associated emails are only accessible within the selected region. For example, you can't access a Contact list or Template residing in us-west-2
if you set a different region in the credentials. The region follows specific formats for specifying, and below is a screenshot of some regions from the AWS homepage. You should use the code on the right side:
Below is a short video demonstrating how to create a new IAM user, attach the aforementioned policies, and use those credentials to execute one of the SES CLI operations to verify that the credentials are valid and have all the necessary permissions.
You can skip the above video if you already have SES full access permissions with your existing credentials.
Enhanced SES Operations: Streamlined and Supported Actions
Supported Operations in SESV2 APIs:
CreateContact - This operation allows you to create a new email contact and add the contact to your contact list.
CreateContactList - This operation enables you to create a new contact list name to store all your emails for future email campaigns. Note that only one contact list per region is allowed.
CreateEmailIdentity - Use this operation to send a verification email to a given email address. This API sends a verification email since only verified emails can receive emails and be added to the contact list. You have two options when adding an email to the list: you can either add the email to the list and then send a verification email, or you can simply add the email to the list. If you choose the latter option, you must execute the Create Email Identity operation separately to send a verification email. Each option includes a help message at the end to guide you through the process, even if you haven't read the documentation.
CreateEmailTemplate - This operation creates an HTML and subject template, optionally allowing you to set a text-only body for recipients who don't support HTML. Both the HTML and subject parts can contain template variables to personalize the email. However, do not use HTML-like formatting in the subject part. You can find example HTML and subject body templates here and here respectively, to help you create your own templates. You can have multiple email templates under the same credentials.
DeleteContact - Use this operation to delete an email contact from the list. For deleting a verified identity, you should use the
Delete Email Identity
operation, which cannot be used directly at this time.DeleteContactList - This operation deletes a contact list, including all the emails in that list if one is available.
DeleteEmailIdentity - Delete an email identity created using the
Delete Email Identity
option. This operation cannot be used directly.DeleteEmailTemplate - Delete the template associated with the given template name. The CLI option will download the email template to your local computer before deleting it.
GetEmailIdentity - This operation is not consumed directly but is used to generate all email identities, both in text and PDF formats, in the
Get Email Identities
option.GetEmailTemplate - Download the email template associated with a given template name. The placeholder will show you all available template names in your credentials.
ListContactLists - This API is used for placeholder information and also within other options to prevent errors when providing a nonexistent contact list name without executing an operation.
ListContacts - This API is used within different options to provide informative error messages. You can download all the emails in the given contact list by executing the Retrieve Emails from the Provided List option, which generates both a text and PDF file with the emails.
ListEmailIdentities - This operation is not consumed directly but is used to prevent errors in other options. For example, it helps prevent the creation of email identities for the same email address, which would result in an error. This is checked before passing the email into the
Create Email Identity
orGet Email Identity
operations.ListEmailTemplates - This operation is not consumed directly. Options starting with
List*
are used to avoid errors in API calls. The API response is used when providing a nonexistent template name without executing an operation, allowing you to continue using the application without crashing.SendEmail - You can use this operation to send a simple email without creating an email template, but the email must be verified. There is no need to use the SendBulkEmail API to send multiple emails.
SendCustomVerificationEmail - This operation exists but is not used. It allows you to send a custom verification email. Due to the free trial version limitations, I cannot use and test both CreateCustomVerificationEmailTemplate and SendCustomVerificationEmail to demonstrate their functionality.
UpdateEmailTemplate - This operation updates an existing email template.
Deliberate Emphasis on Reliability
The Email Template contains three part , a subject , a html body or a text only part. An example template is available here which uses template variabeles , links ,styles. The subject can also have template variables. Here is an example subject template I used which shouldn't contain html part otherwise you have difficulties of debugging why template email can't received by the recepients.
Exploring {{Lang}} and {{Api}} with {{Author_Name}}
You don't have to worry about unverified identities or emails without identities in the contact list when executing the Send a Bulk of Templated Emails or Send a Bulk of Simple Emails functions to send multiple emails. These emails are filtered out using the "ListEmailIdentities" feature, ensuring that only verified emails are included in the SendEmail API. These options are robust in handling unverified emails and those without email identities, i.e., emails that have been added to the contact list without creating an email identity. As you may be aware, only verified emails can receive the email. If we attempt to pass unverified emails using the plain SendEmail API, it will result in an error, preventing some other verified emails in the list from receiving their emails. Below is a screenshot illustrating the explanation above
The three possibilities in the above execution are as follows:
1) The first email only added to the list, iq6gv2914@mozmail.com. Therefore, we can't send an email to this address without creating an email identity and verifying it. The message states, Therefore, it creates an identity by executing the Create Email Identity option on your behalf
to send a verification email to that address and then continue with other emails in the list.
2) The second email, 075wbawjp@mozmail.com, has an email identity but is not verified, i.e., the verified status is false. You can check this by executing Get Email Identities
to determine which emails are verified and which ones are not, and then continue.
3) The third email, u66rlhyoa@mozmail.com, has an email identity and is verified. Therefore, the email has been sent successfully, and these possibilities for handling errors continue as long as the contact list doesn't have any more emails in it.
The title uses words like reliable and robust for a reason. This kind of error handling and displaying human error messages is essential to understand why some of them fail and what you can do about it. You can refer to the send_mono_email function for more information.
Access Features and Information at Your Fingertips
Let's discuss Default Values
options. This option let you know the default values for the envrironment variables FROM_ADDRESS , LIST_NAME, TEMPLATE_NAME so that you can skip wherever the word default is appear either in placeholder or yellow colored help messages by entering with empty output and then default values is used. For the first you run , it's unlikely that you don't have those environment variables. Below is the ouput of when you run for the first time or not setting those environment variables.
Instead of unwrapping, it's uses unwrap_or method to show the message in the placeholder i.e the deafult value only shown when those environment variables are set. Set the environment variables permanently otherwise you have to set it for each run. You have to close the current running terminal or command line to environment variables take effect. For example, you have to restart the visual studio code if you are setting the environment variables.The FROM_ADDRESS is most likely the same for most operations. Once you set the FROM_ADDRESS environment variable, you can skip entering it without any inputs. The same is true for TEMPLATE_NAME and LIST_NAME. You can use the same template for all the emails and also use the same list name if you only send emails to that list. Just because those environment variables have been set doesn't mean you can't override them by typing different values when prompted. The default values will only be used when the input is empty.
When the placholder information shown or some times operation that are not executed also shows the outputs you should aware of . For example, when you provide the non exist contact list name even when the placholder shows you the correct contact list you still receive those names after inputting the wrong information and use Ctrl + Shift + c to copy the and Ctrl + Shift + v to paste the placholder information inside the terminal window. You shoudd use Ctrl + Shift + c and Ctrl + Shift + v in a Linux-based system; otherwise, the terminal will terminate the current running process since Ctrl + C kills the current process.
Sending templated emails is straightforward using the existing email template. However, whether the recipient receives the template email is uncertain. Even though the request is successful, the email may not be received by the recipients due to discrepancies in the template data, which should exactly match the chosen template. Passing different template variables, missing template variables, typing errors in the template variables (e.g., different cases or extra spaces), can cause the recipients not to receive the template email, even though the request is successful. To assist you, there are two options available to debug this problem when creating the template data for the associated template in the SES service.
The first option is to use Get Email Template Variables, which prompts you for the template name associated with the template and returns the template variables for the subject and HTML content, if any. Below is the output of executing this option and the used template
The second option is Match Template Variables which returns both matched and unmatched template variables from the template you provided by specifying their associated names. Below is the output of this option, using the same template as above. The JSON template data can be found here, which I modified in a copy of that document to ensure its correctness, enabling you to experiment with it.
Please note that the keys are sensitive to spaces around them. Avoid using spaces around the key when working with a template variable. For instance, refrain from using,
{
" key " : "Value",
"Another's Key" : "Value",
" Another,key" : "value"
}
Instead, use it like this:
{
"key" : "Value",
"Another_key" : "Another Value",
"Anothers_key" : "Value"
}
The template variables shouldn't contain apostrophes or commas around them. For example, if the subject or HTML template variables contain these, you will receive an error like this:
The 'read 'main' panicked at 'Error from create_email_template: ServiceError(ServiceError { source:
BadRequestException(BadRequestException { message:
Some("Handlebars compilation failed for input Template Content")
The function Send a Bulk Templated Email sends templated emails to all the email addresses in the list without requiring any input from you. The "Send a Bulk of Templated Emails" option only works if you use the same template in the assets folder. This option is included for your reference on how to customize it. The {{Email}} and {{Name}} template variables are replaced by the email addresses in the list, and the name is derived from the first 9 characters of each email address. In the real world, the name may come from different sources. You have to modify the code and change the template_data.json document to tailor that option for your use cases. Here are the necessary changes:
1) Set the environment variables appropriately since the function takes no parameters from the caller. Alternatively, you can modify it to accept parameters. 2) Change the content of the template_data.json file in the assets folder, and do not move it to a different path as it should exist in that exact folder. Alternatively, change the path accordingly in the include_str! macro in the send_bulk_templated_emails function.
Once you make the necessary changes to ses_ops.rs in the aws_apis repository, you have to update it on Git (assuming you've already forked that repository and have permission to upload it to Git). Then, change the aws_apis package path to your Git repository in the Cargo.toml file of Simple-Email-Service-SES-Client repo. Afterward, build it from source using the following instructions:
cargo update -p aws_apis # Assuming you haven't changed the package other than the repository path
cargo build --release # Once this is successful, you can run it from the target directory
target/release/ses_client # If you are on Windows
./target/release/ses_client # If you are on a Linux-based system like Ubuntu
However, if you already have a list, you can use the Send a Bulk of Simple Emails option to send emails to them without any changes to the codebase.
There are two instances where PDF documents are generated. When you choose the Retrieve emails from the provided list option, you get all the available emails in that list. Below is a visual representation of how it looks:
It appears you don't have a PDF plugin for this browser. you can click here to download the PDF file.
Please note that the PDF only shows the email status if they were created via the Retrieve emails from the provided list option; otherwise, it shows No Identity Exists
for the email and other fields. When you choose the Get Email Identities option, you get the following PDF:
It appears you don't have a PDF plugin for this browser. you can click here to download the PDF file.
This PDF contains all the emails in the credentials within the region. Each email has the identity info returned by the GetEmailIdentity API. The masked emails are generated using Firefox Relay, and these emails map to the same email address. This is why, in the video, executing both Send Bulk of Simple Emails and Send Bulk of Templated Emails will send the email to the same email address, resulting in receiving those types of emails in the same inbox.
The Send a Single Simple Email option allows you to specify either a local path or an S3 URL for the simple email content. Type "No" to provide the local path to the simple email content in HTML format. An example of simple email content that doesn't have any template variables is available here. Type "Yes" to provide an S3 URL that contains the simple email content file with or without any extension. An example S3 URL is: Simple Email Content S3 URL. The reason I chose an S3 URL is not just because it's also an AWS service, but also because of the response it returns when using the S3 URL to access content.
The S3 URL for the object is different from the same URL on the GitHub link. The major difference is that when you use the S3 URL, it only returns the single entity associated with that object. However, when downloading content from the GitHub link, it returns multiple files alongside the actual object you requested, which can be problematic as it contains more content than you requested. Be aware of newline and line feed control characters (\n, \r). When downloading HTML or any other file, extra symbols may be added to the file. To remove these characters, you can use the following code:
// The following code removes newlines, line feeds, and control characters
let x: &[_] = &['\n', '\r', ' ', '\x1b', '\u{20}', '\u{7f}', '\u{80}'];
let body_data = body_data.trim_matches(x);
Failure to remove these characters may result in errors similar to the one below:
The content contains control characters or symbols.
You can visit this link to learn more.
Please do not remove the possible_error.txt file inside the assets folder, as it is needed during the build process but not when running the application. Here is the code link where it is located.
Below is the video demonstration of executing SES client options. Please watch the above video before continuing with this one
Please feel free to share your thoughts in the comments.
References:
2) SesClient
3) Aws IAM
Subscribe to my newsletter
Read articles from Sanjeevi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by