Notification System
Source: equa-server/modules/notifications/
Last Updated: 2026-02-28
Verified Against: Spec 021 verification-results.md (13/13 tasks PASS)
Overview
The notification system handles transactional email delivery for the Equa platform. It implements aNotifier interface that accepts a user ID and a notification object, renders HTML from Handlebars templates, and sends via a configurable transport layer. The module is backend-only with no dedicated API endpoints — it is consumed as a library by auth, admin, referral, billing, and organization modules.
Architecture
Core Interface
TheNotifier type from common:
newNodeMailerNotifier (email-notifier.ts:64-90) creates a Notifier that:
- Resolves recipient email via
getContactInfo(user)or falls back tonotification.args.to - Looks up the template by
notification.templatename - Renders HTML via the compiled Handlebars template
- Sends via
transporter.sendMail({ from, to, bcc, subject, html }) - Calls optional
onFinishedcallback
to is undefined or template is not found.
Email Transports
Transport is selected via theEMAIL_TRANSPORTER environment variable in services.ts:96-113:
Transport Composition
combineNodeMailerMethods (email-notifier.ts:49-62) sends via multiple transports simultaneously using Promise.all. Returns the last transport’s response. Used in test utilities to combine dev + test methods.
Template System
Loading (templates.ts:27-35)
newEmailTemplateMap loads templates synchronously at startup:
- Reads all
.handlebarsfiles fromsrc/partials/and registers as Handlebars partials - Reads all
.handlebarsfiles fromsrc/templates/and compiles with{ noEscape: true } - Returns a map of
{ templateName: compiledFunction }
noEscape: true means all Handlebars expressions render raw HTML without escaping. This is intentional for HTML email content but requires that template variables are sanitized upstream if they contain user-provided content.Email Templates (10)
Layout Partials (3)
AWS SES Configuration
newSesClient (aws.ts:6-13) creates an AWS SES client with API version 2010-12-01 using aws-sdk v2.
newAwsConnectionConfig (aws.ts:15-21) builds credentials from environment:
The SES region is passed as a function parameter (not read from env directly by the notifications module). The wiring in
services.ts reads AWS_SES_REGION and passes it through. These are separate credentials from S3 file storage (AWS_S3_ACCESS_KEY_ID / AWS_S3_SECRET_ACCESS_KEY).Complete Environment Variables
Type Definitions
Core Types (types.ts)
Transport Types (email-notifier.ts)
User Email Resolution
UserEmailGetter is implemented as getUserContactInfo in persistence/src/common/reading.ts:528-542:
- SQL:
SELECT "fullName", users."email" FROM "users" LEFT JOIN profiles ON profiles."id" = $1 WHERE "users"."id" = $1 - Returns
{ name: fullName, address: email }orundefinedif not found - Wired in
services.ts:137and passed tonewNodeMailerNotifier
Legacy: Mandrill
Themandrill/ subdirectory contains a Mandrill (Mailchimp Transactional) implementation that is fully commented out. The index.ts re-exports types only. This is dead code from a previous email provider; current production uses Nodemailer + SES exclusively.
Known Issues
Related
- Notifications Guide — End-user guide
- Auth and Permissions — Authentication triggers
- File Storage Architecture — Shares AWS credential patterns
- Environments and Config — Environment variable reference