How do you get Amazon SES bounce and complaint notifications into a system you can act on, and why is email feedback forwarding not enough at scale?
answer
- configuration set carries event destinations
- choose which event types to publish
- SNS to act, Firehose to analyse
- permanent versus transient bounce
- complaints are always permanent
basics
~20 sAttach an SES configuration set with an event destination — SNS, Kinesis Data Firehose, CloudWatch or EventBridge — and subscribe to the Bounce and Complaint event types. Email feedback forwarding just drops human-readable mail into a mailbox nobody parses or monitors.
solid answer
~50 sSES emits structured sending events, and you route them by attaching an **event destination** to a **configuration set**, then sending with that configuration set — or making it the identity's default so no send can bypass it. You choose which event types to publish: `SEND`, `DELIVERY`, `BOUNCE`, `COMPLAINT`, `REJECT`, `DELIVERY_DELAY`, `RENDERING_FAILURE`, `OPEN`, `CLICK`, `SUBSCRIPTION`. Destinations differ in purpose: SNS pushes each event as JSON for immediate handling, Kinesis Data Firehose batches to S3 or OpenSearch for analysis, CloudWatch turns events into dimensioned metrics you can alarm on, and EventBridge gives you rule-based routing. A consumer then reads `bounce.bounceType` to separate `Permanent` from `Transient`, and treats every complaint as permanent. Email feedback forwarding delivers a human-readable report to a mailbox instead — fine for a pilot, useless once you have real volume, because nothing parses it and nobody reads it.
code
json · 20 lines{
"eventType": "Bounce",
"mail": {
"messageId": "0100018f-0000-0000-0000-000000000000",
"source": "[email protected]",
"destination": ["[email protected]"],
"tags": { "stream": ["transactional"] }
},
"bounce": {
"bounceType": "Permanent",
"bounceSubType": "General",
"bouncedRecipients": [
{
"emailAddress": "[email protected]",
"status": "5.1.1",
"diagnosticCode": "smtp; 550 5.1.1 user unknown"
}
]
}
}go deeper
Know that SES reports bounces and complaints as events, that you must stop mailing an address that hard-bounced or complained, and that a returned message ID does not mean the mail arrived.
Explain the wiring: a configuration set with an event destination to SNS, Firehose, CloudWatch or EventBridge, which event types you subscribe to, and how bounceType splits permanent from transient handling.
Show the operational judgment: default the configuration set on the identity so nothing bypasses it, make the consumer idempotent against at-least-once delivery, and alarm on reputation metrics below the level where AWS intervenes.
Own the feedback architecture across every sending stream in the organisation: who consumes the events, how suppression state reconciles with product databases, and what tagging scheme makes a reputation problem attributable to one team within minutes.
## The obligation behind the question Sending on SES is a privilege tied to your reputation, and AWS makes handling bounces and complaints a condition of keeping it. High bounce or complaint rates put an account under review and can end in a sending pause. So "how do you get feedback" is not a plumbing question — it is the mechanism by which you stay allowed to send. ## Configuration sets and event destinations A **configuration set** is a named bundle of sending rules you attach to a send. One of the things it carries is a list of **event destinations**, each of which says: publish these event types to this AWS target. The event types SES can publish include `SEND` (accepted by SES), `DELIVERY` (accepted by the receiving mail server), `BOUNCE`, `COMPLAINT`, `REJECT` (SES refused it, typically a virus detection), `DELIVERY_DELAY`, `RENDERING_FAILURE` (template substitution failed), `OPEN`, `CLICK` and `SUBSCRIPTION`. The destination choices are not interchangeable: - **SNS** — one JSON message per event, pushed immediately. Fan out to an SQS queue and a Lambda consumer. This is the shape you want for acting on individual bounces. - **Kinesis Data Firehose** — buffered delivery to S3, OpenSearch or Redshift. This is the analytics path: per-campaign bounce analysis, long-term retention, ad-hoc querying. - **CloudWatch** — events become metrics with dimensions you define (from message tags), so you can alarm on a bounce-rate spike per tenant or per stream. - **EventBridge** — rule-based routing to many targets when different event types belong to different consumers. Most mature setups use two at once: SNS or EventBridge for the act-on-it path, Firehose for the keep-and-analyse path. ## Making sure nothing bypasses it The classic gap is a send that forgets to name the configuration set, so its events go nowhere. Set a **default configuration set on the email identity** so any send from that identity picks it up whether or not the caller asked. This matters most for the SMTP interface and for third-party code paths you do not control. ## Reading the event A bounce event is JSON. The fields you actually branch on: - `bounce.bounceType` — `Permanent`, `Transient` or `Undetermined`. - `bounce.bounceSubType` — the reason, for example `General`, `NoEmail`, `MailboxFull`, `Suppressed`. - `bounce.bouncedRecipients[]` — per-recipient, with the receiving server's diagnostic text. - `mail.messageId` — correlate back to your own send record. A complaint event carries `complaint.complainedRecipients[]` and often a `complaintFeedbackType` such as `abuse`. Complaints arrive through ISP feedback loops that SES participates in on your behalf — a recipient hits "this is spam" and the ISP reports it back. The handling rules are simple and interviewers expect them stated plainly: - **Permanent bounce** — stop sending to that address, permanently. The mailbox does not exist. - **Transient bounce** — retry later with backoff; a full mailbox or a temporarily deferring server may accept tomorrow. Retrying forever is how a transient turns into a reputation problem, so cap it. - **Complaint** — stop sending, always, and never on a retry timer. A complaint is a person saying stop. SES will also add hard-bounced and complained addresses to your account-level suppression list automatically, but that protects the *account*; your own database still needs to know, or your product will keep showing the user as subscribed. ## Consumer engineering Two properties of the delivery path shape the consumer. SNS delivery is **at-least-once**, so the same bounce can arrive twice — make suppression idempotent, which it naturally is if you upsert by address. And events are **not ordered**, so a `DELIVERY` and a later `COMPLAINT` for the same message can arrive in either order; branch on event type rather than assuming a sequence. Include a message tag carrying your own tenant or campaign identifier at send time, so the event tells you which stream misbehaved without a database lookup. ## Watching the rate, not just the events Enable reputation metrics so SES publishes `Reputation.BounceRate` and `Reputation.ComplaintRate` to CloudWatch under the `AWS/SES` namespace, and alarm well below the level at which AWS would intervene. As of 2025 AWS's published guidance puts a bounce rate above 5% and a complaint rate above 0.1% into the range where an account comes under review, so alarm thresholds should sit under those, not at them. The number matters less than the discipline: you want to find the broken import job on the first thousand messages, not the hundred-thousandth. ## Why email feedback forwarding is not the answer Email feedback forwarding — SES's default for verified domains — mails you a human-readable bounce report. It is genuinely useful for a first-week pilot, because it needs no setup. Past that it fails on every axis: nothing parses it into your database, the volume buries a shared mailbox, there is no metric to alarm on, and it silently competes with the mailbox's own spam filtering. And if you configure a custom MAIL FROM, bounces route through your own subdomain's MX record, which makes the forwarding path even less like something you want to depend on.
- Your bounce consumer receives the same permanent bounce twice. Is that a bug?No. SNS delivery is at-least-once, so duplicates are expected and your handler must be idempotent. Suppressing by address naturally is — upserting the same address twice changes nothing. The bug would be a handler that increments a per-address counter or sends an alert on every event, because duplicates then inflate metrics and page people twice for one failure.
- How would you separate transactional and marketing feedback if both send from the same domain?Use a configuration set per stream, each with its own event destination, and add message tags naming the stream. CloudWatch dimensions then split the reputation view, and a marketing complaint spike does not hide inside the transactional volume. Same-domain streams still share account-level reputation, so isolating the signal is what lets you find the offending stream quickly.
- Which event destination would you pick to alarm on a bounce-rate spike, and why?CloudWatch, because it turns events into dimensioned metrics that an alarm can evaluate directly. SNS gives you individual events but no aggregate, and Firehose delivers on a buffering delay that is too slow to page on. In practice you run CloudWatch alongside SNS: one for the alarm, one for acting on each bounce.
- Why not simply rely on the account-level suppression list instead of processing bounce events?Because suppression protects your AWS account's sending reputation, not your product. Your own database still shows the user as reachable, so the app keeps queuing mail, showing them as subscribed, and counting sends that never happen. Processing the events is how your system's state stays true — suppression is a backstop underneath it, not a substitute.
saying these in an interview costs you the question
- Sending without a configuration set and expecting events anyway
- Retrying permanent bounces because the send returned a message ID
- Treating a complaint as something to retry later
- Assuming SES events arrive exactly once and in order
- Relying on a shared mailbox to catch forwarded bounce reports