- inject signed unsubscribeUrl into every optional-workflow Novu payload
(templates render it via {{#if unsubscribeUrl}} footer)
- add 'conversion' campaign workflow to OPTIONAL_WORKFLOWS + email_marketing
category so its one-click tokens validate and opt-outs suppress it
- declare UNSUBSCRIBE_SECRET/URL_BASE/EMAIL in env schema (""→undefined
preprocess against the url().optional() boot-crash trap), .env.example and
both compose env blocks
- fix confirmation-page settings link (?tab=notifications)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route all lifecycle/transactional emails through Novu
(api.bildirim.semih.ai, delivered via Postal). A framework-agnostic
client is shared by the NestJS API and the standalone BullMQ worker.
- welcome + referral on signup (better-auth user.create.after)
- email-verification + password-reset (auth.ts; token links never
track-wrapped so the one-time token survives)
- referral-qualified / referral-reward to the referrer on qualification
- payment-success / payment-failed in the Stripe webhook handlers
- trial-ending + win-back via a new daily lifecycle-email cron (worker),
idempotent via a 1-day endDate window (no sent-flag column)
- signed track.sase.tr CTA links when MAILTRACK_SECRET is set
- NOVU_* / APP_PUBLIC_URL / MAILTRACK_SECRET env added to config,
validation, .env.example and both compose service blocks
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>