mailshot

Architecture

System overview, AWS services, data flow, caching, and cost model.

Design principles

  • Product-agnostic - The framework defines contracts (event shapes, subscriber schema, template format). You provide your own sequences, templates, and events.
  • Zero idle cost - No servers running between emails. You only pay when a step executes.
  • AI-native operations - No admin UI. All management happens through Claude Code via MCP tools and skills.
  • Single-table DynamoDB - All subscriber state in one table, engagement events in a second. No relational database.

AWS services

ServiceRole
EventBridgeEvent ingestion and routing. Custom bus receives events from your app. Rules route to Step Functions (sequences) or Lambda (fire-and-forget).
Step FunctionsSequence orchestration. One state machine per sequence. Handles branching, delays, and step ordering.
LambdaEight functions: SendEmail, CheckCondition, Unsubscribe, BounceHandler, EngagementHandler, Subscribe, Broadcast, ReplyHandler (conditional).
SQSBroadcast send queue with dead-letter queue. Fans out broadcast emails to SendEmailFn with backpressure and retries.
DynamoDBTwo tables. Main table: subscriber profiles, active executions, send logs, suppression records. Events table: engagement tracking (opens, clicks, deliveries, replies).
S3HTML email templates. Deployed via CDK BucketDeployment. Cached in Lambda memory for 10 minutes.
SESEmail delivery with open/click tracking via configuration set.
SNSRoutes SES notifications (bounces, complaints, engagement events) to Lambda handlers.

Data flow

┌──────────────────────────────────┐
│ Your App Backend                 │
│ publishes events to EventBridge  │
└───────────────┬──────────────────┘


┌──────────────────────────────────────────────────┐
│ EventBridge (Custom Bus)                         │
│                                                  │
│ Sequence rules ─────→ Step Functions             │
│ Event rules ────────→ SendEmailFn                │
│ Subscribe rule ─────→ SubscribeFn                │
│ Broadcast rule ─────→ BroadcastFn ──→ SQS Queue │
└───────┬──────────────────┬──────────────┬────────┘
        │                  │              │
        ▼                  ▼              ▼
 Step Functions     SendEmailFn      BroadcastFn
   │                    │               │
   ├─ register ─────────┤          Scans subscribers
   ├─ send (per step) ──┤          by tags/attributes
   ├─ wait               │               │
   ├─ choice (native)   │               ▼
   ├─ condition ───→ CheckConditionFn  SQS Queue
   ├─ complete ─────────┤               │
   │                    │               ▼
   └────────────────────┤          SendEmailFn
                        │          (per subscriber)
                ┌───────┴───────┐
                ▼               ▼
           DynamoDB           S3 Templates
           (state)            (HTML + Liquid)


               SES ──→ Recipient

                ├─ Bounce/Complaint ──→ SNS ──→ BounceHandlerFn
                │                                  └─ suppress subscriber
                │                                  └─ stop executions

                ├─ Engagement ────────→ SNS ──→ EngagementHandlerFn
                │  (open/click/delivery)           └─ write to Events table

                └─ Unsubscribe link ──→ UnsubscribeFn (Function URL)
                                           └─ mark unsubscribed
                                           └─ stop executions

  Inbound Reply ──→ SES Receipt Rule ──→ SNS ──→ ReplyHandlerFn
                    (if REPLY_TO_EMAIL set)        └─ write reply to Events table
                                                   └─ publish email.replied to EventBridge
                                                   └─ exitOn rules stop sequences

Cost model

At 1,000 subscribers, total cost is under $5/month:

  • Step Functions: $0.025 per 1,000 state transitions. A 5-email sequence is 12 transitions per run ($0.30 per 1,000 executions).
  • Lambda: Negligible at low volume. 128–256MB functions running for less than 1 second per invocation.
  • DynamoDB: On-demand pricing. A few cents per month at 1,000 subscribers.
  • SES: $0.10 per 1,000 emails.
  • S3: Negligible for storing HTML templates. Compare this to $29–$299/month for email SaaS tools that do the same thing.