```html

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.py with 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