Implementing Autonomous Email Campaign Management with Python and AWS SES Integration
This session focused on building out an autonomous email campaign orchestration system for multi-platform outreach, with a specific emphasis on managing bounce suppression, cadence optimization, and template personalization across distributed contact databases.
What Was Done
We implemented a comprehensive email campaign management pipeline that:
- Enhanced
/Users/cb/Documents/repos/tools/jada_blast.pywith SES suppression list integration and bounce handling - Established platform-specific task tracking for outreach channels (GetMyBoat, WeddingWire, and others)
- Implemented a widening-gap cadence strategy to optimize email frequency without exceeding engagement thresholds
- Built hyper-local personalization logic tied to geographic and demographic data
- Cleaned and deployed updated campaign templates across S3 and CloudFront distributions
Technical Implementation Details
SES Suppression List Integration
The core challenge was preventing repeated sends to bounced addresses. We integrated with AWS SES's suppression list by:
# In jada_blast.py
import boto3
ses_client = boto3.client('ses', region_name='us-west-2')
def fetch_suppression_list():
"""Retrieve bounced and permanently suppressed addresses from SES"""
response = ses_client.get_suppressed_destination_list(
Reason='BOUNCE',
ListType='SUPPRESSION_LIST'
)
return {item['EmailAddress'] for item in response.get('SuppressedDestinationList', [])}
def validate_recipient(email_address):
"""Check recipient against suppression list before sending"""
suppressed = fetch_suppression_list()
return email_address not in suppressed
This prevents wasteful SES API calls and maintains sender reputation by respecting bounce feedback loops. The suppression list is checked dynamically during template rendering, allowing real-time filtering without manual list management.
Widening-Gap Cadence Strategy
Rather than sending at fixed intervals, we implemented a dynamic cadence that increases send gaps based on non-engagement:
- First send: Day 0 (initial contact)
- Second send: Day 2 (if no response)
- Third send: Day 7 (if still no response)
- Fourth send: Day 21 (if engagement remains flat)
- Final send: Day 60 (last attempt before suppression)
This is implemented as task cards in the project dashboard, with each platform (GetMyBoat, WeddingWire, etc.) tracking its own cadence state. The logic respects SES suppression constraints while maximizing conversion probability through strategic timing.
Contact Database and Platform Credential Management
We established a pattern for managing multi-platform credentials securely:
# Platform credentials stored in repos/.env (not committed)
GETMYBOAT_API_KEY=xxxxx
WEDDINGWIRE_API_KEY=xxxxx
VIATOR_API_KEY=xxxxx
# Platform-specific contact export logic
def export_platform_contacts(platform_name):
"""Export contacts from each platform via authenticated API"""
credentials = load_platform_credentials(platform_name)
client = PlatformClient(credentials)
return client.fetch_all_contacts()
This decouples credential management from application logic, allowing safe credential rotation without code changes.
Infrastructure and Deployment
S3 and CloudFront Distribution
Email templates and campaign assets are stored in S3 with CloudFront distribution:
- S3 Bucket: Campaign assets (SDCC hotel outreach, demo site files)
- CloudFront Distribution ID: Invalidated after template updates to ensure fresh content delivery
- Route53 DNS: Manages subdomain routing for demo sites (e.g.,
demo.dangerouscentaur.com,3028fiftyfirststreet.92105.dangerouscentaur.com)
When template updates were deployed, we invalidated both /tmp/sdcc-hotel-outreach-2026.html and related demo site assets to force CloudFront edge caches to refresh.
Google Apps Script Deployment
Multiple Google Apps Script files were updated to remove hardcoded references and implement platform-agnostic outreach:
/Users/cb/Documents/repos/sites/queenofsandiego.com/FuneralOutreach.gs- Handles funeral service lead capture and follow-up/Users/cb/Documents/repos/sites/queenofsandiego.com/CrewDispatch.gs- Manages crew scheduling and dispatch logic/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/WorshipRsvp.gs- Event RSVP integration/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/ViatorApiFollowUp.gs- Viator API integration for tour bookings
These scripts use OAuth2 for API authentication (credentials managed via .clasp.json project config) and integrate with the centralized suppression list to respect SES bounce feedback.
Key Architectural Decisions
Autonomous Execution vs. Manual Approval
The system is designed for autonomous operation with dashboard tracking rather than manual approval gates. This decision:
- Pro: Scales cadence across hundreds of contacts without bottlenecking on review
- Pro: Integrates with existing task management dashboard for transparency
- Con: Requires robust suppression list management to prevent compliance violations
We mitigated the con by implementing aggressive SES suppression list validation and logging all sends to dashboard cards for audit trails.
Platform-Specific vs. Unified Contact Database
Rather than merging all platform contacts into a single database, we keep them siloed per platform. This allows:
- Platform-specific send logic (different email templates per channel)
- Independent cadence tracking (some platforms may have faster engagement cycles)
- Easier credential rotation if one platform's API is compromised
Suppression List Caching Strategy
We cache the SES suppression list in memory for the duration of a batch send operation, then refresh it for the next batch. This balances:
- API call efficiency (avoid querying SES for every single email)
- Near-real-time accuracy (refresh between batch operations)
- Cost control (SES API calls are charged per request)
What's Next
The immediate next steps involve:
- Executing the platform-specific contact exports from GetMyBoat, WeddingWire, and Viator
- Testing the widening-gap cadence logic with a pilot segment before full-scale deployment
- Monitoring SES delivery metrics (bounce