# Send Amazon SES bounces to Spaw through SNS

Subscribe an SNS topic to your Spaw feedback URL so SES permanent bounces, complaints and deliveries update your suppression list and measured accuracy.

Updated: 2026-09-03

Amazon SES reports what happened to a message through Amazon SNS: SES publishes a notification to a topic, and SNS delivers it to every subscriber. Spaw is built to be one of those subscribers. Subscribe your Spaw feedback URL to the topic and permanent bounces land on your suppression list, deliveries count as confirmed mailboxes, and the History tab measures how accurate the verdicts were for your own sending.

## Set up the subscription

1. In the Spaw dashboard, open the API keys page and generate a feedback key. Copy the SES URL it shows:

```text
https://spaw.co/api/v1/email/feedback/ses/fb_…
```

2. In the AWS console, create an SNS topic, or reuse the one your SES notifications already publish to.
3. Point SES at the topic. Either add an event destination of type SNS to the configuration set you send with and select the Bounce, Complaint and Delivery event types, or set the topic on the verified identity's notification settings for the same three types.
4. Add a subscription to the topic with protocol HTTPS and the Spaw URL as the endpoint.
5. SNS sends a `SubscriptionConfirmation` message to the URL. Spaw confirms it by fetching the `SubscribeURL` in that message, but only when it is an https URL on an `amazonaws.com` host, so nothing else can make Spaw fetch arbitrary addresses. The subscription shows as confirmed within a few seconds; if AWS retries the confirmation, that is handled the same way.

Raw message delivery can be on or off. With it off, the notification arrives as an SNS envelope whose `Message` field is a JSON string, and Spaw decodes it; with it on, the bare SES notification arrives and is read directly. SNS posts JSON with a `text/plain` content type, and Spaw decodes the body as JSON regardless.

## What Spaw reads from the notification

Spaw looks at `notificationType`, or `eventType` for configuration-set events, and nothing else in the payload:

| SES notification | Recorded as |
| --- | --- |
| `Bounce` with `bounceType` Permanent | bounced, one outcome per address in `bouncedRecipients` |
| `Bounce` with `bounceType` Transient or Undetermined | ignored, SES retries these itself |
| `Complaint` | complained, one outcome per address in `complainedRecipients` |
| `Delivery` | delivered, one outcome per address in `delivery.recipients` |

For a bounce the recipient's `diagnosticCode` is stored as the reason, falling back to the `bounceSubType`; for a complaint the `complaintFeedbackType`. The notification's timestamp becomes the time of the outcome. A permanent bounce looks like this once unwrapped:

```json
{
  "notificationType": "Bounce",
  "bounce": {
    "bounceType": "Permanent",
    "bounceSubType": "General",
    "timestamp": "2026-09-03T10:12:44.000Z",
    "bouncedRecipients": [
      { "emailAddress": "mia@acme.com", "diagnosticCode": "smtp; 550 5.1.1 user unknown" }
    ]
  }
}
```

## What Spaw does with the events

A permanent bounce or a complaint adds the address to your suppression list with source `feedback`. Batch, bulk and monitor runs then answer it from the stored verdict at no cost, marked `suppressed` with a risk score of 100, until a single lookup re-verifies it. A delivery removes any entry Spaw had added automatically, and for 90 days the address counts as a confirmed mailbox for your own lookups, reported as `smtp_reason: "delivered_recently"` without a probe.

Each outcome is matched against the verdict you were given for the address in the previous 90 days, which is what the feedback summary endpoint and the History tab report: the delivery rate among addresses answered `deliverable` and the bounce rate among addresses answered `undeliverable`. Outcomes are kept for 180 days.

## Keep the URL private

SNS cannot send a bearer token, so the feedback key in the URL is the credential. Anyone holding the URL can add outcomes to your account. If it leaks, rotate the key on the API keys page: every old URL stops working at once and answers `401 INVALID_FEEDBACK_KEY`, and you update the SNS subscription with the new one.

## Cost

Feedback is free and never logged as a lookup. The endpoint is throttled at 120 requests per minute per IP; SNS retries a request that is throttled or times out on its own schedule.

Reference: https://spaw.co/integrations/amazon-ses
