The stack I kept building for every SaaS I shipped:
Mailchimp or Loops for campaigns: $50 to $99/mo
Postmark or Resend for transactional: $10 to $50/mo
Customer-io or a homegrown drip system for onboarding: $100+/mo or your weekends
Plus glue code to wire billing events into all three, plus a sync job to keep audience lists in sync with your user table, plus a separate merge tag setup in each tool because none of them know your user model.
For a solo founder pre-revenue, that's $15 to $250/mo out the door before you have paying customers. And you still own the integration mess. Every time you add a new billing event, you're updating it in three places.
I got tired of it. So when I started building BuildBase, the React/Next.js SaaS backend SDK I've been shipping with my brother, we decided the email layer wasn't going to be a separate concern. It was going to live inside the same SDK as auth, workspaces, billing, and workflows.
Two years of build later, the last missing piece shipped this week. Wanted to write up what "a real ESP inside your backend" actually means, because I underestimated it badly when we started.
What we thought would take a month
Transactional email is easy. Send a request to an API, get a message ID back, done. If that's all you need, buy Postmark and move on.
Campaign email is where it gets hard. Here's what we ended up needing:
That list is the difference between "we send email" and "we run an ESP." Every item on it was a week of work I didn't budget for.
The design lesson I want to share
The last piece shipped this week: organization-level merge tags. Values like {{appName}} or {{supportEmail}} or {{privacyPolicyUrl}} that you set once at the org level and every email template renders automatically.
The interesting decision was around delete behavior. First instinct: let users delete tags. Obvious.
Wrong. If you delete a merge tag that's referenced in a live template, the email renders with a broken placeholder and you don't find out until a customer emails support asking why the subject line says "Welcome to {{undefined}}."
So we made tags immutable. You can archive them, which hides them from the picker and flags them on save if you're editing a template that still references them, but existing templates keep rendering fine. Nothing breaks silently.
Same reasoning applied to the slug. Once a tag is created, the {{key}} is locked forever. Rename requires archive + recreate.
If you're building anything template-driven, steal this pattern. Every variable I've ever nuked in production has come back to bite me. Archive-not-delete costs nothing and prevents an entire class of production incidents.
Was building it worth it?
Honest answer: the ROI hasn't landed yet because BuildBase is still pre first external paying customer. So I can't tell you "we saved $X/mo across N customers."
What I can tell you:
We run four of our own paid products on it (PlugNode, AgentCenter, Imejis, RemoteWait). Every welcome email, password reset, campaign, and drip on those products goes through this system. That's real production traffic. Not scaled, but real.
Merge tags referencing user, workspace, and subscription data don't need a sync job. The template rendering context has direct database access. Every ESP I've paid for required either a batch sync or a real-time webhook to keep audience data current. That's a class of bug that just doesn't exist in this architecture.
One dashboard for the customer. If BuildBase becomes the SaaS backend layer for another founder, they get campaigns, transactional, drips, and audience management in the same place they manage auth and billing. That's the pitch, and it only works because the ESP is native, not bolted on.
What I'd tell anyone at the same fork
If your product only needs transactional email, buy Postmark. It's cheap, deliverable, and someone else handles the deliverability wars with Gmail. Don't build.
If your product needs campaigns + drips + transactional wired to your billing events + audience data that stays in sync with your user table, the integration cost of renting three tools is higher than the sticker price suggests. Build it, or pick a backend that has it built in.
Still missing on my side: bounce webhook handling for hard-bounce suppression, and the RFC 8058 List-Unsubscribe header for Gmail/Yahoo bulk-sender compliance. Both are on the near-term list. I'd rather ship honest than ship overclaimed.
If you want to see it, BuildBase is at https://buildbase.app.
Happy to answer questions on the architecture, the deliverability tradeoffs, or the build-vs-buy math in the comments.