“Automated Customer Email Campaigns with AWS SES and Serverless Architecture”

In modern businesses, customer engagement through personalized emails is a key strategy to retain users and increase conversions. However, managing large-scale email campaigns manually can be inefficient. In this blog, we’ll build an automated email campaign system using AWS SES and Serverless Architecture, where emails are automatically triggered based on customer data or actions — without managing any servers.

By the end, you’ll have a fully automated system capable of:

  • Sending promotional or transactional emails
  • Tracking email campaign data
  • Running entirely on AWS Serverless components

Architecture Overview

1. Customer

The customer is the end user who triggers the workflow — for example, by performing an action such as signing up, placing an order, or requesting a password reset. This action initiates an automated email campaign through AWS Simple Email Service (SES).


2. AWS SES (Simple Email Service)

AWS SES is a cloud-based email service used to send and receive emails securely and at scale. It generates and sends personalized, dynamic emails using preconfigured SES Email Templates. These templates allow you to insert variables (like customer name or order details) to customize content automatically.


3. SES Email Template

The SES Email Template stores reusable HTML or text email formats. SES dynamically replaces placeholders in the template with user-specific information, ensuring personalized communication (e.g., “Hello Aditya, your order has been shipped”).


4. AWS SNS (Simple Notification Service)

AWS SNS acts as a messaging hub between SES and other AWS services. SES publishes email sending events (e.g., delivery, bounce, complaint) to an SNS topic. SNS then forwards these notifications to subscribers such as AWS Lambda or Amazon S3, enabling real-time processing and event tracking.


5. AWS Lambda

AWS Lambda is a serverless compute service that automatically runs code in response to SNS events. For instance, when SES sends a bounce notification via SNS, Lambda can process that data, update databases, or take corrective actions like disabling invalid email addresses. It can also read data from Amazon S3 for further processing or analytics.


6. Amazon S3

Amazon S3 (Simple Storage Service) is used to store event data or logs generated by SES and SNS. For example, detailed email delivery reports can be stored in S3 for auditing, analytics, or machine learning insights. Lambda can later read this data for automated analysis or reporting.

End to End Implementaion

Step 1 — Create S3 bucket and upload CSV (ap-south-1)

  1. Go to the AWS Management Console and open the S3 service.
  2. Click Create bucket and fill in the details:
  • Bucket name: use something unique, like yourname-customer-email-lists.
  • Region: select Asia Pacific (Mumbai) [ap-south-1].
  • You can keep all other default settings, or optionally enable Server-Side Encryption (SSE) or Versioning if your policy requires it.

3. Open bucket → Upload → add gmail_customers.csv → Upload.


Step 2 — Verify sender identity in SES (required) & request production access

You must send from a verified email or verified domain. Best: verify your domain (so you can send from any address on it).

Verify domain (recommended):

  1. Console → Services → SES → Region: ap-south-1.
  2. Left menu → Verified identities → Click Create identity.

3. Choose Domain → enter akashdevops.xyz (or domain you control) → enable Generate DKIM and click Create identity.

4.SES will show DNS records (TXT for verification, DKIM CNAMEs, and SPF instructions). Add these records to your DNS provider (Route53 or other). After DNS propagates, SES will show the domain as Verified. AWS Documentation

    (Or) Verify a single email address: same page → Create identity → Email address → verify (SES will send a verification email you must click).

    Request production access (if still in sandbox):

    5.SES Console → Account dashboard (or Sending statistics / Account details) → Find Request production access or Edit your account details → Follow the form: specify sending use case, website, sample email content, expected volumes. Submit. SES team reviews. AWS Documentation

      now add 3 cname record in hostinger record

      Step 3 — Create SES email template (dynamic)

      1. SES console → left → Email templates (or Templates) → Create template.
      2. Fill:
        • Template name: PromoTemplate
        • Subject: Hi {{name}} — Special Offer Just for You!
        • HTML Body:
      <h1>Hello {{name}}</h1>
      <p>We’ve got an exclusive offer for you. Click <a href="<https://yourwebsite.com>">here</a> to claim it.</p>
      • Text Body: Hello {{name}}, visit <https://yourwebsite.com> for your exclusive offer.

      3.Save template.

        Or via aws cli

        aws ses create-template \\
          --cli-input-json '{
            "Template": {
              "TemplateName": "PromoTemplate",
              "SubjectPart": "Hi {{name}} — Special Offer Just for You!",
              "HtmlPart": "<h1>Hello {{name}}</h1><p>We've got an exclusive offer for you. Click <a href=\\"<https://yourwebsite.com>\\">here</a> to claim it.</p>",
              "TextPart": "Hello {{name}}, visit <https://yourwebsite.com> for your exclusive offer."
            }
          }'

        Step 4 — Create SNS topics for feedback (bounces / complaints)

        1. Console → Services → SNS → Create topic → Type: Standard.
          • Name: ses-bounces-ap-south-1

        2. Create another topic: ses-complaints-ap-south-1.

          3.(Optional) Create subscriptions for these topics:

          • Example: HTTP webhook (your app), or SQS queue URL, or Lambda function (if you want a function to process bounces).
          • If you just want email alerts: Create subscription → Protocol: Email → Endpoint: your-admin-email → Confirm subscription via link in email.

            Step 5 — Create SES Configuration Set to route events to SNS

            1. SES console → Configuration setsCreate configuration set. Name: bulk-email-cfg.

            2. Inside the configuration set, Add destination → choose Amazon SNS as destination.

              3. For event types tick bounces, complaints and optionally deliveries. Select the SNS topics you created. Save. AWS Documentation

                When sending via the SES API, you must include the configuration set name (or set it in your SES sending configuration) so SES knows to push events to those SNS topics.

                Step 6 — Create IAM role & policies for Lambda (console)

                Create a role for Lambda with these minimum permissions:

                1. Console → Services → IAM → Roles → Create role → Select Lambda service.

                2. Attach these managed policies (quick start):

                • AWSLambdaBasicExecutionRole (for CloudWatch logs)
                • (Optional) AmazonS3ReadOnlyAccess (or create a narrow S3 policy below)

                3.After creation, edit role and attach a custom inline policy for SES send and narrow S3 access (example JSON below). Replace bucket name with yours.

                4. attach the policy to lambdarole

                  Custom policy (example)

                  {
                    "Version": "2012-10-17",
                    "Statement": [
                      {
                        "Sid": "AllowReadYourS3File",
                        "Effect": "Allow",
                        "Action": ["s3:GetObject"],
                        "Resource": ["arn:aws:s3:::yourname-customer-email-lists/*"]
                      },
                      {
                        "Sid": "AllowSESSend",
                        "Effect": "Allow",
                        "Action": [
                          "ses:SendBulkTemplatedEmail",
                          "ses:SendRawEmail",
                          "ses:SendEmail"
                        ],
                        "Resource": ["*"]
                      },
                      {
                        "Sid": "CloudWatchLogs",
                        "Effect": "Allow",
                        "Action": ["logs:CreateLogStream", "logs:CreateLogGroup", "logs:PutLogEvents"],
                        "Resource": ["arn:aws:logs:*:*:*"]
                      }
                    ]
                  }

                  Note: SES IAM resource ARNs aren’t required for Send APIs—Resource: “*” is typical; you can tighten later.

                  Step 7 — Create Lambda function (ap-south-1) — GUI paste code

                  Console → Services → Lambda → Create function

                  • Name: ses-bulk-sender
                  • Runtime: Python 3.11 (or 3.10)
                  • Permissions: Use the role you created above

                  2. Set up Environment variables (Lambda → Configuration → Environment variables):

                  • BUCKET_NAME = yourname-customer-email-lists
                  • TEMPLATE_NAME = PromoTemplate
                  • SOURCE_EMAIL = no-reply@oyebusy.in (must be a verified identity)
                  • BATCH_SIZE = 50 ← keep at 50 to match SES bulk limit
                  • REGION = ap-south-1

                  Paste this code into the inline editor (replace bucket/template/source as needed):

                  import boto3
                  import csv
                  import io
                  import json
                  import os
                  from botocore.exceptions import ClientError
                  
                  REGION = os.getenv('REGION', 'ap-south-1')
                  s3 = boto3.client('s3', region_name=REGION)
                  ses = boto3.client('ses', region_name=REGION)
                  
                  BUCKET = os.getenv('BUCKET_NAME')
                  TEMPLATE = os.getenv('TEMPLATE_NAME')
                  SOURCE = os.getenv('SOURCE_EMAIL')
                  BATCH_SIZE = int(os.getenv('BATCH_SIZE', '50'))
                  
                  def chunk_list(lst, n):
                      for i in range(0, len(lst), n):
                          yield lst[i:i+n]
                  
                  def lambda_handler(event, context):
                      # Support S3 event trigger OR manual env values
                      if 'Records' in event and event['Records'][0].get('s3'):
                          record = event['Records'][0]['s3']
                          bucket = record['bucket']['name']
                          key = record['object']['key']
                      else:
                          # fallback values (if you want to invoke manually)
                          bucket = BUCKET
                          key = os.getenv('OBJECT_KEY', 'customer_emails_with_names.csv')
                  
                      print(f"Reading s3://{bucket}/{key}")
                      obj = s3.get_object(Bucket=bucket, Key=key)
                      body = obj['Body'].read().decode('utf-8', errors='replace')
                      reader = csv.DictReader(io.StringIO(body))
                  
                      destinations = []
                      count_total = 0
                      for row in reader:
                          email = (row.get('Email') or row.get('email') or '').strip()
                          if not email:
                              continue
                          # Enforce gmail-only safety
                          if not email.lower().endswith('@gmail.com'):
                              print(f"Skipping non-gmail: {email}")
                              continue
                          name = row.get('Name') or row.get('name') or ''
                          destinations.append({
                              'Destination': {'ToAddresses': [email]},
                              'ReplacementTemplateData': json.dumps({'name': name})
                          })
                          count_total += 1
                  
                      print(f"Prepared {count_total} gmail destinations")
                  
                      results = []
                      for chunk in chunk_list(destinations, BATCH_SIZE):
                          try:
                              # Build request dynamically to avoid NoneType errors
                              kwargs = {
                                  "Source": SOURCE,
                                  "Template": TEMPLATE,
                                  "DefaultTemplateData": json.dumps({"name": "Customer"}),
                                  "Destinations": chunk
                              }
                              config_set = os.getenv("CONFIG_SET", "").strip()
                              if config_set:
                                  kwargs["ConfigurationSetName"] = config_set
                  
                              resp = ses.send_bulk_templated_email(**kwargs)
                              print("Sent chunk response:", resp)
                              results.append(resp)
                          except ClientError as e:
                              print("SES send error:", e.response)
                              results.append({'Error': str(e)})
                  
                      return {
                          'statusCode': 200,
                          'body': json.dumps({
                              'sent_chunks': len(results),
                              'total_destinations': count_total
                          })
                      }
                  • Save the function.
                  • Set Timeout to e.g. 2–5 minutes depending on list size (Configuration → General configuration).

                  Step 8 — Test end-to-end

                  1. Ensure SOURCE_EMAIL/domain is verified and production access is requested/approved (or verify each recipient if still in sandbox). AWS Documentation
                  2. Upload gmail_customers.csv (filtered) to the S3 bucket (the S3 event will trigger Lambda).
                  3. Check Lambda → Monitor → View logs in CloudWatch for success/failure.
                  4. Check recipients’ Gmail inboxes (and spam folder) for the dynamic email.
                  5. Check SNS topic subscriptions for bounce/complaint messages; process them (delete/bounce marking) in your DB.

                  Benefits of This Architecture

                  Building an Automated Customer Email Campaign System using AWS SES and Serverless Architecture offers several powerful benefits for scalability, cost efficiency, and automation. Here’s why this approach stands out:


                  1. Fully Serverless and Scalable

                  This architecture is built entirely on serverless AWS components — SES, SNS, Lambda, and S3.
                  That means you don’t need to manage or scale servers manually. AWS automatically handles infrastructure scaling based on the number of emails sent or events processed, allowing you to focus purely on your campaign logic and customer experience.


                  2. Cost-Effective Pay-as-You-Go Model

                  With AWS’s pay-per-use pricing, you only pay for what you use — emails sent via SES, events handled by SNS, and compute time consumed by Lambda.
                  There’s no need to maintain dedicated servers or email systems, which makes this architecture ideal for startups and small businesses with limited budgets.


                  3. Automated Event Handling and Feedback

                  By integrating AWS SNS, you can automatically capture important feedback from SES such as bounces, complaints, and delivery reports.
                  This helps maintain a clean and healthy mailing list, ensures better email deliverability, and protects your sender reputation — all without manual monitoring.


                  4. Dynamic and Personalized Campaigns

                  Using SES Email Templates, emails can be customized dynamically with placeholders like {{name}}.
                  This allows you to send personalized promotions or notifications at scale, improving user engagement and click-through rates without manually tailoring each message.


                  5. Centralized Data Storage and Analytics

                  All email-related logs and events are stored in Amazon S3, providing a secure, durable, and centralized storage location.
                  You can later analyze this data with tools like AWS Athena, QuickSight, or Lambda scripts to gain insights into campaign performance and customer behavior.


                  6. Easy Integration and Automation

                  The Lambda function seamlessly connects S3, SES, and SNS — automating the entire process from reading the email list to sending emails and processing responses.
                  Once set up, uploading a new CSV file or triggering a workflow automatically starts the entire campaign without human intervention.


                  7. High Reliability and Security

                  All components are managed by AWS with built-in high availability, security, and compliance.
                  You can further secure your setup by enabling encryption (SSE), IAM-based access control, and DKIM/SPF for email authenticity, ensuring compliance with email best practices and data protection standards.

                  Conclusion

                  By building Automated Customer Email Campaigns with AWS SES and Serverless Architecture, we’ve created a fully automated, cost-efficient, and scalable email system — without managing a single server.

                  This solution uses AWS SES for reliable and scalable email delivery, Lambda for automation, S3 for data storage, and SNS for real-time feedback. Together, these services enable businesses to automatically send personalized emails to customers based on actions or data — whether it’s a new signup, a purchase, or a promotional campaign.

                  This architecture ensures:

                  • Zero manual effort once configured — everything runs automatically.
                  • Personalized customer engagement through dynamic templates.
                  • Real-time monitoring of email delivery, bounces, and complaints.
                  • Pay-as-you-go savings, ideal for both startups and enterprises.

                  This serverless design empowers businesses to focus more on marketing strategy and customer experience, while AWS takes care of scalability, reliability, and automation in the background.