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 recipientsmerge_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:
- Go to SES Console → Configuration sets → Create set
- Add Event Destinations (SNS topic for webhooks)
- 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.