Files
webadderall eaf87be95b Recordly 2.0 local review stack 2/6
Dependency-ordered review preparation. Not pushed or remotely reviewed.
2026-11-06 05:24:21 +11:00

7.5 KiB

Recordly transactional email designs

All 27 authentication, security, subscription and billing designs use the shared renderer. Its colour and large-button values are generated from the installed @heroui/styles default theme and Button CSS by scripts/build-return-pages.mjs, then exported as concrete inline values in theme.generated.mjs. The emails keep the approved full-width banner, original Recordly mark and flat layout.

HTML email cannot run HeroUI's React/JavaScript components. It also cannot reliably use CSS variables, OKLCH colours or flex layout across clients. The build converts HeroUI colours to hex and button dimensions to pixels, keeping presentation tables, inline styles and Outlook fallbacks. The app's DM Sans is explicitly applied to text and table cells; clients that block web fonts use system/Arial fallbacks. A plain-text alternative preserves content and links without visual styling.

Copy is warm, lightly playful and concise. Payment and security messages keep the required action clear. Each catalog item also includes inbox preview text (preheader); the shared renderer uses it rather than repeating the subject. Keep user-approved exact copy and provider placeholders when adjusting tone.

Run npm run email:build after changing a catalog, the renderer or HeroUI. It regenerates the HeroUI theme snapshot before the email files. This generates HTML and plain-text files for all 27 messages. Run npm run email:preview and open http://127.0.0.1:3500/ to inspect them or make browser-local copy drafts.

Delivery status

The 13 authentication/security HTML templates in ../supabase/templates/ were published separately through Supabase. All seven optional security notifications were enabled and verified after reload on 28 October 2026. Their generated .txt files are reference alternatives; this build does not configure Supabase's MIME parts. The received authentication email's plain-text part has not been inspected.

The 14 subscription/billing messages in billing/ have been connected in the private hosted backend to verified Stripe events, a durable D1 outbox, scheduled renewal/trial/card reminders and a Resend HTML/text sender. That implementation has passed local Worker/D1 tests. Production billing delivery remains off pending deployment, webhook configuration and a provider delivery check. A sending-only Resend key restricted to the verified auth subdomain is now stored as a Cloudflare Worker secret. The private backend keeps credentials and recipient selection outside this public desktop source. This folder itself has no queue or network side effects; building or previewing never sends mail. All 27 manual previews were sent separately to the owner's inbox; those previews did not test production billing delivery. Trial messaging is conditional on a real trial; it does not create or enable trials.

Design Intended trigger
Subscription activated First verified paid activation, not merely subscription creation
Plan changed Verified plan change at its effective date
Upcoming renewal Upcoming invoice with the actual expected total and renewal date
Cancellation scheduled Cancellation newly scheduled for period end
Cancellation reversed Previously scheduled cancellation removed
Subscription ended Cancellation effective and account returned to Free
Trial ending Trial ending, only if trials are offered; use the actual post-trial outcome
Payment received / renewal paid Verified successful payment; distinguish from activation
Payment failed Failed subscription invoice payment
Payment needs confirmation Payment awaiting customer authentication
Card expiring Default payment method approaching expiry
Refund issued Confirmed issued refund, including the actual refunded amount
Invoice available / payment reminder Open invoice or reminder; current amount due and due date
Credit note issued Issued credit note; never imply a cash refund without confirmation

Stripe receipts, invoices and recovery emails remain separately controlled in Stripe. Before connecting these designs, choose one sender for each notification to avoid sending duplicate receipts or reminders.

The prepared billing sender is Recordly <notifications@auth.recordly.dev> on the existing verified sending subdomain, with support@recordly.dev as reply-to. Support mail now forwards to the owner's inbox. notifications@recordly.dev can be used later after verifying the root sending domain. The existing Supabase SMTP sender (noreply@auth.recordly.dev) is unchanged; this source does not create mailboxes, set up forwarding or verify domains. The verified auth subdomain can also support a notifications address on that same subdomain.

Stripe's shared icon, brand blue (#006fee) and dark accent (#18181b) were updated through the dashboard on 28 October 2026. Checkout has explicit matching colours, Inter (DM Sans isn't offered in its font menu), with provider controls for background colour and rounded/pill shapes. This branding does not activate billing, change prices, turn on email notifications or replace Stripe's own component layout. The 512-pixel PNG in stripe/ is rendered from the existing Recordly SVG mark for Stripe's icon size requirements.

Render a billing email

Import renderBillingEmail from render-billing-email.mjs and pass a trusted item from billing/catalog.json plus its required values. It returns { subject, html, text }. Include both html and text when sending with Resend (or the equivalent multipart/alternative parts with SMTP).

const message = renderBillingEmail(activationTemplate, {
  PlanName: "Recordly Cloud",
  Amount: formattedActualPrice,
  Interval: formattedActualBillingInterval,
  RenewalDate: formattedActualRenewalDate,
  ActionURL: authenticatedBillingPageOrStripeLink,
});

Use server-verified subscription/payment data and real formatted currency/taxes, dates and customer locale. Preview prices and dates are illustrative only. The renderer requires all fields, escapes HTML, rejects header line breaks and permits HTTPS action links only on the managed Recordly origin and listed Stripe payment hosts. Stripe links must be obtained from Stripe for the intended customer; do not construct them from arbitrary webhook metadata. Use a durable outbox and event deduplication before production delivery; the rendering helper does not implement those billing responsibilities. Persist delivery state independently of entitlement processing, handle event ordering, and do not let an email failure change access. Never put secret billing links or customer records in this catalog.

Failure previews

The Rendering menu offers:

  • Normal: HTML, images and web fonts as supported by the browser.
  • Images blocked: a CSP blocks image and font requests in the preview frame. Text and actions remain. Clients may reserve different amounts of image space.
  • No formatting: removes CSS and decorative images; HTML text and links remain.
  • Plain text: the generated text alternative, including amounts/dates, actions and URLs. Edited browser drafts derive a text approximation from their current HTML, without changing the generated text files.

These are browser simulations, not certification for Gmail, Outlook or Apple Mail. HTML is included in the message rather than fetched as a remote webpage. A failed image/font request does not remove the email body. A client that only shows text needs a text MIME part; generating a .txt file alone does not send that part.

Renderer checks: node --test services/email/render-billing-email.test.mjs.