Receive application mail with Amazon SES receipt rules
Route a dedicated receiving subdomain into SES, store raw email in S3, and build a recoverable processing path without replacing employee mail.
Reviewed
SES receiving applies ordered receipt-rule actions to messages delivered for your verified domain. A practical application path stores raw mail in S3, then triggers processing. SES does not provide an IMAP inbox, and a Lambda receipt event is not necessarily a copy of the full message body.
Isolate receiving from existing mailboxes
Choose a receiving-supported Region and a dedicated application subdomain, such as inbound.example.com. Verify that domain and publish the exact regional inbound MX target from AWS’s receiving setup.
Do not point an employee-mail domain at SES to test one address. Sending identity DKIM records, optional custom MAIL FROM records, and receiving MX records perform different jobs. Keep the original Exchange/Gmail domain routing unchanged unless an administrator has approved an actual migration.
Design the rule and storage first
Provision a private S3 bucket and grant SES only the required write permission for this receipt path. Configure compatible Region/resource permissions using AWS’s receiving-permissions guide. Do not make the bucket public as a permissions workaround.
Create or update an approved rule set. Only one receipt-rule set is active in an account/Region, so inspect the current one before activating a replacement:
aws ses describe-active-receipt-rule-set --region "$AWS_REGION"
This command uses the classic ses API namespace even if sending identity setup used sesv2. Review existing recipient conditions, rule order, and stop actions with the account owner.
Add an exact-recipient S3 rule
The following is an illustrative receipt-rule object for an administrator to adapt, not a complete provisioning script. Replace the address and bucket with your verified receiving identity and private bucket. Create the bucket policy before enabling the rule.
{
"Name": "application-inbox",
"Enabled": true,
"Recipients": ["app@inbound.example.com"],
"TlsPolicy": "Require",
"ScanEnabled": true,
"Actions": [
{
"S3Action": {
"BucketName": "your-private-inbound-bucket",
"ObjectKeyPrefix": "application-mail/"
}
}
]
}
Require rejects delivery when the sender cannot establish TLS; choose that policy deliberately. Scanning exposes verdicts to downstream processing—it does not automatically implement your application’s accept/quarantine policy. Recipient conditions match SMTP envelope recipients, not simply the message’s To header.
Add a processing trigger only after durable storage works. For a receipt-rule Lambda action, put S3 storage before Lambda if the worker needs to retrieve the full raw MIME message. Alternatively, use an appropriate S3 event path. Do not assume every SNS notification contains the body: SES’s SNS action and S3 delivery notifications have different payload contracts.
Verify recovery and data handling
Send one controlled message to the exact recipient on the receiving subdomain. Confirm an S3 object, its usable MIME content, and the expected processing event. Parse MIME with a maintained library; do not treat raw email as JSON.
Use a durable processing record and deduplicate by a stable stored-object/message identity. Test duplicate notifications and a failed worker. Restrict attachment handling and retention. If enabling the receipt action’s KMS encryption option, follow AWS’s client-side decryption requirements; do not assume it is identical to ordinary S3 server-side encryption.
Choose an HTTP-native alternative deliberately
For a managed signed-JSON receiver instead of owning S3/MIME/worker plumbing, use MailKite’s receiving quickstart on a verified application domain. SES notifications cannot be posted to a MailKite webhook receiver as though they carried MailKite signatures; use the correct provider verification or a separately designed adapter.
References