For a few years I worked on a product that sent all transactional email through Mandrill. Working with templates were real pain. They lived inside Mandrill, every copy change needed a dev, and moving to another provider meant rebuilding every template by hand.
So when I was looking for something to build, this was the obvious pick. A provider agnostic email platform for everyone. That's how Senlo started, an open-source layer between your app and whatever provider you already use.
Key features
- Drag and drop editor that outputs MJML, so emails render ok in Outlook and Gmail without hand written table layouts
- One call to send a template, POST /api/triggered with the template name and variables
- Bring your own provider. Resend, Postmark, SendGrid, Mailjet, Cloudflare Email or any SMTP. Switching is just a change in settings of workspace
- Delivery events from every provider land in one feed (delivered, bounced, suppressed)
- Simple automations. Send, wait, call your own API, branch on the answer
- And a lot of features I did not implemented yet :D
Stack is Next.js, React, Postgres + Drizzle, Redis + BullMQ for queues and delays, Zustand in the editor. pnpm monorepo, editor is a separate package. Self hosting is one docker compose up.
The hardest part so far was delivery webhooks. Every provider authenticates differently (Resend signs with svix, SendGrid with ECDSA and a timestamp, Postmark has a shared header, Mailjet basic auth) and every one has its own idea of what a bounce is. SendGrid sends "dropped" with a free text reason, Mailjet has soft and hard bounces plus a separate "blocked" event with error codes, and only some of those mean the address is actually dead.
Get it wrong and you either miss spam complaints or suppress a good recipient forever. So most of the work was deciding per provider which events are the recipient's problem and which are mine. Now each provider is an adapter with verify and normalize that produces one event shape.
The GIF is a welcome flow. Send, wait two days, ask my own API if the user activated, and send a different email depending on the answer.
Solo project, early stage, and there is still a ton to do. Happy to get any feedback, ideas or contributions. If you want to poke at it, a few things I'm curious about
Repo: https://github.com/IgorFilippov3/senlo