Blogs

How to prevent AWS SES review and suspension

How to prevent AWS SES review and suspension

How to prevent AWS SES review and suspension

Jun 8, 2022

Developers may find their AWS SES account banned even if they never sent any spam. Follow these best practices to prevent your SES from being reviewed and suspended.

One day you wake up to angry customers and colleagues that emails are not working. You notice the deadly error in your logs:

"554 Message rejected: Sending suspended for this account"

If your product relies on emails, getting your AWS SES account banned is the last thing you want. Resolving a suspension can take weeks or never if you cannot satisfy the AWS team of your good intentions. So it's just much faster to prevent this from happening.

But we don't send spam. Do I need to worry?"

The short answer is: maybe, it takes minutes to figure out.

The long answer is: Bots, your own test users, frustrated users, poorly configured private mail servers and many other things can contribute to your SES being banned. Just because you don't send spam doesn't exactly mean you are safe. It only takes a few minutes to figure out if you need to worry (first method under Prevention Methods).

What causes AWS SES being suspended?

AWS monitors your SES activity both manually and automatically. They look for:

  • High bounce rate: emails being sent to addresses that don't exist

  • High complaint rate: people or automated filters reporting your emails as spam

  • Spamtraps: these are addresses controlled by Anti-Spam organizations. Like under cover cops, these inboxes are used to identify spammers

  • Content violations and illegal activity: Unless you work on weird stuff, you don't need to worry about this one

Prevention Methods - Step by Step

Step 1: Monitor bounce/complaint rate with CloudWatch (5 minutes)

Bounce and complaint rate are automatically reported in CloudWatch metrics. All you need to do is create an alarm for them:

  1. Go to your SES dashboard => Reputation Metrics

  2. Next to the bounce rate graph, click the View in CloudWatch

  3. Click the bell icon to create an alarm (located in the bottom table, under actions column)

  4. Set the statistic to Maximum, set the period to 1 Minute

  5. Set the condition to Greater than 0.025 for Bounce (use 0.0005 for Complaint)

  6. Click next

  7. If you have an existing SNS topic for CloudWatch alarms, use that here. Otherwise, select Create new topic, give it an email address, then press Create Topic.

  8. Press Next until alarm is created.

AWS will certainly suspend your account at 10% bounce and 0.2% complaints. AWS considers half of these values as the warning threshold, thus potentially putting your account in manual review. That's why we monitor for half of the warning threshold to never risk it.

All the above can be done using the following Terraform example:

provider "aws" {
  region = var.AWS_REGION
  access_key = var.AWS_ACCESS_KEY
  secret_key = var.AWS_SECRET_KEY
}

# CloudWatch alarm for bounce rate
resource "aws_cloudwatch_metric_alarm" "ses-reputation-bounce-rate" {
  alarm_name                = "ses-reputation-bounce-rate"
  comparison_operator       = "GreaterThanOrEqualToThreshold"
  evaluation_periods        = "1"
  metric_name               = "Reputation.BounceRate"
  namespace                 = "AWS/SES"
  threshold                 = "0.025"
  period = 3600
  statistic = "Maximum"
  alarm_description         = "Alarm at 50% of the recommended warning level for bounces."
  alarm_actions = [aws_sns_topic.cloud-watch-alarms.arn]
}

# CloudWatch alarm for complaint rate
resource "aws_cloudwatch_metric_alarm" "ses-reputation-complaint-rate" {
  alarm_name                = "ses-reputation-complaint-rate"
  comparison_operator       = "GreaterThanOrEqualToThreshold"
  evaluation_periods        = "2"
  metric_name               = "Reputation.ComplaintRate"
  namespace                 = "AWS/SES"
  threshold                 = "0.0005"
  period = 3600
  statistic = "Average"
  alarm_description         = "Alarm at 50% of the recommended warning level for complaints."
  alarm_actions = [aws_sns_topic.cloud-watch-alarms.arn]
}

# SNS that listens to cloudwatch alarms
resource "aws_sns_topic" "cloud-watch-alarms" {
  name = "cloud-watch-alarms"
}

# Subscribing admin email to cloudwatch alarms SNS
resource "aws_sns_topic_subscription" "ses-cloud-watch-alarms" {
  endpoint = var.ADMIN_EMAIL_ADDRESS
  protocol = "email"
  topic_arn = aws_sns_topic.cloud-watch-alarms.arn
}

Step 2: Fix bounces from automated tests (10 minutes)

If your software sends notifications to users, test users with fake addresses will result in bounces.

  1. Create a real email address like: testinbox@yourcompany.com

  2. Go through your automated tests, especially end-to-end tests like Selenium and Cypress, and ensure that test users use addresses such as: testinbox+TESTNAME@yourcompany.com

You can also use this inbox to confirm the UI of your emails.

Alternatively, if you don't want to pay for an inbox or if you don't care about the UI of your notifications, you can use AWS provided test addresses. E.g. success+TESTNAME@simulator.amazonses.com.

Step 3: Ignore emails to unverified accounts (1 hour)

If you use Auth0 or Cognito, you probably have an email verification process in place. In that case, your user objects have a "verified" property on them.

You can ignore emails to unverified users with a simple check:

With Cognito:

// pass the user object you get from cognito to this function
const sendEmail = async (user: CognitoIdentityServiceProvider.UserType, subject, html) => {
  const verifiedAttribute = user.Attributes.find((a) => a.Name === 'email_verified');
  if(!verifiedAttribute) {
    return;
  }
  
  // email sending logic ...
}


With Auth0:

// pass the user object you get from Auth0 to this function
const sendEmail = async (user: Auth0.User, subject, html) => {
  if(!user.email_verified) {
    return;
  }
  
  // email sending logic ...
}

Step 4: Suppress addresses that bounce/complain (1 day)

Your SES comes with a cool feature called Suppression List. When you use SES to send emails to addresses on this list, SES simply ignores your request without throwing an error. All you need to do is add addresses that bounce or complain to this list. Sounds easy, but it's a little more complicated:

  • First you have to create an SES ConfigurationSet that tells SES to report on bounces/complaints

  • Then you have to connect this ConfigurationSet to an SNS that receives bounce/complaint reports

  • Then you connect the SNS to a Lambda function that reads the report and adds the bounced/complaining email address to the Suppression List

  • Finally, whenever you using SES from your code, you have to set it to use the ConfigurationSet we created in the first step

Step 5: Implement an unsubscribe link (1 day)

You can use the Suppression List from the previous method to allow users to unsubscribe from your emails:

  1. When sending emails, add an unsubscribe link that follows this format:
    https://api.yourcompany.com/unsubscribe/{email}/{encryptedEmail}
    The encryptedEmail value is simply the email address that is encrypted or hashed using any secret only available to your back-end.

  2. Create an end-point for the above address

  3. Ensure encrypted version of {email} matches {encryptedEmail} to users from impersonating others

  4. Add email to Suppression List

The ultimate method: Notification Service (10 minutes)

There is a lot that goes into building robust notifications. A managed Notification Service, such as NotificationAPI, allows you to build notifications in minutes and forget about the nitty-gritty like updating the HTML of your emails.

How to send your notifications using NotificationAPI:

// Install package: 
// npm i notificationapi-node-server-sdk
// yarn add notificationapi-node-server-sdk

const notificationapi = require('notificationapi-node-server-sdk').default;

// init
notificationapi.init('CLIENT_ID', 'CLIENT_SECRET');

// send
notificationapi.send({
  notificationId: 'NOTIFICATION_ID',
  user: {
    id: 'TEST_USER_ID',
    email: 'TEST@TEST.COM',   // required for email notifications
    number: '+15005550006'    // required for SMS/Call
  }
});

Conclusion

In this guide, we’ve explored 6 methods for preventing your AWS SES account from being reviewed and suspended. Depending on the amount and complexity of your notifications, you may spend anywhere between minutes to days on putting systems in place to prevent your AWS SES from being banned. You may also decide to "buy vs. build" and outsource these pains to a Notification Service altogether.