D1-backed email for Cloudflare. Every send is a durable job: scheduled, retried, tracked. Themeable templates, click, open and unsubscribe tracking, and bounce-driven suppression. No mail ever sent inline.
@pithy-sh/email
pithy add emailTransactional mail that survives a bad afternoon: scheduled, retried, tracked. Themeable templates, click, open and unsubscribe tracking, and bounce-driven suppression so you stop mailing an address that is gone.
What it sits on
A request handler never sends email. It enqueues a row, and the actual send always runs inside a Workflow — so every send is durable, retryable and auditable.
The honest bit
You cannot await a send and tell the user it went. You enqueue, and the Workflow owns delivery from there. That is strictly more reliable and slightly less convenient, and it is not configurable — a send that can fail silently inside a request is the bug this design exists to prevent.
If you build it yourself
The first send takes an afternoon. The failure modes take the rest of the quarter.
A timeout is not a failure. Distinguishing them, and making the retry idempotent, is why every send here is a durable job.
Hard bounces must stop future sends to that address, or your domain reputation does it for you, permanently.
Alignment, key rotation and a subdomain policy — the difference between arriving and arriving in spam.
A redirect service, a pixel, and unsubscribe handling that honors the header rather than only the link.
Inline styles, table layouts, dark mode, and the fact that one popular client still renders like it is 2007.
Every send is enqueued and delivered by a Workflow, so a send cannot vanish quietly inside a request that timed out.
pithy add emailComposes with
In the dashboard
The queue, what was delivered, what bounced, and who is suppressed.
The dashboard is the admin interface for your own application: whatever this capability writes, your team can look up, check and act on from a browser. Reading is free, forever, with no time limit. Connect the project when you deploy.
Everything else
Each one composes the same way. Nothing you skip costs you anything.
One command installs the package, wires its bindings into every environment your project declares, and runs its migrations.