| name | aws-ses-inbound |
| description | Set up AWS SES for receiving inbound emails with SNS webhook delivery |
| license | MIT |
| compatibility | opencode |
| metadata | [object Object] |
What I Do
Set up Amazon SES (Simple Email Service) for receiving inbound emails and forwarding them to a webhook via SNS (Simple Notification Service). This is commonly used for:
- AI email agents that process incoming emails
- Email-to-ticket systems
- Inbound email parsing APIs
Prerequisites
- AWS CLI installed:
brew install awscliorpip install awscli - AWS credentials with SES and SNS permissions
- Domain with configurable DNS (for MX records)
Credential Setup
Set credentials via environment variables:
export AWS_ACCESS_KEY_ID='AKIA...'
export AWS_SECRET_ACCESS_KEY='...'
export AWS_DEFAULT_REGION='us-east-1'
Or use AWS CLI profile:
aws configure --profile ses-admin
export AWS_PROFILE=ses-admin
Supported Regions for SES Inbound
SES inbound email is only available in:
us-east-1(N. Virginia)us-west-2(Oregon)eu-west-1(Ireland)
Complete Setup Flow
Step 1: Verify Domain in SES
# Register domain for verification
aws ses verify-domain-identity --domain example.com
# Response includes VerificationToken - add as TXT record:
# Name: _amazonses.example.com
# Value: <VerificationToken>
Step 2: Get DKIM Tokens
# Get DKIM tokens for email authentication
aws ses verify-domain-dkim --domain example.com
# Response includes 3 DkimTokens - add as CNAME records:
# Name: <token>._domainkey.example.com
# Value: <token>.dkim.amazonses.com
Step 3: Create SNS Topic
# Create topic for receiving email notifications
aws sns create-topic --name my-inbound-email-topic
# Save the TopicArn from response
Step 4: Create SES Receipt Rule Set
# Create a rule set (container for rules)
aws ses create-receipt-rule-set --rule-set-name my-email-rules
Step 5: Create Receipt Rule
# Create rule to forward emails to SNS
aws ses create-receipt-rule \
--rule-set-name my-email-rules \
--rule '{
"Name": "forward-to-sns",
"Enabled": true,
"Recipients": ["example.com"],
"Actions": [
{
"SNSAction": {
"TopicArn": "arn:aws:sns:us-east-1:123456789:my-inbound-email-topic",
"Encoding": "UTF-8"
}
}
],
"ScanEnabled": true
}'
Step 6: Activate Rule Set
# Only one rule set can be active at a time
aws ses set-active-receipt-rule-set --rule-set-name my-email-rules
Step 7: Subscribe Webhook to SNS
# Subscribe your HTTPS endpoint to the SNS topic
# IMPORTANT: Use --notification-endpoint, NOT --endpoint
# (--endpoint overrides the AWS API URL, which is NOT what you want)
aws sns subscribe \
--topic-arn "arn:aws:sns:us-east-1:123456789:my-inbound-email-topic" \
--protocol https \
--notification-endpoint "https://example.com/api/email-webhook"
Important: Your webhook endpoint must handle the SNS subscription confirmation request. SNS sends a POST with:
- Header:
x-amz-sns-message-type: SubscriptionConfirmation - Body: JSON with
Type,SubscribeURL,Token, etc.
Your endpoint must visit the SubscribeURL (make a GET request) to confirm the subscription.
Required DNS Records
After running the SES commands, add these DNS records:
Domain Verification (TXT)
Name: _amazonses.example.com
Type: TXT
Value: <VerificationToken from verify-domain-identity>
DKIM Records (3 CNAMEs)
Name: <token1>._domainkey.example.com
Type: CNAME
Value: <token1>.dkim.amazonses.com
Name: <token2>._domainkey.example.com
Type: CNAME
Value: <token2>.dkim.amazonses.com
Name: <token3>._domainkey.example.com
Type: CNAME
Value: <token3>.dkim.amazonses.com
MX Record (for receiving email)
Name: example.com (or subdomain like mail.example.com)
Type: MX
Priority: 10
Value: inbound-smtp.<region>.amazonaws.com
For us-east-1: inbound-smtp.us-east-1.amazonaws.com
Checking Status
Verify Domain Status
aws ses get-identity-verification-attributes --identities example.com
# VerificationStatus should be "Success"
Check DKIM Status
aws ses get-identity-dkim-attributes --identities example.com
# DkimVerificationStatus should be "Success"
List Receipt Rules
aws ses describe-active-receipt-rule-set
aws ses describe-receipt-rule --rule-set-name my-email-rules --rule-name forward-to-sns
List SNS Subscriptions
aws sns list-subscriptions-by-topic --topic-arn "arn:aws:sns:..."
# SubscriptionArn should show confirmed ARN, not "PendingConfirmation"
Webhook Payload Format
SNS sends JSON with this structure:
{
"Type": "Notification",
"MessageId": "...",
"TopicArn": "arn:aws:sns:...",
"Message": "{\"notificationType\":\"Received\",\"content\":\"<raw MIME email>\",...}",
"Timestamp": "...",
"Signature": "...",
"SigningCertURL": "..."
}
The Message field contains a JSON string with:
notificationType: "Received" for incoming emailscontent: Raw MIME email content (needs parsing)mail: Metadata (messageId, source, destination, headers)
Use a library like mailparser (Node.js) or email (Python) to parse the MIME content.
SNS Subscription Confirmation
When you first subscribe, SNS sends a confirmation request:
{
"Type": "SubscriptionConfirmation",
"SubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&...",
"Token": "...",
"TopicArn": "..."
}
Your webhook must:
- Detect
Type === "SubscriptionConfirmation" - Make a GET request to the
SubscribeURL - Return 200 OK
Signature Verification
Always verify SNS message signatures in production:
- Validate
SigningCertURLis from*.amazonaws.com - Fetch the certificate
- Build the canonical message string
- Verify signature using the certificate's public key
Troubleshooting
"Domain not verified"
- Check TXT record:
dig _amazonses.example.com TXT - DNS propagation can take up to 72 hours (usually 5-30 min)
"DKIM not verified"
- Check CNAME records:
dig <token>._domainkey.example.com CNAME - Ensure all 3 DKIM records are added
Emails not arriving
- Verify MX record points to correct SES inbound server
- Check receipt rule set is active
- Verify recipient domain/address matches rule
Webhook not receiving
- Check SNS subscription status (should not be "PendingConfirmation")
- Verify endpoint is publicly accessible via HTTPS
- Check endpoint handles SNS confirmation handshake
- Review CloudWatch logs for SNS delivery failures
"Access Denied" errors
- Verify IAM user has
AmazonSESFullAccessandAmazonSNSFullAccesspolicies - Check region is correct (SES inbound only in us-east-1, us-west-2, eu-west-1)