Skip to content

✨ Welcome email after signup #4116

Description

@PierreBrisorgueil

What — A new user receives one welcome email when their workspace is created.
Why — Today the only emails at signup are transactional (verify, reset); nothing greets the user or points them to the first step.

Scope

  • No welcome template exists in config/templates/ (12 templates). The mailer resolves ./config/templates/<name>.html (lib/helpers/mailer/index.js:87), so a downstream can override the HTML with a same-named file.
  • Send point: inside createOrganizationForUser (modules/organizations/services/organizations.service.js ~60-115), in the if (created) block next to organizationEvents.emit('organization.created', …). It only runs from handleSignupOrganization's provisioning branches: never on convergence, never from the manual "create another org" path (organizations.crud.service.js:135), so it is exactly-once with no dedup field. Do not hook organization.provisioned (fires on convergence) or organization.created (also fires from the manual crud path).
  • Fix: add config/templates/welcome.html; best-effort, non-blocking mailer.sendMail({ template: 'welcome', to, subject, params: { displayName, organizationName, url, appName, appContact } }), behind a toggle (e.g. config.mailer.welcome.enabled, default true).

Edge cases — idempotent by construction; no i18n in the template pipeline today (out of scope).

Tests — new modules/organizations/tests/organizations.service.welcomeEmail.unit.tests.js: sent on created, not on convergence, not when disabled, a mailer failure does not break signup.

Refs: #3747 (generic templates precedent)

Update (scope re-validated)

  • Toggle: config.organizations.welcomeEmail.enabled in the organizations module config (not config.mailer, which is transport only), read with ?? true, commented as fail-open.
  • Send point: in handleSignupOrganization, after buildResult, in both create branches (orgs enabled + disabled) — never on convergence, never on manual org create. A rollback after a send can no longer mail a removed user.
  • Fire-and-forget, gated on mailer.isConfigured(): Promise.resolve(mailer.sendMail(...)).catch(warn) inside a try — never await, never block signup / OAuth redirect.
  • Params: { displayName, url: getBaseUrl(), appName: config.app.title, appContact: config.app.contact } (+ orgName only when organizations are enabled; add the getBaseUrl import). The template must not require the org name (B2C hides it). Subject Welcome to ${appName}.
  • Existing mocks break: patch organizations.service.signup.unit.tests.js:21-23, organizations.service.silent.catch.unit.tests.js:58-60, organizations.emailVerification.unit.tests.js:23-25, organizations.emailVerification.policy.unit.tests.js:40-42 → sendMail: jest.fn().mockResolvedValue(null).
  • Tests also: B2C branch sends without orgName; sendMail rejecting or returning undefined → signup still succeeds.
  • Refs: createOrganizationForUser is 55-119; with strict email verification the welcome goes out after verification (provisioning runs in verifyEmail).

Scope: validated 2026-09-25
Created via /dev:issue

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions