Defaults plus per-tenant overrides
Template content lives in@duro/email as a set of defaults (keyed receipt, renewal_reminder, dunning_1…4, payment_recovered, card_expiring, welcome, …), each a { subject, body, channel }. A merchant can override any of them, stored per-tenant in the core schema’s email_templates. The resolver is simple and predictable:
Merge tags ({{store_name}}, {{amount}}, {{plan_name}}, {{invoice_id}}, {{update_link}}, {{next_attempt}}) are interpolated by an iterative renderer — no recursion, no template-injection surface. {{store_name}} is filled from the tenant automatically.
Wired into the lifecycle
The worker’sLifecycleMailer is the bridge between an event happening and an email being queued:
Each is rendered with the right context and enqueued, not sent — the email worker drains the queue against SMTP.
The full
dunning_1…4, card_expiring, and abandoned_cart… templates all ship as defaults and are what the dunning settings’ email-sequence editor arranges; the automatic worker lifecycle above currently dispatches dunning_1 on the first failure and payment_recovered on recovery. All template content is overridable per tenant.