Email infrastructure your AI agent operates.

No dashboard. A typed, code-first platform on AWS — Claude Code creates sequences, deploys them, queries engagement, suppresses bounces, previews against live subscribers. Your account, your data.

Read the docs

What you’d buy from Mailchimp, but agent-native.

Dashboard SaaSmailshot
OperatorHumans clicking around a UIAn agent calling typed MCP tools
ConfigurationWizards, forms, drag-and-dropTypeScript configs, version-controlled
Where subscribers liveOn the vendor’s serversIn your own DynamoDB
PricingPer contact, per month, foreverPer AWS send — pennies
ExitTheir schema, their export, their paceYour repo. MIT licensed. Walk away anytime.

Mailchimp, Customer.io, Loops, and friends are good. They’re built for a different operator.

Pennies per execution. No fixed cost.

mailshot is MIT-licensed and runs on your own AWS account. There is nothing to pay the framework for, ever.

A 5-email sequence costs ~$0.30 per 1,000 runs in Step Functions, plus ~$0.50 in SES sends. At 10,000 subscribers you’re looking at under $10/month total AWS bill.

You pay AWS for state transitions, email sends, and DynamoDB reads. Nothing runs between emails. Nothing accrues when you sleep.

Sequences are TypeScript. CDK deploys them.

Triggers, steps, delays, conditional branches — everything lives in sequences/. CDK auto-discovers each file and ships it as a Step Functions state machine.

Write the config by hand, or hand it to Claude Code. Either way it’s code you own and review before deploy.

Sequence reference
sequences/welcome/sequence.config.tsTypeScript
import type { SequenceDefinition }
  from "@mailshot/shared";

export default {
  id: "welcome",
  trigger: {
    detailType: "customer.created",
    subscriberMapping: {
      email: "$.detail.email",
      firstName: "$.detail.firstName",
    },
  },
  steps: [
    { type: "send",
      templateKey: "welcome/hello",
      subject: "Welcome aboard" },
    { type: "wait", days: 2 },
    { type: "send", ... },
    { type: "choice",
      field: "$.subscriber.attributes.plan",
      branches: [
        { value: "pro", steps: [...] },
        { value: "free", steps: [...] },
      ],
    },
  ],
} satisfies SequenceDefinition;

Bring whatever HTML you already make.

mailshot is an orchestration framework. It doesn’t care how you author the HTML — only that there’s an .html file at the end. At send time it injects subscriber data via LiquidJS {{ firstName }} variables. That’s the only contract.

Known to work with

  • React Email
  • MJML
  • Raw HTML
  • Claude · ChatGPT · any LLM
  • Figma → HTML
  • Your existing templates
Template docs

From zero to live in four moves.

  1. 1

    Scaffold

    npx create-mailshot my-project

    A monorepo with CDK, shared types, handlers, and an MCP server wired to your AWS account.

  2. 2

    Create sequences

    sequences/welcome/sequence.config.ts

    Write the config by hand, or describe what you want to Claude Code. Drop your HTML templates in next to it — any tool, any format.

  3. 3

    Deploy

    /deploy

    Validates types, checks template references, synthesizes CDK, and ships Step Functions + EventBridge + S3 templates in one go.

  4. 4

    Operate

    mcp: "What are open rates on onboarding?"

    Twenty-three MCP tools let Claude Code query engagement, suppress bounces, preview templates, debug deliveries — all from inside your editor.

Data flow

Your app publishes events to EventBridge. Rules route to Step Functions for sequences or Lambda for one-off sends. SES delivers, then ships engagement back into DynamoDB.

Architecture reference
┌──────────────────────────────────┐
│ Your App Backend                 │
│ publishes events to EventBridge  │
└───────────────┬──────────────────┘
                │
                ▼
┌──────────────────────────────────┐
│ EventBridge (Custom Bus)         │
│                                  │
│ Sequence rules ──→ Step Functions│
│ Event rules ────→ SendEmailFn    │
└───────┬──────────────────┬───────┘
        │                  │
        ▼                  ▼
 Step Functions        SendEmailFn (fire-and-forget)
   │                       │
   ├─ register ────────────┤
   ├─ send (per step) ─────┤
   ├─ wait                  │
   ├─ choice (branch)       │
   ├─ condition ───→ CheckConditionFn
   ├─ complete ────────────┤
   │                       │
   └───────────────────────┘
                │
        ┌───────┴───────┐
        ▼               ▼
   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

The operator is an agent with no UI to reach for.

Traditional email tools assume a human at a dashboard, clicking through wizards and copy-pasting between tabs. mailshot assumes the opposite. Three things follow.

Typed schemas, not screen reads.

Sequences are typed configs. Templates are HTML with named Liquid variables. Subscribers are DynamoDB rows with typed attributes. An agent introspects every shape, validates it before sending, and emits code that compiles — no scraping, no guessing.

One MCP, full surface area.

Twenty-three MCP tools cover the entire control plane — subscribers, engagement, templates, suppression, sequences, broadcasts, health. No screen scrapes. No API-key juggling. A typed function in, a typed response out.

Code is the source of truth.

Sequences live in your repo. CDK auto-discovers them. Changes ship through git, not Save buttons. Your agent writes the diff and runs typecheck; you keep the final review.

MCP server

23 tools. One command to wire.

claude mcp add mailshot -- npx @mailshot/mcp

The MCP server connects Claude Code to your live AWS resources. Subscribers, engagement, templates, sequences, broadcasts, health — typed in, typed out.

Full tool reference

Subscribers

  • get_subscriberFetch a profile with attributes and history
  • list_subscribersFilter by tags, attributes, suppression status
  • update_subscriberPatch attributes, tags, contact details
  • delete_subscriberRemove a subscriber and stop active sequences
  • unsubscribe_subscriberMark unsubscribed and stop sequences
  • resubscribe_subscriberRe-engage a previously unsubscribed contact

Engagement

  • get_subscriber_eventsOpens, clicks, bounces per address
  • get_template_eventsRolled up per template — handy for A/B variants
  • get_sequence_eventsRolled up per sequence or broadcast

Templates

  • list_templatesEverything in your S3 template bucket
  • preview_templateRender against a live subscriber
  • validate_templateCheck Liquid syntax and variable shape

Sequences

  • list_sequencesAll deployed sequences with active counts
  • list_sequence_subscribersWho is currently inside a given sequence
  • export_sequenceReconstruct a deployed sequence as local code

Broadcasts

  • send_broadcastOne-off send filtered by tags or attributes
  • get_broadcastAudience size + live engagement counters
  • list_broadcastsHistory of broadcasts, newest first

Suppression

  • list_suppressedAll suppressed addresses with the reason
  • remove_suppressionLift a hard-bounce or complaint flag

System

  • get_failed_executionsRecent Step Functions runs that failed
  • get_delivery_statsSES bounce + complaint rates over time

Skills

  • sync_skillsRefresh the local Claude Code skill set

Production-ready out of the box.

  • Full SES integration

    Delivery tracking, open/click events, bounce handling, List-Unsubscribe headers, HMAC-signed unsubscribe URLs.

  • Conditional branching

    Choice steps branch on subscriber attributes. Condition steps query DynamoDB at runtime for send history and profile changes.

  • Automatic suppression

    Bounces and complaints suppress subscribers and stop all active Step Functions executions. No manual intervention.

  • Serverless, pay-per-use

    Step Functions, Lambda, EventBridge, DynamoDB, S3. Pay-per-execution on your own AWS account. No fixed cost.

  • Single-table DynamoDB

    Subscribers, executions, send logs, suppression — one table. Engagement events in a second. No relational database.

  • Bring your own templates

    React Email, MJML, hand-coded, Figma exports, AI-generated. mailshot just renders variables with LiquidJS at send time.

Published on npm.

  • @mailshot/sharedTypes, constants, DynamoDB key helpers
  • @mailshot/handlersFive Lambda handlers + shared lib
  • @mailshot/cdkCDK infrastructure, modular constructs
  • @mailshot/mcpMCP server for Claude Code
  • @mailshot/skillsClaude Code skills shipped to user projects
  • create-mailshotCLI scaffolder

Hand the email stack to your agent.

Scaffold the project, describe a sequence to Claude Code, deploy. From zero to a live sequence on your own AWS account in an afternoon.

Quickstart

mailshot

MIT licensed · free forever

Built by @mdwt