Skip to content
The foundation under itUI and contentemail

Email the app sends

Invitations, notifications, export links, report deliveries.

Growth-OS sends mail for a small set of things: inviting a teammate, a notification someone subscribed to, a data export link, a dashboard report, a time-off note. Templates are typed components in the repository with a shared responsive layout, so changing one is an ordinary code change with review and history.

  • 1 path every email goes through
  • 5 shared templates in the repository
  • 0 email secrets in browser code

How it works

The mechanism end to end, as implemented in the repository.

  1. 1

    Pick or write a template

    Typed components take props for names, amounts and links. Five shared templates use one responsive layout, and the local preview shows desktop, mobile, text and source.

  2. 2

    Send from the server

    A server service or background job calls one helper with the component, a recipient and a subject. The helper reads server-only configuration, so it cannot end up in browser code.

  3. 3

    It renders and delivers

    The helper renders the component to HTML with a plain-text alternative and hands it to the provider. A provider error is rethrown so the calling job owns the retry.

  4. 4

    Unconfigured is a skip, not a lie

    Without a key or a from-address the helper logs a development warning and returns without claiming a delivery happened.

Interactive example

See it working

This walkthrough demonstrates the implemented backend contract without using an account, credentials, or provider calls.

Interactive example

Explore the decisions this makes

Choose a sample state
Welcome component

Deterministic sample data only. No account, provider, or network connection is used by the interaction.

The problem this solves

Without a shared foundation, every team rebuilds these edge cases, differently.

The template that broke quietly

Mail that lives in a vendor dashboard has no link to the code. A field gets renamed, and weeks later someone receives a message with a placeholder in it. Typed templates fail the build instead.

Five features, five ways to send

Each feature spinning up its own client means from-addresses drift, errors are swallowed differently, and changing provider becomes a hunt. One helper is one place to change.

Blocked on day one without a key

An app that crashes at boot without an email key forces everyone to either hold production credentials or wrap every send in a condition. Here a missing key turns a send into a logged skip.

Highlights

Why it matters

Templates are code

Five shared templates live in the repository as typed components with matching MJML variants. Renaming a field they use fails the build instead of sending a broken line to someone.

One door for every send

Every feature and background job sends through the same helper, so the from-address, rendering and error handling live in one place.

No keys, no crash

Without an email key a send logs a warning and returns. Local work and tests run without production credentials, and nothing pretends a message was delivered.

Engineering guarantees

What this does and does not promise

Safety boundary

This page previews what a template looks like; it never sends anything. Whether a message reaches an inbox depends on domain setup and the provider, which is outside this code. Campaigns in the workspace are a separate thing and still send nothing at all.

Implementation evidence

The boundary above is code, not a claim. These files carry the contract:

  • src/lib/email.ts
  • src/lib/email/templates
  • src/features/privacy/jobs.ts
Questions

Frequently asked questions

What email the app sends rests on

The docs describe the same contracts this page demonstrates.