Simplifying Email Infrastructure Without Disruption
Our client runs a transactional email platform for enterprise customers. Their applications send mail through an HTTP API and SMTP, and the platform checks each tenant's sending rules, tracks every delivery, open, click and unsubscribe, and gives customers a web portal to manage it all.
The product worked, but the codebase behind it had become hard to change. It was spread across eleven repositories of Lambda functions, builder pipelines and edge-routing workarounds, and a single feature often needed several pull requests and a carefully ordered deploy. We moved the whole platform into one TypeScript monorepo running as containers on Amazon EKS, without asking a single customer to change their integration.

The Challenge
The platform was running in production and processing customer email continuously, but its architecture had become difficult to maintain.
- Customer applications depended on exact endpoints, error codes and response shapes, so the API had to stay 100% backwards compatible
- Three apps, a shared library, builder repos and Terraform modules had to be released in lock-step, and one missed piece could break production
- Manual database updates had left differences between environments
- Every account needed secure access to its own applications and message data
- One API call could fan out into many personalized copies, and a failure in one copy could overwrite the record of another
- Gmail and Yahoo's 2024 bulk sender rules required one-click unsubscribe links that were signed, replay-safe and safe against mail scanners clicking them automatically
- Existing tests did not always catch the regressions they were meant to prevent
The goal was to simplify email infrastructure without asking customers to rebuild their integrations.
Our Solutions
We brought the Mail API, Provisioning API, customer portal, and shared code into one TypeScript monorepo. Containerized services on Amazon EKS replaced the core Lambda-based setup, and a single pipeline coordinated releases. The Next.js portal became a standalone application with runtime configuration, simplifying deployment across environments.
That let us retire the API gateways, the serverless framework, the builder repos and a proxy fleet. Template rendering stayed in an isolated Lambda sandbox on purpose, because customer-written templates should never run inside the API.
We moved data access from hand-built SQL helpers to Drizzle ORM on Aurora PostgreSQL, while every endpoint kept answering exactly as before. Schema changes now go through versioned migrations, and a CI gate fails any pull request where the migrated schema doesn't match a fresh build. Every migration ships with a rollback that has actually been run.
On the product side, we rebuilt access control as one fail-closed layer for the HTTP API, SMTP and the portal. A caller with no applications sees nothing, and another tenant's message returns the same 404 as a missing one. The send pipeline now records an outcome for every recipient, so a failed copy in a fan-out rejects only its own rows. We also introduced a three-level unsubscribe model and RFC 8058 one-click unsubscribe support.
Finally, we set up a 12-job DevOps CI pipeline on GitHub Actions and a full local stack in Docker Compose, including real SMTP delivery. Every fix is tested the hard way: we remove the guard, confirm the test fails, then put the guard back.
Results
- 11 repositories consolidated into one monorepo
- 100% backwards-compatible migration
- Core API moved from Lambda to autoscaled EKS containers
- Cold starts removed from the main API request path
- Database schema drift blocked automatically in CI
- Accurate delivery outcomes for every recipient
- RFC 8058 one-click unsubscribe support
- 1,700+ Mail API tests running on every merge
- Full platform can now run locally, including SMTP delivery
The result was a platform the team could release, troubleshoot, and maintain more confidently, while customers kept using the integrations they already trusted.


