A Go library for handling Amazon SES incoming email notifications via Amazon SNS. This handler processes SNS notifications, verifies payload signatures, downloads email content from S3, and parses MIME messages with spam and authentication verdicts.
- ✅ SNS Payload Verification: Automatically verifies SNS message signatures to ensure authenticity
- ✅ Subscription Confirmation: Handles SNS subscription confirmation requests
- ✅ Email Processing: Processes incoming email notifications from Amazon SES
- ✅ S3 Integration: Downloads email content from S3 buckets when configured
- ✅ MIME Parsing: Parses MIME email content with support for attachments
- ✅ Security Verdicts: Extracts spam, SPF, DKIM, and DMARC verdicts from SES receipts
- ✅ Spam Detection: Implements spam filtering logic based on SES verdicts
go get github.com/mailio/go-mailio-amazon-ses-handlerpackage main
import (
"net/http"
"github.com/aws/aws-sdk-go-v2/aws"
amazonseshandler "github.com/mailio/go-mailio-amazon-ses-handler"
)
func main() {
// Initialize AWS config
cfg := aws.Config{
Region: "us-east-1",
// Add your AWS credentials here
}
// Create handler
handler := amazonseshandler.NewAmazonSESHandler(cfg)
// HTTP handler function
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
mail, err := handler.ReceiveMail(*r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if mail == nil {
// Subscription confirmation or other notification type
w.WriteHeader(http.StatusOK)
return
}
// Process the email
// mail contains parsed MIME content, verdicts, and raw MIME data
w.WriteHeader(http.StatusOK)
})
http.ListenAndServe(":8080", nil)
}The handler returns a *abi.Mail object that contains:
- Parsed MIME content: Headers, body, attachments
- Security verdicts: Spam, SPF, DKIM, DMARC status
- Raw MIME: Original email content as bytes
mail, err := handler.ReceiveMail(*r)
if err != nil {
// Handle error
}
// Access parsed email data
subject := mail.Subject
from := mail.From
body := mail.Body
// Check security verdicts
if mail.SpamVerdict != nil {
spamStatus := mail.SpamVerdict.Status // "PASS" or "FAIL"
}
// Access raw MIME content
rawMime := mail.RawMimeThis section provides step-by-step instructions for configuring Amazon SES to receive emails, store them in S3, send notifications via SNS, and handle failures with a Dead Letter Queue.
- An active AWS account
- AWS CLI configured with appropriate permissions
- Permissions to create and manage:
- Amazon SES (receiving email)
- Amazon S3 (bucket for email storage)
- Amazon SNS (notifications)
- Amazon SQS (Dead Letter Queue)
-
Navigate to Amazon S3 Console
- Go to Amazon S3 console
-
Create a New Bucket
- Click "Create bucket"
- Enter a unique bucket name (e.g.,
my-email-storage-bucket) - Select your preferred AWS region
- Click "Create bucket"
-
Configure Bucket Policy for SES
- Go to your bucket → Permissions → Bucket policy
- Add the following policy (replace
YOUR_ACCOUNT_IDandBUCKET_NAME):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSESPuts",
"Effect": "Allow",
"Principal": {
"Service": "ses.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*",
"Condition": {
"StringEquals": {
"aws:Referer": "YOUR_ACCOUNT_ID"
}
}
}
]
}For more details, see: Amazon SES Receiving Email Permissions
-
Navigate to Amazon SES Console
- Go to Amazon SES console
- Select "Verified identities" from the left menu
-
Verify Your Domain
- Click "Create identity"
- Select "Domain"
- Enter your domain name
- Follow the DNS verification steps to add the required DNS records
-
Request Production Access (if in SES Sandbox)
- If you're in the SES sandbox, request production access to receive emails from any address
- Go to "Account dashboard" → "Request production access"
-
Navigate to Amazon SNS Console
- Go to Amazon SNS console
-
Create a Topic
- Click "Create topic"
- Choose "Standard" topic type
- Enter a topic name (e.g.,
ses-email-notifications) - Click "Create topic"
-
Note the Topic ARN
- Copy the Topic ARN (e.g.,
arn:aws:sns:us-east-1:123456789012:ses-email-notifications) - You'll need this when configuring SES receipt rules
- Copy the Topic ARN (e.g.,
-
Navigate to Amazon SQS Console
- Go to Amazon SQS console
-
Create a Queue for DLQ
- Click "Create queue"
- Choose "Standard" queue type
- Enter a queue name (e.g.,
ses-notifications-dlq) - Configure retention period (default: 4 days, max: 14 days)
- Click "Create queue"
-
Note the Queue ARN
- Copy the Queue ARN for later use
-
Create an HTTP/HTTPS Subscription
- In your SNS topic, click "Create subscription"
- Protocol: "HTTPS" (or "HTTP" for development)
- Endpoint: Your webhook URL (e.g.,
https://your-domain.com/webhook) - Click "Create subscription"
-
Confirm the Subscription
- AWS will send a subscription confirmation request to your endpoint
- The handler automatically confirms subscriptions when it receives a
SubscriptionConfirmationmessage - Verify the subscription status shows "Confirmed" in the SNS console
-
Configure Dead Letter Queue for Subscription
- In your SNS topic, select the subscription
- Click "Edit" → "Redrive policy"
- Enable "Dead-letter queue"
- Select your DLQ queue
- Set "Maximum receives" (recommended: 3-5)
- Click "Save changes"
For more details, see: Amazon SNS Dead-Letter Queues
-
Navigate to Amazon SES Console
- Go to Amazon SES console
- Select "Email receiving" → "Rule sets" from the left menu
-
Create or Edit a Rule Set
- If you don't have a rule set, create one and set it as active
- Click on your rule set to edit it
-
Create a Receipt Rule
-
Click "Create rule"
-
Rule name: Enter a name (e.g.,
store-and-notify) -
Recipients:
- Add email addresses or domains that should trigger this rule
- Example:
@yourdomain.comorincoming@yourdomain.com
-
Actions (add multiple actions):
Action 1: Store in S3
- Click "Add action" → "S3"
- Select your S3 bucket
- Optionally set an object key prefix (e.g.,
emails/) - Click "Save"
Action 2: Publish to SNS
- Click "Add action" → "SNS"
- Select your SNS topic ARN
- Click "Save"
-
Click "Create rule"
-
-
Verify Rule Order
- Rules are processed in order
- Ensure your rule is positioned correctly in the rule set
-
Get Your SES Receiving Endpoint
- In SES console, go to "Email receiving" → "Rule sets"
- Note the receiving endpoint (e.g.,
inbound-smtp.us-east-1.amazonaws.com)
-
Update DNS MX Records
- In your domain's DNS settings, add an MX record:
- Name:
@or your subdomain - Priority:
10 - Value: Your SES receiving endpoint
- Name:
- In your domain's DNS settings, add an MX record:
-
Wait for DNS Propagation
- DNS changes can take up to 48 hours to propagate
- Use
digornslookupto verify the MX record
For more details, see: Amazon SES Receiving Email Concepts
-
Send a Test Email
- Send an email to an address configured in your receipt rule
- Example:
test@yourdomain.com
-
Verify Email Storage
- Check your S3 bucket for the stored email
- The email should appear with a timestamp-based object key
-
Check SNS Notifications
- Verify your webhook endpoint receives the SNS notification
- Check CloudWatch Logs for any errors
-
Test Dead Letter Queue
- Temporarily disable your webhook endpoint
- Send a test email
- After the maximum retry attempts, check your DLQ for the message
The handler processes the following notification types:
- SubscriptionConfirmation: Automatically confirms SNS subscriptions
- Notification with type:
- Received: Processes incoming emails (downloads from S3, parses MIME, extracts verdicts)
- Bounce: Email bounce notifications (returns nil)
- Complaint: Email complaint notifications (returns nil)
- Delivery: Email delivery notifications (returns nil)
- Reject: Email rejection notifications (returns nil)
- Send: Email send notifications (returns nil)
The handler extracts and processes the following security verdicts from SES:
- SpamVerdict: Spam detection status (
PASSorFAIL) - VirusVerdict: Virus detection status (
PASSorFAIL) - SPFVerdict: SPF authentication status (
PASS,FAIL, orGRAY) - DKIMVerdict: DKIM authentication status (
PASSorFAIL) - DMARCVerdict: DMARC authentication status (
PASSorFAIL)
The handler implements spam filtering logic:
- Emails are considered spam if:
SpamVerdict = PASSANDVirusVerdict = PASSANDSPFVerdict = PASS or GRAY→ Not spam - Emails with viruses are always flagged
- Other combinations default to not spam
The handler returns errors in the following cases:
- Invalid JSON payload
- Failed SNS signature verification
- Missing S3 bucket/key when MIME content is not included
- S3 download failures
- MIME parsing errors
Always check for errors when calling ReceiveMail():
mail, err := handler.ReceiveMail(*r)
if err != nil {
log.Printf("Error processing email: %v", err)
// Handle error appropriately
return
}Run the test suite:
go test ./...The tests include:
- Subscription confirmation handling
- Notification processing with MIME content
- Signature verification
- Amazon SES Receiving Email Concepts
- Amazon SES SNS Notifications
- Amazon SNS Dead-Letter Queues
- Mail Manager – Amazon SES Email Routing and Archiving
See LICENSE file for details.