Back to AWS Fullstack
AWSEvent Driven ArchitectureServerless

Trigger AWS Workflows from Incoming Emails Using Amazon SES

Trigger AWS Workflows from Incoming Emails Using Amazon SES Ever wanted to trigger a workflow based on an email sent to a specific recipient address? That’s exactly what we’re going to discuss …

April 9, 2026
5 min read
Trigger AWS Workflows from Incoming Emails Using Amazon SES

Ever wanted to trigger a workflow based on an email sent to a specific recipient address?

That’s exactly what we’re going to discuss today.

In this post, you’ll learn:

  • What MX records are
  • How to connect SES with your domain
  • How to set up an SES rule to trigger automation in AWS

Amazon SES is the Simple Email Service from AWS. It allows you to send emails (outbound) from your applications to your customers.

In addition to sending emails, you can also trigger actions based on emails you receive (inbound).

Let’s say you have a custom domain.

I have my domain mjfernando.com, which I purchased from Route 53. I’ve also created a hosted zone in Route 53 to manage my domain records.

To trigger actions for inbound emails, the first step is to create an MX record in the hosted zone.

Not sure what an MX record is? 🤔

An MX record is a type of DNS record, similar to:

  • A record
  • CNAME record
  • TXT record

DNS records define how requests to your domain are handled. For example:

  • A Record: Points to an IP address
    Example: “Go to this IP address to view the website”
  • MX Record: Points to mail servers
    Example: “Deliver all emails for this domain to SES servers”
  • TXT Record: Returns text
    Example: “Here is a verification code to prove domain ownership”

Now let’s say I want to store email content in an S3 bucket when I receive emails at hello@mjfernando.com.

Step 1: Verify Your Domain in SES

First, I need to prove to SES that I own the domain

Create an identity in SES (this can be a domain, subdomain, or email address).

In this case, I’ll use the domain.

Allow SES to publish DNS records to Route 53 for verification.

SES will automatically create CNAME records in my hosted zone.

After some time, my domain status will show as verified.

Step 2: Create an SES Receiving Rule

Next, create a receiving rule.

I have two options:

  • Standard receiving rules
  • SES Mail Manager (a premium “email gateway” service for advanced use cases with additional monthly cost i.e. $50/month)

I’ll use the standard receiving rule.

Go to Email Receiving under the SES configuration section and Create a rule set.

At this point, SES will remind us to:

  • Verify our domain
  • Create an MX record pointing to SES mail servers

Step 3: Create the MX Record

Even though the domain is verified, I still need to create the MX record.

For example, if I’m using the N. Virginia (us-east-1) region, I will use the email receiving endpoint in N.Virginia.

AWS publish these endpoints in their documentation.

I created a new MX DNS record with the value below.

10 inbound-smtp.us-east-1.amazonaws.com

Here we use 10 as the default priority to for the mail servers since we only have SES as our mail server. This points incoming emails to SES.

Step 4: Create the Rule

Next, I create a email receiving rule set. (e.g. my-rule-set)

Add a rule (e.g. my-rule)

Then I configure the rule with a recipient condition to trigger this SES rule only for the emails receiving to hello@mjfernando.com

Next, as the action, I select “Deliver to S3 bucket”

We’ll also need an S3 bucket and an IAM role with permissions for SES to write to S3.

After clicking Next, I go to the IAM role and attach S3 permissions (I used AmazonS3FullAccess for testing feel free to apply more fine-grained permissions). Otherwise, you won’t be able to create the rule.

Now we can create the rule, and it’s created successfully.

Afterward, I set the rule to Active to enable it.

Now I’ll send an email to hello@mjfernando.com from my Gmail account and check whether the email content appears in the S3 bucket.

I can see that the email has been received in the S3 bucket.

After downloading and opening the file in a text editor, I can see the message I sent from Gmail.

Now I can hook this S3 bucket to a Lambda function or an SNS topic using S3 events to process the file and start the workflow.

In the S3 bucket, go to Properties → Event notifications and create a notification to trigger a Lambda function when a new object is added.

In the Lambda function, I can write code to fetch the content of the file.

I can use an npm module like mailparser to parse the email.

import { simpleParser, Attachment } from "mailparser";export const handler = async (event: S3Event): Promise<void> => {  for (const record of event.Records) {    const bucket = record.s3.bucket.name;    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));    console.log(`Processing email from s3://${bucket}/${key}`);    // Fetch raw email from S3    const emailContent = await getEmailFromS3(bucket, key);    console.log(`Email fetched — ${emailContent.length} bytes`);    // 2. Parse MIME email    const parsed = await simpleParser(emailContent);    console.log(`Parsed — Subject: "${parsed.subject}", From: ${parsed.from?.text}, Attachments: ${parsed.attachments.length}`);    // 3. Extract Excel attachments    const excelAttachments = extractExcelAttachments(parsed.attachments);    console.log(`Excel attachments found: ${excelAttachments.length}`);  }}/** Fetch raw email bytes from S3 */async function getEmailFromS3(bucket: string, key: string): Promise<Buffer> {  const resp = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));  return Buffer.from(await resp.Body!.transformToByteArray());}/** Filter attachments */function extractExcelAttachments(attachments: Attachment[]): Attachment[] {  return attachments.filter((a) => {    const ct = (a.contentType ?? "").toLowerCase();    const fn = (a.filename ?? "").toLowerCase();    return ct.includes(EXCEL_MIME);  });}

Now that I can read the email content, I can execute the business logic that runs when an email is received.

Cheers!

A version of this article was first published on April 9, 2026 on Medium.

Related articles