Skip to content

Amazon SES Backend

The SES backend (SESBackend) sends emails via AWS Simple Email Service using boto3 (sync) and aioboto3 (async).

Installation

pip install amsdal_mail[ses]

Configuration

Environment Variables

Variable Default Description
AWS_ACCESS_KEY_ID AWS access key (required)
AWS_SECRET_ACCESS_KEY AWS secret key (required)
AWS_REGION or AWS_DEFAULT_REGION us-east-1 AWS region
AWS_SES_CONFIGURATION_SET SES Configuration Set name (for tracking)

Constructor Parameters

from amsdal_mail import get_connection

conn = get_connection(
    'ses',
    region_name='eu-west-1',
    aws_access_key_id='AKIA...',
    aws_secret_access_key='...',
    configuration_set='my-tracking-set',
)

Basic Usage

from amsdal_mail import send_mail

status = send_mail(
    subject='Order Confirmation',
    message='Your order #1234 has been confirmed.',
    from_email='orders@example.com',
    recipient_list=['customer@example.com'],
)

print(status.message_id)   # SES MessageId, e.g. '0100018f...'
print(status.is_success)   # True

Tags and Metadata

SES supports message tags for filtering and analytics.

Tags

Tags are sent as SES Tags with value='true':

from amsdal_mail import send_mail

status = send_mail(
    subject='Welcome',
    message='...',
    from_email='noreply@example.com',
    recipient_list=['user@example.com'],
    tags=['onboarding', 'welcome-series'],
)

Metadata

Metadata is converted to SES Tags with a metadata: prefix:

status = send_mail(
    subject='Invoice',
    message='...',
    from_email='billing@example.com',
    recipient_list=['customer@example.com'],
    metadata={'order_id': '1234', 'campaign': 'q1-billing'},
)

This produces SES tags: metadata:order_id=1234, metadata:campaign=q1-billing.

Warning

SES supports up to 50 tags total per message. Tags and metadata share this limit.

Template-Based Emails

SES supports server-side templates. Create the template in AWS SES Console first, then reference it by ID:

from amsdal_mail import EmailMessage, get_connection

msg = EmailMessage(
    subject='Welcome, !',
    template_id='welcome-template',
    from_email='noreply@example.com',
    to=['alice@example.com', 'bob@example.com'],
    merge_global_data={
        'company': 'ACME Corp',
        'year': '2026',
    },
    merge_data={
        'alice@example.com': {'name': 'Alice', 'plan': 'Pro'},
        'bob@example.com': {'name': 'Bob', 'plan': 'Free'},
    },
)

conn = get_connection('ses')
status = conn.send_messages([msg])
  • merge_global_data — template variables applied to all recipients
  • merge_data — per-recipient overrides, keyed by email address
  • Recipient-specific data takes precedence over global data

Note

When using template_id, the body field can be empty — SES renders the body from the template.

Async

from amsdal_mail import asend_mail

status = await asend_mail(
    subject='Hello',
    message='Sent asynchronously via SES.',
    from_email='noreply@example.com',
    recipient_list=['user@example.com'],
)

Or with EmailMessage:

from amsdal_mail import EmailMessage, get_connection

conn = get_connection('ses')
status = await conn.asend_messages([msg])

The async version uses aioboto3 to create SES sessions and clients.

Configuration Set

A Configuration Set is required for open/click tracking and event notifications. Create one in the AWS SES Console:

  1. Go to SES Console → Configuration sets → Create set
  2. Add Event Destinations (SNS topic for webhooks)
  3. Enable event types: Send, Delivery, Bounce, Complaint, Open, Click

Then set the name:

export AWS_SES_CONFIGURATION_SET=my-tracking-set

Or pass it to the constructor:

conn = get_connection('ses', configuration_set='my-tracking-set')

Note

The track_opens and track_clicks fields on EmailMessage are not used by SES — tracking is controlled entirely by the Configuration Set. These fields are included for compatibility with other backends.

SES Message ID

SES returns a MessageId for every successfully sent message. This is stored in SendStatus.message_id and in each RecipientStatus.message_id. All recipients of the same message share the same MessageId.