Amazon SES (Simple Email Service)

1. What is Amazon SES?

Amazon SES is a cloud-based email sending and receiving service — it lets your applications send transactional emails, marketing emails, and bulk campaigns at scale, and optionally receive inbound email.

Email categories SES handles:
  Transactional:  order confirmations, password resets, receipts, alerts
  Marketing:      newsletters, promotions, announcements
  Bulk:           large-scale campaigns to opt-in mailing lists
  Notification:   system alerts, CloudWatch alarms via email

SES does NOT:
  Provide a GUI email client (that's an MUA — use Gmail, Outlook)
  Replace an Exchange server (that's Microsoft 365 / WorkMail)
  Send SMS (that's SNS)

Why SES Instead of SMTP Server

Self-managed SMTP server:
  Maintain server 24/7 → patches, uptime, scaling
  Build/maintain IP reputation from scratch
  Handle bounces, complaints, blacklists manually
  Complex DKIM/SPF/DMARC setup

Amazon SES:
  Fully managed — no servers to maintain
  AWS-managed IP reputation (warm IPs with established reputation)
  Built-in bounce/complaint handling + suppression lists
  Easy DKIM with automatic key rotation every 90 days [captaindns](https://www.captaindns.com/en/blog/amazon-ses-transactional-email-technical-guide)
  Cost: $0.10 per 1,000 emails

2. Identities (Verification) ⭐

Before sending, you must verify the sending identity — SES confirms you own the domain or email address:

Email Address Identity

Verify a single email address:
  SES → Verified Identities → Create → Email address
  → SES sends verification link to that email
  → Click link → verified ✅

Limitation: can only send FROM this exact email address
Use case: quick testing, single-sender applications
Verify an entire domain:
  SES → Verified Identities → Create → Domain
  → SES provides DNS records to add to your domain (Route 53 or other)
  → DNS propagates → domain verified ✅

Benefit: send from ANY address at that domain
  noreply@ibtisam-iq.com ✅
  support@ibtisam-iq.com ✅
  orders@ibtisam-iq.com  ✅

DNS Records Added During Domain Verification

1. DKIM records (3 CNAME records for Easy DKIM)
   Automatically used for both domain verification AND DKIM signing

2. Optional MAIL FROM (custom MX record)
   Required for SPF DMARC alignment (see Section 4)

3. DMARC TXT record (you add manually)
   Not generated by SES — you write it based on your policy

3. Sandbox vs Production ⭐

All new SES accounts start in sandbox mode — a restricted environment to prevent abuse:

Sandbox Restrictions

Can only send TO: verified email addresses or domains (you own both sides)
Sending limit: 200 emails/day
Send rate:     1 email/second
Cannot send to: random email addresses (would get AccessDenied error)

Why Sandbox Exists

New AWS accounts are untrusted — SES cannot know if you will spam.
Sandbox forces you to prove:
  → You own the sending domain (verified identity)
  → You know who you're sending to (verified recipients)
  → You have basic bounce/complaint handling

Moving to Production

Submit a request via AWS Support → Service Limit Increase → SES Sending Limits. AWS reviews manually. Your request must demonstrate:

Required in your request:
  1. Website / use case description
     → "We send order confirmation emails to paying customers"

  2. Email type
     → Transactional / Marketing / Notification

  3. Opt-in mechanism
     → "Users check a box at registration confirming they want emails"
     → Double opt-in preferred (confirm email address via link)

  4. Bounce handling
     → "We have SNS + Lambda processing bounces in real-time
        and suppressing bounced addresses from future sends"

  5. Complaint handling
     → "We process SES complaint notifications and unsubscribe
        complainants immediately"

  6. Sending volume estimate
     → Daily and peak volumes

  7. Unsubscribe mechanism (for marketing)
     → One-click unsubscribe link in every email

Best practices to guarantee approval:
  → Verify domain (not just email address)
  → Configure bounce + complaint notifications (SNS)
  → Enable account-level suppression list [docs.aws.amazon](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_PutConfigurationSetSuppressionOptions.html)
  → Set up DKIM (Easy DKIM)
  → Have a real application/website to show

4. Email Authentication: SPF, DKIM, DMARC ⭐

These three protocols work together to prove your emails are legitimate:

SPF (Sender Policy Framework)

What: DNS TXT record listing which mail servers may send on behalf of your domain
How:  Receiving mail server checks MAIL FROM domain → looks up SPF record
      → is the sending IP in the allowed list? YES → SPF pass

DNS TXT record:
  Name: mail.ibtisam-iq.com    ← your custom MAIL FROM subdomain
  Value: "v=spf1 include:amazonses.com ~all"
    v=spf1           → SPF version
    include:amazonses.com → SES IP ranges are authorized
    ~all             → soft fail (mark suspicious, don't reject)
    -all             → hard fail (reject outright — use after testing)

Critical: SPF checks MAIL FROM domain (Return-Path / envelope sender)
          NOT the From: address you see in your email client
          → Must use custom MAIL FROM for SPF DMARC alignment [easydmarc](https://easydmarc.com/blog/amazon-ses-spf-and-dkim-configuration/)

DKIM (DomainKeys Identified Mail)

What: cryptographic signature on outgoing emails proving content wasn't modified
How:
  SES (sender): signs email with private key
  Receiving server: retrieves public key from DNS → verifies signature
  → Signature valid? Content intact? → DKIM pass

Easy DKIM (recommended): [docs.aws.amazon](https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-dkim.html)
  SES generates 2048-bit RSA key pair automatically
  Provides 3 CNAME records to add to your DNS:
    abc123._domainkey.ibtisam-iq.com → CNAME → abc123.dkim.amazonses.com
    def456._domainkey.ibtisam-iq.com → CNAME → def456.dkim.amazonses.com
    ghi789._domainkey.ibtisam-iq.com → CNAME → ghi789.dkim.amazonses.com
  → AWS rotates keys automatically every 90 days ← no manual maintenance [captaindns](https://www.captaindns.com/en/blog/amazon-ses-transactional-email-technical-guide)
  → These 3 CNAMEs also serve as domain verification records

BYODKIM (Bring Your Own DKIM):
  You generate 1024–2048 bit RSA key pair
  Add TXT record with public key to DNS
  Upload private key to SES
  You are responsible for rotation

DMARC (Domain-based Message Authentication, Reporting & Conformance)

What: policy that tells receiving servers what to do when SPF or DKIM fail
      AND ensures the From: address domain matches the authenticated domain

DMARC requires ALIGNMENT:
  SPF alignment:  MAIL FROM domain must match From: domain
  DKIM alignment: d= in DKIM signature must match From: domain
  At least one must align and pass → DMARC pass

DMARC DNS TXT record:
  Name:  _dmarc.ibtisam-iq.com
  Value: "v=DMARC1; p=quarantine; rua=mailto:dmarc@ibtisam-iq.com; pct=100"
    v=DMARC1        → DMARC version
    p=none          → policy: monitor only (start here — no rejection)
    p=quarantine    → send to spam folder if DMARC fails
    p=reject        → reject email entirely if DMARC fails (most strict)
    rua=mailto:...  → aggregate reports sent here (daily digest)
    ruf=mailto:...  → forensic reports (individual failures)
    pct=100         → apply policy to 100% of email

Rollout strategy:
  Phase 1: p=none → collect reports, find legitimate sources
  Phase 2: p=quarantine → test with pct=10, increase gradually
  Phase 3: p=reject → full protection against spoofing

The Three Work Together

Receiving server checks email from ibtisam@ibtisam-iq.com:

SPF check:
  Looks at MAIL FROM (Return-Path): mail.ibtisam-iq.com
  → DNS TXT lookup: mail.ibtisam-iq.com SPF record
  → SES IP in allowed list? → SPF PASS ✅
  → mail.ibtisam-iq.com matches ibtisam-iq.com? → SPF ALIGNMENT ✅

DKIM check:
  Finds DKIM-Signature header in email
  → d=ibtisam-iq.com in signature
  → DNS CNAME lookup: public key
  → Verifies signature → DKIM PASS ✅
  → d=ibtisam-iq.com matches From: ibtisam-iq.com? → DKIM ALIGNMENT ✅

DMARC check:
  Looks up _dmarc.ibtisam-iq.com
  → SPF aligned + passed OR DKIM aligned + passed → DMARC PASS ✅
  → p=reject but DMARC passed → deliver normally ✅

If DMARC FAILS (someone spoofing your domain):
  p=none:       deliver, send report to rua address
  p=quarantine: move to spam, send report
  p=reject:     refuse delivery, send report

5. Custom MAIL FROM Domain ⭐

By default, SES uses amazonses.com as the MAIL FROM (Return-Path) domain. This breaks SPF DMARC alignment because amazonses.com ≠ ibtisam-iq.com.

Default (no custom MAIL FROM):
  From:         ibtisam@ibtisam-iq.com     ← what user sees
  Return-Path:  bounce@amazonses.com       ← SPF checks THIS domain
  SPF passes for amazonses.com but fails alignment with ibtisam-iq.com
  → DMARC FAILS for SPF (only DKIM can save it)

With custom MAIL FROM (mail.ibtisam-iq.com):
  From:         ibtisam@ibtisam-iq.com
  Return-Path:  bounce@mail.ibtisam-iq.com ← SPF checks THIS domain
  → mail.ibtisam-iq.com is subdomain of ibtisam-iq.com → ALIGNMENT ✅
  → DMARC passes via SPF ✅

DNS records to add:
  MX:  mail.ibtisam-iq.com  10  feedback-smtp.us-east-1.amazonses.com
  TXT: mail.ibtisam-iq.com  "v=spf1 include:amazonses.com ~all"

Custom MAIL FROM is strongly recommended — it enables both SPF and DKIM to contribute to DMARC alignment, giving you redundant protection.


6. Sending Email — Methods

SMTP Interface

Use any application that supports SMTP:
  Host:     email-smtp.us-east-1.amazonaws.com
  Port:     587 (STARTTLS) or 465 (SSL) or 25
  Auth:     SMTP credentials (separate from IAM — generate in SES console)
  Username: SMTP username (starts with AKIA...)
  Password: SMTP password (different from access key secret)

Use case:
  Existing apps that use SMTP (WordPress, Jira, Jenkins notifications)
  CMS systems with built-in SMTP configuration
import boto3

ses = boto3.client('sesv2', region_name='us-east-1')

response = ses.send_email(
    FromEmailAddress='noreply@ibtisam-iq.com',
    Destination={
        'ToAddresses': ['customer@example.com'],
        'CcAddresses': [],
        'BccAddresses': []
    },
    Content={
        'Simple': {
            'Subject': {'Data': 'Your order has shipped', 'Charset': 'UTF-8'},
            'Body': {
                'Text': {'Data': 'Your order #1234 has shipped.', 'Charset': 'UTF-8'},
                'Html': {
                    'Data': '<h1>Order Shipped</h1><p>Your order #1234 is on its way.</p>',
                    'Charset': 'UTF-8'
                }
            }
        }
    },
    ConfigurationSetName='production-tracking'   # ← always use a configuration set
)

Sending from Lambda (Common Pattern)

# Lambda → SES → customer email
# IAM role must have: ses:SendEmail or ses:SendRawEmail permission

import boto3, os

def handler(event, context):
    ses = boto3.client('sesv2')
    ses.send_email(
        FromEmailAddress=os.environ['FROM_EMAIL'],
        Destination={'ToAddresses': [event['customerEmail']]},
        Content={
            'Simple': {
                'Subject': {'Data': f"Order #{event['orderId']} confirmed"},
                'Body': {'Html': {'Data': build_html_email(event)}}
            }
        },
        ConfigurationSetName='prod-config-set'
    )

7. Configuration Sets ⭐

A configuration set is a named group of rules applied to emails sent using that configuration set. Always attach one to every email send.

Configuration Set: "production-tracking"

Components:
  Event destinations → where to send email events (see below)
  Reputation options → track bounce/complaint rates
  Sending options    → enable/disable sending for this config set
  Suppression options → override account-level suppression (see Section 8)
  TLS policy         → REQUIRE TLS or OPTIONAL
  Virtual Deliverability Manager → ML deliverability insights

Event Destinations

Track what happens after you send:

Event Type Meaning
SEND SES accepted the email for delivery
RENDERING_FAILURE Template rendering failed
REJECT SES rejected before sending (virus, policy)
DELIVERY Email delivered to recipient's mail server
BOUNCE Mail server returned email undeliverable
COMPLAINT Recipient marked email as spam
OPEN Recipient opened the email (pixel tracking)
CLICK Recipient clicked a link
SUBSCRIPTION Subscription management event
Route events to:
  SNS   → real-time processing (Lambda → update DB, suppress address)
  Kinesis Firehose → stream to S3/analytics
  CloudWatch → metrics and alarms on delivery rates
  EventBridge → route to any downstream service

Essential configuration: [oneuptime](https://oneuptime.com/blog/post/2026-02-12-move-amazon-ses-out-of-sandbox/view)
  aws sesv2 create-configuration-set-event-destination \
    --configuration-set-name "production-tracking" \
    --event-destination-name "bounces-and-complaints" \
    --event-destination '{
      "Enabled": true,
      "MatchingEventTypes": ["BOUNCE", "COMPLAINT"],
      "SnsDestination": {
        "TopicArn": "arn:aws:sns:us-east-1:123456789012:ses-bounces"
      }
    }'

8. Bounces and Complaints ⭐

Email deliverability and sender reputation depend critically on keeping bounce and complaint rates low.

Bounce Types

Hard bounce (permanent failure):
  Address does not exist: user@nonexistentdomain.com
  Address permanently rejected: mailbox closed, blocked sender
  → NEVER send to this address again
  → Sending repeatedly → reputation destroyed → SES may suspend account

Soft bounce (temporary failure):
  Mailbox full, server temporarily unavailable
  → SES retries automatically for up to 72 hours
  → After exhausting retries → treated like hard bounce

Complaint

Recipient clicks "Mark as Spam" in Gmail/Outlook
→ ISP sends complaint notification to SES via Feedback Loop
→ SES triggers COMPLAINT event
→ You must stop sending to this person immediately
→ Continuing to send to complainants = major reputation damage

Reputation Thresholds

Bounce rate:
  < 2%  → Healthy
  2–5%  → Warning — AWS may pause sending
  > 5%  → Account sending suspended

Complaint rate:
  < 0.08% → Healthy (Gmail threshold: below 0.1%)
  0.08–0.1% → Warning
  > 0.1%  → Risk of suspension, deliverability severely impacted

These metrics in CloudWatch: AWS/SES namespace
  Reputation.BounceRate
  Reputation.ComplaintRate
  → Set alarms on both at warning thresholds

Suppression List ⭐

Account-level suppression list:
  Automatically adds addresses that result in hard bounce or complaint
  SES will not attempt delivery to suppressed addresses
  Suppressed addresses NOT counted toward bounce/complaint rate metrics

Enable:
  aws sesv2 put-account-suppression-attributes \
    --suppressed-reasons BOUNCE COMPLAINT

Configuration-set-level suppression override: [docs.aws.amazon](https://docs.aws.amazon.com/ses/latest/dg/sending-email-suppression-list-config-level.html)
  You can override account-level suppression per configuration set
  Example:
    Account: suppress BOUNCE + COMPLAINT
    Config set "transactional": suppress BOUNCE only
    → Transactional emails ignore complaint suppression
       (transactional emails like password resets are expected)

Manually add/remove addresses:
  Add:    aws sesv2 put-suppressed-destination --email-address bad@example.com --reason BOUNCE
  Remove: aws sesv2 delete-suppressed-destination --email-address recover@example.com
  List:   aws sesv2 list-suppressed-destinations

Global AWS suppression list:
  AWS maintains a list of addresses that bounced across ALL SES customers
  Addresses on this global list → SES suppresses even if not on your list
  Cannot override global suppression

9. Receiving Email

SES can receive inbound email for your verified domain:

Setup:
  1. Add MX record: ibtisam-iq.com MX 10 inbound-smtp.us-east-1.amazonaws.com
  2. Create receipt rules defining what to do with incoming email

Receipt rule actions:
  S3:         save raw email to S3 bucket
  Lambda:     invoke function with email content (process, parse, respond)
  SNS:        publish notification
  SES:        forward to another email address (bounce if unverified)
  WorkMail:   deliver to Amazon WorkMail
  Stop:       stop processing further rules

Receipt rule example:
  Rule: emails to support@ibtisam-iq.com
    → S3: save to my-email-bucket/support/
    → Lambda: parse email → create Jira ticket
    → SNS: notify on-call engineer

Use cases:
  Auto-reply system
  Email-to-ticket integration
  Email-based workflow triggers
  Catch-all inbox with Lambda processing

10. Dedicated IP Addresses

By default SES uses shared IPs — IPs shared with other SES customers:

Shared IPs:
  AWS manages reputation
  Good for low-to-medium volume
  Cheaper (no extra cost)
  Reputation affected by other customers' behavior (rare but possible)

Dedicated IPs:
  IP assigned exclusively to your account
  You own the reputation → send consistently to build it
  Required for: high volume, strict deliverability requirements
  Cost: ~$24.95/month per dedicated IP (plus usage)
  Warm-up: must gradually increase volume over weeks to build reputation

Dedicated IP Pools:
  Group dedicated IPs → use different pools for different email types
  Pool "transactional": high-reputation IPs for password resets
  Pool "marketing":     separate IPs for campaigns (isolate reputation)

11. Virtual Deliverability Manager (VDM)

ML-powered dashboard showing deliverability health:
  Open rates, click rates per ISP (Gmail, Outlook, Yahoo)
  Inbox placement rate
  Deliverability recommendations
  Optimal time-to-send suggestions

Enable per configuration set or account-level
Additional cost per processed email
Use for: large-scale senders needing per-ISP deliverability insights

12. SES Pricing

Sending:
  $0.10 per 1,000 emails
  Free tier: 3,000 emails/month free when sent from EC2 or Lambda

Attachments: $0.12 per GB of attachments sent

Receiving: $0.10 per 1,000 received emails
           First 1,000 received/month: FREE

Dedicated IPs: ~$24.95/month per IP

Virtual Deliverability Manager: $0.0015 per message processed

No charge for:
  Bounces/complaints to your SNS topics
  SES console usage
  Sandbox emails within limits

13. SES vs SNS for Email

Amazon SES Amazon SNS (email)
Purpose Rich email sending (HTML, attachments) Simple notification delivery
HTML support ✅ Full HTML, CSS inline ❌ Plain text only
Attachments
Bounce/complaint handling ✅ Built-in ❌ Manual
Unsubscribe management
Deliverability tracking ✅ Opens, clicks, delivery
Use case Transactional, marketing email Alert notifications, ops emails
Authentication DKIM/SPF/DMARC ❌ None
Cost $0.10/1,000 $2.00/100,000 notifications

Use SES for any email where formatting, deliverability, and tracking matter. Use SNS email only for quick internal alerts where you control all recipient addresses.


14. Common Mistakes

❌ Wrong ✅ Correct
SPF alone is enough for email authentication SPF + DKIM + DMARC all needed — DMARC requires alignment which SPF alone can't guarantee
Easy DKIM uses one DNS TXT record Easy DKIM uses 3 CNAME records — all three must be added
Default MAIL FROM is fine for production Default (amazonses.com) breaks SPF DMARC alignment — always set custom MAIL FROM
Start DMARC with p=reject Always start with p=none → collect reports → gradually move to reject
Sandbox restrictions are just rate limits Sandbox restricts recipients to verified addresses only — not just rate
Retry sending to hard bounced addresses Hard bounces = permanent — never retry, use suppression list
Complaint rate < 1% is fine Gmail threshold is < 0.1% — 1% will destroy deliverability
Suppressed addresses count toward bounce rate Suppressed addresses are NOT counted toward Reputation.BounceRate
SMTP credentials = IAM access keys SES SMTP credentials are separate from IAM — generated specifically in SES console
Configuration sets are optional Always use configuration sets — without them you cannot track bounces/complaints

15. Interview Questions Checklist

  • What is the difference between transactional and marketing email?
  • What are the two identity types in SES? Which is recommended for production?
  • What is sandbox mode? What are its restrictions?
  • What must you demonstrate to get production access?
  • Explain SPF — what does it verify? What DNS record does it use?
  • Explain DKIM — what does it verify? What is Easy DKIM?
  • Explain DMARC — what is alignment? What are the three policy values?
  • Why does custom MAIL FROM matter for DMARC alignment?
  • What is the difference between a hard bounce and a soft bounce?
  • What bounce rate triggers SES account suspension? (> 5%)
  • What complaint rate is dangerous for Gmail deliverability? (> 0.1%)
  • What is the account-level suppression list?
  • Can you override account-level suppression per configuration set?
  • What is a configuration set? What are event destinations?
  • Name all eight SES event types
  • Shared IPs vs dedicated IPs — when to use dedicated?
  • SES vs SNS for sending email — when to use which?
  • How does SES receive inbound email? What is an MX record change needed?
  • DMARC rollout strategy — three phases?
  • SMTP credentials in SES — how are they different from IAM credentials?