Model or dataset
marclou/mailcheap avatar
marclou/mailcheap

mailcheap: a newsletter app you pay for per email sent

Newsletters where you pay for the emails you send, not for dormant subscribers, at a fraction of the price of beehiiv and the like. Open source: self-host it, or use mailcheap.ai.

1,319 stars337 forksJavaScriptAGPL-3.0

At a glance

What is it?
mailcheap is a self-hostable newsletter application built on Next.js that charges for the emails you send rather than for the subscribers on your list, with the same code available as a hosted service. The most substantial part of it is not the editor but the deliverability machinery, and it exposes everything to an agent through an MCP server.
Who is it for?
Adopt mailcheap if you send a newsletter and are tired of paying for dormant subscribers, if you want to keep the sending account and the domain in your own hands, and if you want an agent to be able to draft and send issues through a documented MCP server. Do not adopt it if you need a supported vendor, because the self-hosted path has no support at all, and do not assume the deliverability protections are a substitute for good list hygiene.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Pay for sends, not for subscribers

MailCheap is a newsletter app where you pay for the emails you send, not for dormant subscribers, at a fraction of the price of beehiiv and the like. The repository topics make the competitive set explicit with beehiiv-alternative, mailchimp-alternative and substack-alternative among them.

That pricing model is the whole idea, and it changes what an application has to do. A service that charges per subscriber has to keep billing the same account for an address that stopped opening three years ago. A service that charges per send has to price the actual work, and the work is the email, its delivery and the reputation of the sending domain.

The scope is a full newsletter application rather than a sending library. You write an issue, send it to your list, and publish it on the web, from the dashboard or from an AI agent. The repository is the whole app: the Next.js application, the editor, the sending pipeline, the website generator for each newsletter, the statistics, the imports and the agent integration.

The licence is AGPL-3.0, the primary language is JavaScript, and the homepage is mailcheap.ai, which runs the same code as a hosted service. The last push was on 2026-10-19, the repository is not archived, and there are no GitHub releases, so the deployment target is the main branch rather than a tagged version.

The dashboard, the API and the agent are the same product

The first feature entry is the one that decides who this is for. Everything the dashboard does also works from an AI agent through the MCP server, and from code through the REST API, with the same checks and the same limits.

The authentication story is documented with the same specificity. ChatGPT, Claude and Grok Bot connect with OAuth, while Claude Code, Codex and Cursor connect with an API key.

The detail that matters most for safety is in the same paragraph. MCP marks sending as an action that cannot be undone, so agents ask you first. A tool that lets a model send email to your subscribers without confirmation is a tool that will, eventually, send email to your subscribers without confirmation. Encoding the irreversibility into the protocol description, so the agent knows to ask, is the cheapest possible mitigation, and the documentation is explicit that it is a mitigation rather than a guarantee.

The dependency list backs this up. There is the Model Context Protocol server package, and better-auth contributes three of its own: an MCP package, a cimd package, and an OAuth provider package, so the agent integrations are first-class rather than bolted on.

Write, review, send

The editor and the review step are the parts a newsletter user touches on every issue, and the README describes them as a three step flow.

You write in an editor with slash commands, images and boxes, or you send Markdown or a complete HTML email through the API and MCP. The editor is built on TipTap, with the React integration, the starter kit, the image and file handler extensions, the suggestion extension and the ProseMirror packages behind it, so the writing experience is a rich text editor rather than a plain textarea.

The review step is the interesting one, because it is where the app applies what it knows about deliverability to your draft. The review shows what to fix before sending, and the list is specific: the subject, the preview text, the unsubscribe link, Gmail's 100 KB clip, the postal address and the sender. Those are the six things that most often get a newsletter filtered or rejected, and checking them in the editor is cheaper than finding out from a bounce report.

Then you send it, schedule it, or start with your most engaged readers and send to more later. That last option is a deliverability strategy as much as a feature: sending a new issue to the active segment first and widening the audience only if the response is clean is what keeps a new sending domain out of trouble.

The website side is part of the same product. Every newsletter gets a page that search engines can find, with a signup form, an author page, share images and a sitemap, and on the hosted service that is a free subdomain, `<slug>.mailcheap.ai`, while on your own server it lives at `/archive/<slug>`.

The deliverability machinery

The largest part of the feature list is about not getting your sending domain destroyed, and the mechanisms are specific enough to be worth reading in detail.

The warm-up is the first. Contacts the account never emailed go out as a 5% slice first, and the rest three hours later if the slice's bounces and complaints stay low. On the hosted service an account's sends also speed up as it proves clean volume. That is how a new domain earns a sending reputation, and doing it automatically is the difference between a service you can use on day one and one you have to babysit.

List cleanup is on by default. Readers who ignored several issues get one email asking whether they still want this, and then stop getting issues. Contact cleaning happens at import time, leaving out invalid addresses, misspelled ones like gmial.com, spam traps, role addresses, throwaway addresses and duplicates, plus domains that cannot receive email, and anyone who unsubscribed, bounced or complained before.

Then there is the never-email list, which is shared across the whole server. An address the email provider says does not exist, for 180 days, or one that turned out to be a spam trap, for good, is left out of every account's imports, signups and sends, and only a keyed hash of it is kept. The privacy design is deliberate, and the key management behind it is what makes it work.

The limits are the harshest part. A send pauses past 2% hard bounces or 0.08% complaints, and the account is blocked past 5% or 0.3%, or on a spam trap hit. The app applies these limits to your own sending account.

Gmail is handled separately, because Gmail never reports one complaint at a time. Every email carries a Feedback-ID identifying its campaign or newsletter, its account and its kind, and once an admin connects Google Postmaster Tools the app reads it every day. An account Gmail flags at 0.08% has its sends paused, at 0.3% it is blocked, and the admins hear when the whole domain reaches 0.1%.

And the compliance basics are on by default: one-click unsubscribe under RFC 8058, a signed unsubscribe link in every email, and confirmed opt-in.

Cloud or self-hosted, and what each one costs

The comparison table between the hosted service and a self-hosted install is the clearest statement of the trade.

On price, the hosted service starts at $19 a month for 20,000 emails, while self-hosting is free plus what your provider charges, $0.10 to $0.16 per 1,000 on Amazon SES. The README puts a number on the realistic case: 10,000 readers and four issues a month is 40,000 emails, about $4 to $6 on Amazon SES.

On sending, the hosted service is set up for you and sends from a shared address, `hello@<slug>.mailcheap.ai`, or from your domain, while self-hosted uses your own Amazon SES or PostShiba account from your own domain. The website is the subdomain on the hosted service and `/archive/<slug>` on your server. Who signs in is anyone who signs up on the hosted service, against the emails you allow when you self-host, and the limits are plan-based on the hosted service and none at all when you self-host.

The practical difference is the last two rows. Setup is a few minutes on the hosted service and about an hour self-hosted, most of it waiting for DNS and production access. Support is a chat in the dashboard, against none, where the issues are off.

The two are the same codebase. MailCheap Cloud is this repository with MAILCHEAP_CLOUD=true, which adds open sign-up, plans and Stripe billing, and only active subscribers count toward a plan rather than the ones list cleanup marked dormant. Its marketing site, the landing, pricing and legal pages, is not part of the open source, and every change to the app ships to both.

The stack behind the app

The dependency list describes a modern Next.js application with a few decisions worth pulling out.

The framework is Next 16.4.1 with React 19.2.9, and the engine requirement is Node 22 or later. The database is MongoDB through mongoose 9.10.4, and the.env.example file notes that a replica set gets transactions while a plain mongod works without them.

Email delivery goes through the AWS SDK, with clients for SES v2, S3 and CloudFront, which is the self-hosted path. The hosted path uses Resend for MailCheap's own emails, and Stripe handles the billing, with a webhook secret alongside the secret key.

The rest of the stack is the usual modern set with a few details. better-auth handles sessions and the agent authentication. Upstash provides Redis, the rate limiter and QStash for background work. TipTap is the editor, radix-ui the component primitives, lucide-react the icons, next-themes the light and dark switching, react-hot-toast the notifications, zod the validation, jose the token signing, marked the Markdown, sanitize-html the HTML cleaning, and papaparse the CSV import.

The test setup is unusually plain for a Next.js project. The test script is node --import./tests/setup/register.mjs with the test glob tests/, which is the Node built-in test runner rather than a framework, and the tests/ directory in the repository root is the rest of the setup. The build scripts are next dev, next build, next start and eslint.

The repository also carries AGENTS.md, a.cursor/ directory, an instrumentation.js file, a proxy.js file, a models/ directory for the data model, lib/ for the server logic and a SECURITY.md.

The environment variables that decide how it starts

The .env.example file is the most useful document in the repository for a self-hoster, and it is unusually well annotated.

The mode variable is MAILCHEAP_CLOUD. It must be exactly true on the hosted service, because it switches on billing, plan limits and open sign-up, and it also switches MailCheap's own emails to Resend only. The server refuses to start without the Stripe keys and the Resend key when that mode is on, and on a self-hosted server the variable is left unset, which means no billing, no limits and sign-in restricted to the allowlist. The comment adds that the public pages read it at build time, so it has to be set for builds as well.

The required self-hosted variables are MONGODB_URI, BETTER_AUTH_SECRET, SUPPRESSION_HASH_KEY, APP_URL outside Vercel, ADMIN_EMAILS and EMAIL_FROM. The database string should include the database name, for example the Atlas string with /mailcheap before the question mark, and the local example is a plain mongodb on 127.0.0.1 with the mailcheap database.

BETTER_AUTH_SECRET signs sessions and, through keys derived from it, every link in sent emails, including unsubscribe, confirm and tracking. It must be at least 32 random characters, and the file gives the command: openssl rand -base64 48. The warning attached to it is the one to memorise, because rotating it signs everyone out and breaks the links in emails already sent.

SUPPRESSION_HASH_KEY is the key over the hashes of the never-email list that every account on the server shares. It needs at least 32 random characters of its own, and it must never be the same value as BETTER_AUTH_SECRET.

The hosted mode adds STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET and RESEND_API_KEY, and the Vercel deploy button in the README carries the same list of environment variable names, so the two deployment routes ask for the same configuration.

Editorial conclusion

Adopt mailcheap if you send a newsletter and are tired of paying for dormant subscribers, if you want to keep the sending account and the domain in your own hands, and if you want an agent to be able to draft and send issues through a documented MCP server. Do not adopt it if you need a supported vendor, because the self-hosted path has no support at all, and do not assume the deliverability protections are a substitute for good list hygiene. Before you start, generate a secret of at least 32 characters with openssl rand -base64 48 for BETTER_AUTH_SECRET and a different one for SUPPRESSION_HASH_KEY, decide whether to run with MongoDB as a replica set, since a plain mongod works but transactions need the replica set, and read the never-email list behaviour before you import a large list.

Frequently asked questions

How does mailcheap charge differently from beehiiv?

You pay for the emails you send rather than for the subscribers on your list, so dormant addresses that never open do not keep costing you. On the hosted service the price starts at $19 a month for 20,000 emails, and self-hosting is free plus the per email cost of your own provider, about $0.10 to $0.16 per 1,000 on Amazon SES.

Can an AI agent use mailcheap?

Yes. Everything the dashboard does also works from an agent through the MCP server and from code through the REST API, with the same checks and limits. ChatGPT, Claude and Grok Bot connect with OAuth while Claude Code, Codex and Cursor connect with an API key, and MCP marks sending as an action that cannot be undone so agents ask you first.

What deliverability protections does mailcheap have?

Automatic warm-up that sends a 5% slice first and the rest three hours later if the results stay clean, list cleanup for readers who stopped opening, contact cleaning at import time, a shared never-email list of addresses the provider says do not exist, send pauses past 2% hard bounces or 0.08% complaints, blocks past 5% or 0.3%, daily Google Postmaster Tools reporting through a Feedback-ID header, and one-click unsubscribe under RFC 8058 with confirmed opt-in.

How do I self-host mailcheap?

You need Node 22 or later, a MongoDB connection string, and secrets for BETTER_AUTH_SECRET and SUPPRESSION_HASH_KEY, each at least 32 random characters and different from each other. Set MAILCHEAP_CLOUD only if you want the hosted behaviour, since the server refuses to start without the Stripe and Resend keys in that mode, and the deployment button in the README is set up for Vercel.

Can I import my existing newsletter subscribers into mailcheap?

The CSV exports from beehiiv, Substack and Mailchimp import as they are, with each reader's status and signup date, and the same is true for Kit, MailerLite, Buttondown, Ghost and EmailOctopus. Imported contacts are filtered, so invalid, misspelled, spam trap, role and throwaway addresses are left out.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. marclou/mailcheap on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/marclou-mailcheap.svg)](https://hysenlabs.com/projects/marclou-mailcheap)

Was this useful?