Module

Email

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

terminal
pithy add email
Read the docs Source on GitHub

What it is for

Transactional 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

D1, Cloudflare Workflows and the Email binding.

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.

  • D1
  • Workflows
  • Email

The honest bit

No mail is ever sent inline, and that will change how you write handlers.

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

Sending is trivial. Not losing mail is not.

The first send takes an afternoon. The failure modes take the rest of the quarter.

Retries that do not double-send

A timeout is not a failure. Distinguishing them, and making the retry idempotent, is why every send here is a durable job.

Bounces and suppression

Hard bounces must stop future sends to that address, or your domain reputation does it for you, permanently.

DKIM, SPF and DMARC

Alignment, key rotation and a subdomain policy — the difference between arriving and arriving in spam.

Open and click tracking

A redirect service, a pixel, and unsubscribe handling that honors the header rather than only the link.

Templates that survive a client

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.

terminal
pithy add email

Composes with

It depends on seams, never on another module's internals.

In the dashboard

Administer it from a browser, without building the screen.

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.

Add it, and get on with the feature.

One command installs the package, wires its bindings into every environment your project declares, and runs its migrations.