Sarath walks you through
storing SNS notifications for SES
in DynamoDB

I use Amazon Simple Notification Service (Amazon SNS) to receive notifications about bounces, complaints, and deliveries for email sent through the Amazon Simple Email Service (Amazon SES). I want to store these notifications in an Amazon DynamoDB database using AWS Lambda. How can I accomplish this?

After you configure Amazon SES Notifications Through Amazon SNS, you can use an AWS Lambda function to write these notifications to a DynamoDB database. Then, you can download these notifications from the DynamoDB database to a CSV file.

Note: The Lambda function created in this sample can be used as a template for writing data to a CRM or other destinations based on where you host your mailing list.

Complete each set of steps below to use a Lambda function to store SNS notification contents for SES to a DynamoDB database:

  1. Create a DynamoDB table.
  2. Add permissions for the Lambda IAM role to write to the DynamoDB table.
  3. Create a Lambda function to processes SES bounce notifications.
  4. Subscribe the Lambda function to one or more topics.
  5. Update function permissions to allow SNS to invoke the function.
  6. Test the set-up by sending an SES message to invoke the Lambda function.
  7. Download a report from DynamoDB to view SES notifications.


  • Allow read and write access to a DynamoDB database.
  • Set up an SES account (email or domain) that is verified and has an SNS topic configured to receive SES notifications. For more information about using SNS to receive SES notifications, see Amazon SES Notifications Through Amazon SNS.

1. Create a DynamoDB table

Create a table in DynamoDB named SESNotifications with a primary partition key named SESMessageId and a primary sort key named SnsPublishTime.

To query the table and generate an SES report, set up the notification type and complaint type secondary indexes as described in the table below. Additional indexes can be added as necessary.

Index Name Partition Key Sort Key
SESMessageType-Index SESMessageType (String) SnsPublishTime (String)
SESMessageComplaintType-Index SESComplaintFeedbackType (String) SnsPublishTime (String)

For information about creating a table in DynamoDB, see Getting Started with Amazon DynamoDB.

2. Add permissions for the Lambda IAM role to write to the DynamoDB table

Create a new AWS Identity and Access Management (IAM) role for use by the Lambda function. The role must allow the Lambda function to call the DynamoDB:PutItem API.

Note: It's a best practice to create and use a new IAM role for different Lambda functions. Avoid reusing roles across multiple functions.

  1. In the IAM console, in the navigation pane choose Roles, and then choose Create Role.
  2. Choose the AWS service trusted entity type, choose Lambda, and then choose Next: Permissions.
  3. Choose the AWSLambdaBasicExecutionRole managed policy, and then choose Next.
  4. Provide a name for the role of lambda_ses_execution, and then complete the creation of the role.
  5. Return to the IAM Roles view, and then choose the role you created.
  6. Choose Add an Inline Policy.
  7. In the Visual editor, choose the Service DynamoDB and the action PutItem under the Write category. Under resources, choose Add ARN and then provide the DynamoDB table ARN created earlier.
  8. Choose Review, provide a policy name, and then choose Create policy.

The following example shows the inline policy statement added to the lambda_ses_execution role to grant access to the DynamoDB database us-east-1:12345678912:table/SESNotifications created in step 1:

------------------------IAM Policy Begins---------------------------
    "Version": "2012-10-17",
    "Statement": [
            "Sid": "Stmt1428510662000",
            "Effect": "Allow",
            "Action": [
            "Resource": [
------------------------IAM Policy Ends-----------------------------

3. Create a Lambda function to processes SES bounce notifications

Use the Lambda console to create a Lambda function named sesnotificationscode.

When creating the function, choose the role lambda_ses_execution created earlier.

Set up a new function using the following sample code. This sample code checks for the three types of SNS notifications as described at Amazon SNS Notification Examples for Amazon SES, and puts the SES notification into an entry in a DynamoDB table.

Note: Replace the value of the TableName parameter SESNotifications in the following sample code with the name of your table.

--------------------------Lambda Code Begins------------------------
console.log('Loading event');
var aws = require('aws-sdk');
var ddb = new aws.DynamoDB({params: {TableName: 'SESNotifications'}});
exports.handler = function(event, context)
  console.log('Received event:', JSON.stringify(event, null, 2));
  var SnsPublishTime = event.Records[0].Sns.Timestamp
  var SnsTopicArn = event.Records[0].Sns.TopicArn;
  var SESMessage = event.Records[0].Sns.Message
  SESMessage = JSON.parse(SESMessage);
  var SESMessageType = SESMessage.notificationType;
  var SESMessageId = SESMessage.mail.messageId;
  var SESDestinationAddress = SESMessage.mail.destination.toString();
  var LambdaReceiveTime = new Date().toString();
  if (SESMessageType == 'Bounce')
  var SESreportingMTA = SESMessage.bounce.reportingMTA;
  var SESbounceSummary = JSON.stringify(SESMessage.bounce.bouncedRecipients);
  var itemParams = {Item: {SESMessageId: {S: SESMessageId}, SnsPublishTime: {S: SnsPublishTime},
  SESreportingMTA: {S: SESreportingMTA}, SESDestinationAddress: {S: SESDestinationAddress}, SESbounceSummary: {S: SESbounceSummary},
  SESMessageType: {S: SESMessageType}}};
ddb.putItem(itemParams, function(err, data)
  if(err) {}
  else {
else if (SESMessageType == 'Delivery')
  var SESsmtpResponse1 =;
  var SESreportingMTA1 =;
  var itemParamsdel = {Item: {SESMessageId: {S: SESMessageId}, SnsPublishTime: {S: SnsPublishTime}, SESsmtpResponse: {S: SESsmtpResponse1},
  SESreportingMTA: {S: SESreportingMTA1},
  SESDestinationAddress: {S: SESDestinationAddress }, SESMessageType: {S: SESMessageType}}};
  ddb.putItem(itemParamsdel, function(err, data)
  if(err) {}
  else {
else if (SESMessageType == 'Complaint')
var SESComplaintFeedbackType = SESMessage.complaint.complaintFeedbackType;
var SESFeedbackId = SESMessage.complaint.feedbackId;
var itemParamscomp = {Item: {SESMessageId: {S: SESMessageId}, SnsPublishTime: {S: SnsPublishTime}, SESComplaintFeedbackType: {S: SESComplaintFeedbackType},
SESFeedbackId: {S: SESFeedbackId},
SESDestinationAddress: {S: SESDestinationAddress }, SESMessageType: {S: SESMessageType}}};
ddb.putItem(itemParamscomp, function(err, data)
  if(err) {}
  else {
>      }
------------------------Lambda Code Ends----------------------------

4. Subscribe the Lambda function to one or more topics

Assuming that you already created at least one SNS topic and configured an SES email domain to use that SNS topic for feedback notifications, follow these steps:

  1. In the SNS console navigation pane, choose Topics. Identify the SNS topic that is used in SES for bounces, such as an SNS topic named ses_notifications_repo.
  2. Choose the ARN of the SNS topic to open the Topic Details page.
  3. Choose Create Subscription.
  4. For Protocol, choose AWS Lambda.
  5. For Endpoint, type the ARN of the Lambda function, and then choose Create Subscription. This creates a subscription with Lambda as the protocol and the Lambda function ARN as the endpoint.

5. Update function permissions to allow SNS to invoke the function

If you didn't use the SNS console in the previous step to create the Lambda subscription for the topic, you can manually create the trigger using the Lambda console.

  1. In the Lambda console for the function, add the SNS trigger from the triggers list on the Designer. A configuration panel appears below the designer.
  2. Choose the topic from the drop-down list in the configuration panel.

Repeat this process to add the different notification topics you want to subscribe this Lambda function.

6. Test the set-up by sending an SES message to invoke the Lambda function

Now that the Lambda function is subscribed to an SNS topic, the next step is to publish an SES message and watch the invocation of the Lambda function in the Lambda console. You can use the SES console to send a test message.

To send a test message, you can use one of the available mailbox simulator addresses to avoid a negative impact on your SES deliverability metrics. For more information, see Testing Amazon SES Email Sending.

After the SES message is sent, SES publishes a notification to the SNS topic. SNS delivers this notification to Lambda as a JSON-escaped SES event notification object in the SNS event object.

To create sample events for local testing using the Lambda console, see Amazon SES Email Receiving Sample Event.

For SES bounce, complaint, and delivery notification examples, see Amazon SNS Notification Examples for Amazon SES.

7. Download a report from DynamoDB to view SES notifications

You can query, sort, and download the contents of the table as a CSV file by using the DynamoDB console.

  1. In the DynamoDB console, choose the SESNotifications table.
  2. Go to the Items tab.
  3. Create a Query or Scan search.

For more information, see What Is Amazon DynamoDB? You can also use AWS Data Pipeline to schedule a download of the file to an Amazon Simple Storage Service (Amazon S3) bucket at regular intervals for compliance and record keeping. For more information, see Export DynamoDB Table to S3 and Part Two: Export Data from DynamoDB

Did this page help you? Yes | No

Back to the AWS Support Knowledge Center

Need help? Visit the AWS Support Center

Published: 2018-06-13

Updated: 2018-07-09