Every copy change is a deployment
A typo in the payment failed email subject line triggers a pull request, a code review, a merge and a deploy. For a text change.
Hardcoded email templates, inline HTML strings and scattered send() calls are a maintenance problem waiting to become a production incident.
A typo in the payment failed email subject line triggers a pull request, a code review, a merge and a deploy. For a text change.
send() calls live in billing services, auth controllers, background jobs and webhooks. No one has a complete picture.
if (user.locale === "de" && user.plan === "Pro") { sendEmail("payment-failed-pro-de-template", ...) } — repeated for every variant.
A broken link or wrong variable in production email templates is an emergency requiring an expedited release.
Finding all emails sent to a specific user requires grepping logs across multiple services.
Fire a domain event from your app. else.events handles the routing, templating and delivery.
Edit copy, swap logos and update CTAs from the else.events dashboard. No deploy required.
Rules match event type, plan, locale and tenant. Your app fires one event; rules decide which template fires.
Replace scattered send() calls with a single POST to /api/events. Your app describes what happened, not what to send.
Every event, every matched rule, every email delivery — in one place with domain context.
// replace your send() calls with this
{
"type": "invoice.payment_failed",
"locale": "en",
"user": { "email": "customer@example.com", "name": "Alex" },
"data": {
"plan": "Pro",
"amount": "29.00",
"currency": "EUR",
"update_payment_url": "https://app.example.com/billing"
}
} else.events matches this event against your rules (plan = Pro, locale = en) and sends the correct template through your provider. No if/else in your app.
One event replaces a scattered collection of send() calls. Start with the most painful email first.