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.
What you’d buy from Mailchimp, but agent-native.
| Dashboard SaaS | mailshot | |
|---|---|---|
| Operator | Humans clicking around a UI | An agent calling typed MCP tools |
| Configuration | Wizards, forms, drag-and-drop | TypeScript configs, version-controlled |
| Where subscribers live | On the vendor’s servers | In your own DynamoDB |
| Pricing | Per contact, per month, forever | Per AWS send — pennies |
| Exit | Their schema, their export, their pace | Your 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 referenceimport 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
From zero to live in four moves.
- 1
Scaffold
npx create-mailshot my-projectA monorepo with CDK, shared types, handlers, and an MCP server wired to your AWS account.
- 2
Create sequences
sequences/welcome/sequence.config.tsWrite 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
Deploy
/deployValidates types, checks template references, synthesizes CDK, and ships Step Functions + EventBridge + S3 templates in one go.
- 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 executionsThe 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/mcpThe MCP server connects Claude Code to your live AWS resources. Subscribers, engagement, templates, sequences, broadcasts, health — typed in, typed out.
Full tool referenceSubscribers
get_subscriberFetch a profile with attributes and historylist_subscribersFilter by tags, attributes, suppression statusupdate_subscriberPatch attributes, tags, contact detailsdelete_subscriberRemove a subscriber and stop active sequencesunsubscribe_subscriberMark unsubscribed and stop sequencesresubscribe_subscriberRe-engage a previously unsubscribed contact
Engagement
get_subscriber_eventsOpens, clicks, bounces per addressget_template_eventsRolled up per template — handy for A/B variantsget_sequence_eventsRolled up per sequence or broadcast
Templates
list_templatesEverything in your S3 template bucketpreview_templateRender against a live subscribervalidate_templateCheck Liquid syntax and variable shape
Sequences
list_sequencesAll deployed sequences with active countslist_sequence_subscribersWho is currently inside a given sequenceexport_sequenceReconstruct a deployed sequence as local code
Broadcasts
send_broadcastOne-off send filtered by tags or attributesget_broadcastAudience size + live engagement counterslist_broadcastsHistory of broadcasts, newest first
Suppression
list_suppressedAll suppressed addresses with the reasonremove_suppressionLift a hard-bounce or complaint flag
System
get_failed_executionsRecent Step Functions runs that failedget_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 projectscreate-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.