Why Your AWS SES Setup Is One Bad Deploy Away from an Account Suspension
You've got Amazon SES running in production. Emails are going out. Customers are receiving them—mostly. Everything feels fine.

Kash Sajadi
You've got Amazon SES running in production. Emails are going out. Customers are receiving them—mostly. Everything feels fine.
Then one morning you wake up to a customer support thread that's been building for six hours: a subset of users never got their password reset emails, a transactional notification was silently failing, and your bounce rate has been quietly climbing toward SES's 5% threshold for the better part of a week.
Nobody caught it. Not because your team is inattentive—because SES doesn't tell you until it's already a problem.
This is the operational gap that trips up almost every team that runs SES at scale. The sending infrastructure itself is genuinely solid. The observability around it is, by default, basically nonexistent.
The Threshold Problem Nobody Talks About
Amazon SES has two hard limits that can get your account suspended:
- Bounce rate above 5% → sending paused
- Complaint rate above 0.1% → sending paused
That complaint rate threshold is not a typo. One complaint per thousand sends. If you're sending 50,000 emails a day across transactional and marketing traffic, you can trigger a review with five complaints. SES will send you a notification—to an email address you may or may not be monitoring—and pause your account.
The math isn't the scary part. The scary part is that without active monitoring, you won't see the rate climbing until it's already at or past the threshold. You'll find out from a customer, or from a developer at 11pm pulling logs.
SES is designed as a sending engine. It records events—bounces, complaints, deliveries, opens, clicks—and it will happily publish them to SNS, EventBridge, or Kinesis if you configure it to do so. But that configuration doesn't come with a dashboard. It doesn't come with alerting. It doesn't come with any default visibility into whether your sending health is trending toward suspension.
That work is left to you.
What "Monitoring SES Properly" Actually Involves
Let's be concrete about what it takes to set up meaningful SES observability from scratch.
Step 1: Configure a notification destination. You need to route SES events somewhere—typically an SNS topic. Simple enough to start, but you'll need a separate configuration set for each domain or use case if you want any granularity.
Step 2: Process those events. Raw SNS payloads don't make a dashboard. You need something to consume them: usually a Lambda function that parses the event type, extracts the relevant fields, and writes somewhere queryable.
Step 3: Store the data. You're probably writing to CloudWatch Logs, S3, or DynamoDB depending on your volume and query patterns. Each choice has trade-offs in cost, latency, and query complexity.
Step 4: Build the dashboard. CloudWatch has a query language. It works. Writing the queries to surface bounce rate over time, broken down by configuration set, with alerting thresholds—that's a real project. Not a weekend one.
Step 5: Set up alarms. CloudWatch Alarms can fire to SNS, which can notify Slack or PagerDuty. Wiring this together for the right metrics—with sensible thresholds and enough context in the alert to be actionable—takes iteration.
Step 6: Maintain all of this. Lambda runtimes need updates. IAM policies drift. CloudWatch Logs get expensive if retention isn't configured. The SNS subscription stops working after some edge case you didn't anticipate. Someone on the team changes a configuration set name and the whole pipeline silently breaks.
None of these individual steps is impossible. The problem is the aggregate: you've now built a secondary piece of infrastructure whose job is to tell you whether your primary infrastructure is working. And that secondary system needs its own maintenance, its own documentation, and its own on-call coverage.
The Template Problem Is Separate and Also Real
Bounce rate monitoring is the most acute risk, but template management is where teams accumulate quiet technical debt.
SES stores templates in your account. Editing them means going to the console (or using the CLI), making a change directly to the live version, and hoping you didn't break anything. There's no version history. There's no review step. There's no way to preview what a change looks like before it goes to a customer.
Most teams work around this by building their own deployment scripts—a templates/ directory in the app repo, a CI job that syncs to SES on merge. This works, more or less. But the script is usually the third or fourth thing that broke in an incident, it's documented in a README that's eight months out of date, and the one engineer who knows how it works is currently on vacation.
The subtler problem: template ownership ends up stuck in engineering. A marketing team that wants to change copy in a welcome email has to file a ticket and wait for a developer to deploy it. A support team that wants to confirm what template version a customer received has no way to check. Email becomes a coordination overhead that scales badly with team size.
What Actually Reduces Risk Here
There's a version of this that teams solve well, and it shares some common traits.
Real-time rate tracking with automatic alerting. Not a dashboard you check—a system that tells you when your bounce rate crosses 2%, giving you time to investigate before you're at 5%. The alert should include enough context to act on: which configuration set, what time window, what the trend looks like.
Separation between monitoring and sending path. The worst thing about the DIY approach is that your observability pipeline has the same blast radius as your sending infrastructure. If you're processing SES events in the same Lambda that handles other workloads, a deployment gone wrong can take both down simultaneously.
Template versioning with audit history. At minimum, you want to know what version of a template was active when a given email was sent. Ideally, templates go through a review process before they hit production—even a lightweight one.
Access controls that don't require AWS credentials. If checking delivery status for a specific email requires CloudWatch Logs Insights access, most of your team won't do it. They'll ask an engineer, who will either spend ten minutes running the query or hand over credentials they shouldn't be sharing. Neither outcome is good.
The Build-vs-Buy Calculation
There's an honest version of this conversation: for some teams, building this internally is the right call. If you have an infrastructure team, a stable CloudWatch investment, and email volume that makes per-email pricing from managed providers untenable—building your own SES observability layer is a legitimate choice.
The honest version also includes the full cost. The initial build is weeks of engineering time. The maintenance is ongoing. Every time SES adds a new event type, someone has to update the pipeline. Every time a new team member needs access, someone has to figure out the permission model. The opportunity cost is whatever those engineers would otherwise be building.
The alternative isn't necessarily migrating to SendGrid or Postmark—accepting per-email pricing that compounds at scale and a weeks-long infrastructure rewrite. There's a middle option: a dedicated control plane that sits on top of your existing SES setup, adds the operational layer, and doesn't require touching your sending path.
That's what we built with SendOps. Not because DIY is impossible, but because the aggregate cost of doing it yourself is higher than most teams realize when they start, and the risk during the gap—while the monitoring system is still being built—is real.
The Takeaway
Amazon SES is good infrastructure. It's reliable, cost-effective, and deeply integrated with the rest of AWS. The risk isn't SES itself—it's operating it without visibility.
If your current setup means someone has to pull CloudWatch Logs to check whether an email was delivered, or your bounce rate could climb to 4% before anyone notices, or a template change requires a developer deploy—those aren't minor inconveniences. They're operational gaps with a real blast radius.
The question isn't whether to add visibility to your SES setup. It's whether you build that layer yourself, buy it, or keep deferring it until an incident makes the decision for you.
Deferring is the most expensive option, even when it doesn't feel that way.