AWS Contact Center
Automating Pinpoint email template migration to Amazon SES v2
If you manage email templates in Amazon Pinpoint and need to migrate them to Amazon Simple Email Service (Amazon SES) v2 before the October 30, 2026 end of support deadline, you know the challenge: renaming fields, converting placeholder syntax, handling click-tracking attributes, and deploying at scale, all tedious and error-prone when done manually across dozens or hundreds of templates.
In this post, you learn how to use migrate_pinpoint_to_sesv2.py, an open-source Python script (available on GitHub), to automate the end-to-end conversion. The script converts Pinpoint templates to Amazon SES v2 format, validates them against service limits, and deploys them directly. In our testing, the script migrated 100 sample templates in under 5 minutes, with no manual field renaming required for standard templates.
Whether you have one template or hundreds, the script provides a repeatable, auditable migration path with dry-run previews and JSON reporting.
Background knowledge
You should be familiar with the following:
- Managing an AWS account
- Amazon Simple Email Service (Amazon SES)
- Amazon Pinpoint email templates
- AWS CLI v2
- Basic Python usage (running scripts from the command line)
Download the script before starting the walkthroughs:
git clone https://github.com/arabam-git/amazon-pinpoint-to-ses-email-template-migrator.git
cd amazon-pinpoint-to-ses-email-template-migrator
What changes between Pinpoint and Amazon SES templates
Amazon Pinpoint and Amazon SES v2 both use HTML email templates with placeholder variables, but there are differences in structure and syntax that you must address during migration, as shown in the following table.
| Aspect | Amazon Pinpoint | Amazon SES v2 |
|---|---|---|
| Template fields | HtmlPart, TextPart |
Html, Text |
| Placeholder syntax | {{User.UserAttributes.FirstName}} |
{{firstName}} (simple Handlebars) |
| Simple placeholders | {{FirstName}} |
{{FirstName}} (no change needed) |
| Link click tracking | ses:tags attributes may appear on <a> tags (passed through to SES) |
Click tracking enabled per configuration set; ses:tags add custom tags to click events |
| Template size limit | N/A | 500 KB per template |
| Template storage | Pinpoint project | Amazon SES account (up to 20,000 per Region) |
Solution overview
The migration script automates the following workflow:
- Load templates from local JSON files, local HTML files, or directly from the Pinpoint API.
- Convert to Amazon SES v2 format: rename
HtmlParttoHtml,TextParttoText, and convert nested placeholders to simple Handlebars syntax. - Validate: check the 500 KB size limit and flag issues.
- Output: save converted JSON to disk, generate a dry-run report, optionally strip
ses:tagsattributes, and optionally inject an unsubscribe link. - Deploy to Amazon SES v2: create or update templates through the API.
- Generate a JSON audit report with per-template details for review.
The script supports three input sources:
- Local JSON files: Pinpoint template exports containing
TemplateNameandTemplateContent. - Local HTML files: raw .html/.htm files; the script extracts the
<title>tag as the Subject and generates a plain text version from the HTML. - Pinpoint API: fetches templates directly from the Pinpoint service using
--from-pinpoint.
Prerequisites
Before you begin, complete the following prerequisites:
- Python 3.8–3.12 (tested on 3.9, 3.11, and 3.12; standard library only for dry-run and local conversion)
- boto3 (latest version recommended; required only for
--deployand--from-pinpoint):pip3 install boto3 - AWS CLI v2 installed and configured with credentials
- A verified sending domain or email address in Amazon SES
- The migration script (see Download the script under Background knowledge)
- AWS Identity and Access Management (IAM) permissions:
- For deployment:
sesv2:CreateEmailTemplate,sesv2:UpdateEmailTemplate - For Pinpoint API fetch:
pinpoint:ListTemplates,pinpoint:GetEmailTemplate - For sending test emails:
sesv2:SendEmail
- For deployment:
Walkthrough: Convert and deploy a single template
This section uses a single HTML email template to demonstrate the end-to-end migration process.
Step 1: Prepare your template directory
Create a working directory and place your Pinpoint template in it:
mkdir ses-migration
cp WelcomeEmailTemplate.html ses-migration/
Step 2: Preview the conversion with dry-run
Run the script in dry-run mode to see what changes without making any modifications:
python3 migrate_pinpoint_to_sesv2.py --local-dir ses-migration --dry-run
The dry-run report shows the following:
- Placeholder mappings (Pinpoint syntax to Amazon SES Handlebars)
ses:tagsattributes found and whether they will be stripped or kept- Template size relative to the 500 KB Amazon SES limit
- Structure changes (
HtmlParttoHtml,TextParttoText) - An HTML preview of the converted template
Review the report to confirm the conversion is correct before deploying.
Step 3: Deploy to Amazon SES v2
python3 migrate_pinpoint_to_sesv2.py --local-dir ses-migration --region us-east-1 --deploy
The script creates the template in Amazon SES. If a template with the same name already exists, it updates the template automatically. The deployment summary shows how many templates were created, updated, skipped, or failed.
Step 4: Verify the template
aws sesv2 get-email-template --template-name WelcomeEmailTemplate --region us-east-1
Step 5: Send a test email
aws sesv2 send-email \
--from-email-address sender@yourdomain.com \
--destination '{"ToAddresses":["test@yourdomain.com"]}' \
--content '{"Template":{"TemplateName":"WelcomeEmailTemplate","TemplateData":"{\"FirstName\":\"Jane\"}"}}' \
--region us-east-1
Every placeholder in the template must have a matching key in TemplateData. If the template contains {{FirstName}} and you do not pass FirstName, Amazon SES returns a Rendering Failure.
Monitoring template rendering failures with a configuration set
After you migrate templates, the most common issue is a Rendering Failure: Amazon SES cannot render the template because a placeholder is missing from TemplateData. Amazon SES does not send the email, and the SendEmail API does not return an error. The only way to catch these failures is by setting up event notifications on a configuration set.
This is especially important after migration because the script converts placeholder names (for example, User.UserAttributes.FirstName becomes firstName). If your sending code still passes the old key names, Amazon SES fails to render the template and does not deliver the email.
Step 1: Create a configuration set
aws sesv2 create-configuration-set --configuration-set-name MigrationMonitoring --region us-east-1
Step 2: Create an Amazon Simple Notification Service (Amazon SNS) topic for notifications
aws sns create-topic --name ses-rendering-failures --region us-east-1
Subscribe your email to the topic so you receive alerts:
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:YOUR_ACCOUNT_ID:ses-rendering-failures \
--protocol email \
--notification-endpoint your-email@yourdomain.com \
--region us-east-1
Step 3: Add a Rendering Failure event destination
aws sesv2 create-configuration-set-event-destination \
--configuration-set-name MigrationMonitoring \
--event-destination-name RenderingFailures \
--event-destination '{
"Enabled": true,
"MatchingEventTypes": ["RENDERING_FAILURE"],
"SnsDestination": {"TopicArn": "arn:aws:sns:us-east-1:YOUR_ACCOUNT_ID:ses-rendering-failures"}
}' \
--region us-east-1
Step 4: Send emails using the configuration set
Add --configuration-set-name to your send calls:
aws sesv2 send-email \
--from-email-address sender@yourdomain.com \
--destination '{"ToAddresses":["test@yourdomain.com"]}' \
--content '{"Template":{"TemplateName":"WelcomeEmailTemplate","TemplateData":"{\"FirstName\":\"Jane\"}"}}' \
--configuration-set-name MigrationMonitoring \
--region us-east-1
If a placeholder is missing from TemplateData, Amazon SES publishes a RENDERING_FAILURE event to your Amazon SNS topic. The event payload includes the template name and the specific error, letting you catch mismatches between your migrated templates and your sending code before they affect production email delivery.
Walkthrough: Migrate multiple templates
When migrating more than one template, use the --report flag to generate a JSON audit file for review before deploying.
Step 1: Dry-run with a JSON report
python3 migrate_pinpoint_to_sesv2.py --local-dir all-templates/ --dry-run --report migration-report.json
The JSON report includes per-template details: placeholder mappings, ses:tags removed, template size, and whether the unsubscribe link was injected. Review this report to catch any issues before deploying.
Step 2: Deploy all templates
python3 migrate_pinpoint_to_sesv2.py --local-dir all-templates/ --region us-east-1 --deploy
The deployment summary shows counts for created, updated, skipped, and failed templates. If any templates fail, the script exits with code 1 and lists the failures.
Walkthrough: Fetch templates directly from Pinpoint API
If your templates have not been exported to local files, the --from-pinpoint flag tells the script to pull them directly from the Pinpoint API. The script calls pinpoint:ListTemplates to discover all email templates in the account. It then calls pinpoint:GetEmailTemplate for each one to retrieve the full content (Subject, HtmlPart, TextPart). No manual export step is needed.
Preview the templates:
python3 migrate_pinpoint_to_sesv2.py --from-pinpoint --region us-east-1 --dry-run
Deploy the templates:
python3 migrate_pinpoint_to_sesv2.py --from-pinpoint --region us-east-1 --deploy
This requires pinpoint:ListTemplates and pinpoint:GetEmailTemplate IAM permissions in addition to the Amazon SES permissions listed in Prerequisites.
Handling ses:tags and unsubscribe links
The script provides two optional flags that control how templates are modified during conversion.
Keeping ses:tags for click tracking
Amazon SES supports the ses:tags attribute on HTML <a> tags for link click tracking; because Amazon Pinpoint email sends through Amazon SES, your Pinpoint templates may already contain them. By default, the script strips these attributes because they have no effect without click tracking enabled on your Amazon SES configuration set. If you use Amazon SES click tracking, preserve them:
python3 migrate_pinpoint_to_sesv2.py --local-dir ./templates --keep-ses-tags --deploy
Injecting an Amazon SES-managed unsubscribe link
The --add-unsubscribe-link flag injects an {{amazonSESUnsubscribeUrl}} placeholder before </body> in templates that do not already have one. Amazon SES replaces this placeholder at send time with a link to the Amazon SES subscription management page.
python3 migrate_pinpoint_to_sesv2.py --local-dir ./templates --add-unsubscribe-link --deploy
Use this flag only if you send with ListManagementOptions in the SendEmail request. Sending without ListManagementOptions when the template contains {{amazonSESUnsubscribeUrl}} causes a Rendering Failure.
For a deep dive on unsubscribe compliance, including how to keep your existing unsubscribe page in sync with Amazon SES subscription management, see the Amazon SES subscription management documentation.
Command-line reference
The script accepts the following flags:
| Flag | Description | Default |
|---|---|---|
--region |
AWS Region | us-east-1 |
--local-dir |
Directory containing template files (JSON, HTML, HTM) | ./sample-templates |
--from-pinpoint |
Fetch templates from Pinpoint API instead of local files | Off |
--output-dir |
Directory to save converted Amazon SES JSON files | ./output-ses-templates |
--dry-run |
Preview conversions (no files written, no API calls) | Off |
--deploy |
Deploy converted templates to Amazon SES v2 | Off |
--keep-ses-tags |
Keep ses:tags attributes in HTML for click tracking |
Off (strip) |
--add-unsubscribe-link |
Inject {{amazonSESUnsubscribeUrl}} link |
Off |
--skip-existing |
Skip templates that already exist in Amazon SES instead of updating | Off (update) |
--report FILE |
Save conversion report as JSON | Off |
Exit codes
The script returns these exit codes:
| Code | Meaning |
|---|---|
| 0 | All operations completed successfully |
| 1 | Partial failure (some templates failed to deploy) |
| 2 | Fatal error (missing directory, missing boto3, and so on) |
Supported input formats
The script supports these input formats:
| Format | Extension | Processing |
|---|---|---|
| Pinpoint JSON | .json | Must contain TemplateName and TemplateContent with Subject, HtmlPart, TextPart |
| HTML email | .html, .htm | Subject extracted from <title> tag, plain text generated from HTML |
Cleanup
If you deployed test templates and want to remove them, run the following command:
aws sesv2 delete-email-template --template-name WelcomeEmailTemplate --region us-east-1
To remove the monitoring configuration set and Amazon SNS topic, run the following commands:
aws sesv2 delete-configuration-set --configuration-set-name MigrationMonitoring --region us-east-1
aws sns delete-topic --topic-arn arn:aws:sns:us-east-1:YOUR_ACCOUNT_ID:ses-rendering-failures --region us-east-1
To list all templates in your account, run the following command:
aws sesv2 list-email-templates --region us-east-1
Limitations
Be aware of the following limitations:
- The script migrates email templates only, not segments, campaigns, or journeys. For engagement feature migration, see Migrating from Amazon Pinpoint.
- Templates exceeding 500 KB are flagged and skipped. Optimize them (minify HTML, remove redundant CSS, compress image references) before re-running.
- Placeholder conversion handles Pinpoint’s standard nested paths (
User.UserAttributes.*,Attributes.*,User.*). Custom helper functions or conditional logic in templates are not converted. - The
--add-unsubscribe-linkflag injects a generic unsubscribe block. Customize the styling in the script source if needed.
Conclusion
Migrating email templates from Amazon Pinpoint to Amazon SES v2 involves renaming fields, converting placeholder syntax, handling click-tracking attributes, and validating against service limits. The migrate_pinpoint_to_sesv2.py script automates this process, providing dry-run previews, JSON audit reports, and direct deployment to Amazon SES v2. By using the script’s dry-run and reporting capabilities, you can review every conversion before making changes to your AWS account, reducing the risk of errors during migration.
Next steps
To get started, take the following steps:
- Download the script and run a dry-run against your Pinpoint templates to assess the migration scope.
- Review the dry-run report for templates with nested placeholders,
ses:tagsattributes, or size issues. - Deploy a pilot batch of templates to Amazon SES and send test emails to verify rendering.
- Set up Amazon SES subscription management if you need one-click unsubscribe compliance with Gmail and Yahoo.
- Monitor your Amazon SES sending with Virtual Deliverability Manager after migration.
- Complete your migration before the October 30, 2026 Pinpoint end of support date.
Related resources
For more information, see the following resources:
- Migration script on GitHub — download
migrate_pinpoint_to_sesv2.pyand sample templates - Migrating from Amazon Pinpoint
- Amazon SES v2 email templates
- CreateEmailTemplate API
- Amazon SES subscription management
- Amazon SES service quotas
- Amazon SES event publishing
- Amazon SES configuration sets
- Amazon SES open and click tracking
- Using one-click unsubscribe with Amazon SES
About the author
Michael Araba is a Sr. Technical Account Manager at Amazon Web Services and a member of the Applied AI Communications Technical Field Community (TFC). He works with enterprise customers to remove migration blockers as they transition from Amazon Pinpoint to Amazon SES, AWS End User Messaging, and Amazon Connect. He developed this migration script to help customers automate email template conversion and deployment at scale.