add Amazon SES
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Kaan Barmore-Genç 2023-10-03 00:18:17 -05:00
parent d39d3ba79b
commit 82a9aa3779
Signed by: kaan
GPG key ID: B2E280771CD62FCF

View file

@ -0,0 +1,134 @@
---
title: "Amazon SES Production Access Approval"
date: 2023-10-03T04:34:37Z
toc: false
images:
tags:
---
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](https://www.brevo.com/). 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:
```ts
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,
}),
});
}
```