I’ve been setting up something for a relative who’s trying to start a business, and as part of that they needed to be able to send transactional emails. After looking through some of the services available, Amazon SES looked like a good option. You get a fairly large number of emails for free every day, and additional emails are available at a decent price paid per email rather than the big “pay $X to get 10,000 more emails” kind of packages that most other providers seem to offer.

Let’s sign up with AWS!

So I signed up for AWS, created my access tokens, imported the SES client into the app, and verified a domain so I could start testing. Phew! It did work pretty painlessly at this point, I was sending my test emails and receiving them very quickly.

But there’s a weird thing Amazon does for SES where your SES account will start out in a sandbox mode where you can only send test emails to your own verified domains or inboxes. To actually start sending emails, you need to request approval first. I thought this was sort of a formality, but it turns out that’s not the case because AWS very quickly rejected my request. Now I get it, spam emails are a big problem and Amazon doesn’t want spammers abusing SES, which could hurt SES’s email reputation. If you are unfamiliar, if email services decide your IP address is a spammer, your emails will start going straight into the spam filter. With SES, you use their shared IP addresses unless you pay extra to reserve your own IP address, so a spammer using SES would cause issues for everyone who’s not paying for a dedicated IP.

This was very frustrating though, because I then decided to sign up for SendGrid who banned my account immediately before I could even complete the sign up. It was a bizarre experience to receive a “here’s the code to verify your email address” email and a “you have been banned, goodbye” email simultaneously. What did I even do?

I was able to get approved after some back and forth with the AWS support team, and I wanted to write about this in case others hit this same issue because the feedback AWS gives you is basically nonexistant. When my request was rejected, the response I got just said:

We reviewed your request and determined that your use of Amazon SES could have a negative impact on our service. We are denying this request to prevent other Amazon SES customers from experiencing interruptions in service. For security purposes, we are unable to provide specific details.

Oof. I was especially confused because I had explicitly described that I would only be sending transactional emails to paying customers, and only once just to deliver their order after they had paid. This is as far away as you can get from spam, the only way you would receive an email is if you asked for it and paid. But I think I understand a few things AWS customer support team is looking for before they approve your request. I wish they would just describe this, but I guess that’s the “security purposes”.

What to put in your production access request

  1. Note how many emails you’ll be sending. Give your best estimate. This won’t be your sending limit, I said I’d send 100 emails a day and got a quota for 50,000 emails per day. So there’s no need to over-estimate to get a higher limit or to lie that you’ll send less, your best estimate should be good enough.
  2. Explain what you’ll do with bounces, complaints, and unsubscribe requests. These might not even make sense for you, for example in my case the emails will only be sent to paid customers after their successful transaction, there are no recurring emails so nothing to unsubscribe from. But explaining that wasn’t enough for AWS, I had to also explain that I would stop sending emails to any bounced addresses or to anyone who complains. If you don’t have the capability to do that in your code, make sure to implement it first.
  3. Attach at least 1 screenshot of the emails you’ll be sending. A picture is worth a thousand words and all, I think this gets your point across much more quickly. I’m not sure if you can attach a picture to your initial request, but I think you can comment again to attach a picture afterwards without waiting for them to reject you.
  4. Write down where you’ll be getting the email addresses to send emails to, even if it feels obvious. I had already said I was going to send emails to customers who bought something, but I think this wasn’t clear enough. In followups I described that customers would enter their email addresses when making a purchase, and added a screenshot of the checkout page where it clearly says “your purchase will be sent to this email address”. I think showing this also helped.

I’m sure there are other things to consider and explain, but this worked for me. If you get denied, reopen the case, add even more screenshots and information and try again.

Brevo

Alternatively, I also signed up for Brevo. It works, and was honestly easier to set up than SES because I didn’t have to pull in AWS client libraries. Instead I just had to call fetch and that was it. Hey, here is the code for that actually:

export type Email = {
  sender: {
    name: string;
    email: string;
  };
  to: string;
  content: {
    text: string;
    html: string;
  };
  subject: string;
};

export async function sendEmailBrevo({
  sender,
  to,
  content: { html: htmlContent, text: textContent },
  subject,
}: Email) {
  await fetch("https://api.brevo.com/v3/smtp/email", {
    method: "POST",
    headers: {
      accept: "application/json",
      "content-type": "application/json",
      "api-key": process.env.BREVO_API_KEY,
    },
    body: JSON.stringify({
      sender,
      to: [{ email: to }],
      subject,
      htmlContent,
      textContent,
    }),
  });
}