Multi-Tenant Email Campaign Automation: Building JADA's Platform-Agnostic Blast System with Cadence Suppression
What Was Done
We implemented a sophisticated email campaign automation system for JADA's multi-platform outreach, focusing on three critical infrastructure improvements:
- Enhanced
jada_blast.pywith widening-gap cadence logic to prevent recipient fatigue across multiple contact lists - Integrated Amazon SES suppression list filtering into the blast pipeline to eliminate bounced addresses before sending
- Refactored marketing materials to remove personal attribution, ensuring team-based branding across all public-facing templates and demo sites
- Created platform-specific task tracking for non-live listing platforms (GetMyBoat, WeddingWire, etc.) with standardized credential management
Technical Architecture: The Widening-Gap Cadence Pattern
The core innovation here is the widening-gap cadence—a contact suppression pattern designed to prevent message fatigue while maintaining engagement velocity. Rather than blasting all recipients simultaneously, we space communications using progressive delays.
In /Users/cb/Documents/repos/tools/jada_blast.py, we implemented a tiered scheduling system:
# Cadence thresholds (in days between contact attempts)
cadence_tiers = {
'tier_1': 3, # High-intent contacts: 3-day gap
'tier_2': 7, # Medium-intent: 7-day gap
'tier_3': 14 # Soft-intent: 14-day gap
}
This approach segmented recipients based on historical engagement (open rates, click-through rates, and reply velocity). The system queries the SES suppression list before each send, filtering out bounce/complaint addresses in real-time rather than post-facto analysis.
SES Suppression List Integration
Previously, the blast tool relied on manual cleanup of bounced addresses. We automated this by:
- Exporting the SES Suppression List (Bounce + Complaint entries) via AWS CLI
- Cross-referencing contact CSVs against suppressed addresses
- Creating a deduplicated, cleaned contact manifest before template rendering
- Logging suppression metrics to CloudWatch for monitoring campaign hygiene
The contact cleaning process:
aws sesv2 get-suppressed-destination-attributes \
--email-address [contact-address] \
--region us-west-2
This is called iteratively within the Python script's contact validation phase. We cache the suppression list for 24 hours to avoid rate-limiting on repeated runs.
Marketing Material Refactoring: Team-Based Branding
A critical decision was made to remove all personal name attribution from public-facing materials. This affected:
- Email templates:
/tmp/sdcc-hotel-outreach-2026.html— replaced "C.B. Ladd, Owner of JADA" with "The JADA Team" - Google Apps Script files:
FuneralOutreach.gs,CrewDispatch.gs,CrewScheduler.gs,WorshipRsvp.gs,ViatorApiFollowUp.gs— removed personal attribution from email signatures and dispatch confirmations - Demo sites:
/Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.htmlanddemo.dangerouscentaur.com/index.html— rebranded to team-centric copy - Progress dashboard:
/Users/cb/Documents/repos/sites/progress.queenofsandiego.com/index.html— updated footer attribution
This was a deliberate architectural choice: personal attribution creates reputational risk in high-volume outreach, and team branding scales better across delegation and future expansion.
Infrastructure: S3, CloudFront, and Deployment Pipeline
All updated marketing materials were deployed to S3 with CloudFront invalidation:
# SDCC email preview deployment
aws s3 cp /tmp/sdcc-hotel-outreach-2026.html \
s3://queenofsandiego-marketing-assets/templates/sdcc-2026.html \
--cache-control "max-age=3600"
# CloudFront cache invalidation
aws cloudfront create-invalidation \
--distribution-id [DIST_ID_SDCC] \
--paths "/templates/sdcc-2026.html"
Similarly, dangerouscentaur demo sites were pushed to their respective S3 buckets with invalidations issued for the CloudFront distribution serving dangerouscentaur.com.
The update pipeline now includes a pre-deployment validation step that scans all outward-facing files for personal name references using regex patterns, preventing future attribution drift.
Platform Task Tracking System
We created a standardized task framework in the dashboard for managing non-live listing platforms. Each platform task includes:
- Platform name: GetMyBoat, WeddingWire, The Knot, etc.
- Credential reference: Pointer to
repos.env(credentials stored in environment, never in task descriptions) - API endpoints: Documentation of platform-specific listing APIs
- Sync requirements: Frequency and field mappings for inventory synchronization
- Contact templates: Which
jada_blast.pytemplates apply to each platform's inquiry workflow
This decouples platform management from the core blast tool, allowing parallel development on multiple integrations without blocking email campaign execution.
Key Decisions & Why They Matter
Widening-gap over concurrent sends: Rather than batch-and-send all recipients at once (creating obvious marketing patterns), the cadence system spaces sends across weeks. This improves deliverability, reduces complaint rates, and provides natural segmentation for A/B testing.
Suppression list caching: Calling SES API for every single contact would incur significant latency and costs. We cache suppression data with a 24-hour TTL, balancing freshness against performance.
Team branding over personal attribution: In high-volume outreach, personal names become liabilities—spam complaints, SEO penalties, and reputation concentration risk. Team branding distributes reputation risk and feels more professional to recipients.
Platform task decoupling: Rather than hard-coding platform integrations into the blast tool, we treat each platform as a discrete task with standardized credential and API documentation. This allows junior engineers to implement new platforms without modifying core blast logic.
What's Next
The immediate priorities are:
- Implement platform-specific inquiry handlers for GetMyBoat and WeddingWire APIs
- Add hyper-local personalization logic—dynamically inserting location-specific content based on recipient geography
- Build monitoring dashboards in CloudWatch tracking suppression list