Skip to Content
IssuesNotifications: 7 pipeline email template slugs missing from seed

Notifications: 7 pipeline email template slugs missing from seed

Status: ✅ Fixed
Severity: Medium
Component: packages/db/src/seed.ts, apps/servers/notifications/src/templates/email-loader.ts

Symptom

All pipeline milestone emails (strategy ready, deliverable plan ready, activity pipeline dispatched/failed, DM review request, SEO rank alerts, custom reports) silently failed. Notification server logs showed BullMQ email jobs failing with HTTP 400 from SendGrid.

Root Cause

Two compounding issues:

1. Missing seeds. The following templateSlug values were referenced in worker enqueueNotification calls but had no corresponding row in emailTemplate (DB):

SlugWorkerVariables
strategy_readystrategy-writer.worker.tsfirstName, companyName, strategyUrl, currentYear
deliverable_plan_readystrategy.worker.tsfirstName, companyName, deliverableCount, goalCount, monthlyCost, reviewUrl, currentYear
activity_pipeline_dm_reviewactivity.worker.tscompanyName, activityCount, periodStart, periodEnd, pipelineUrl, currentYear
activity_pipeline_readyactivity.worker.tsfirstName, companyName, activityCount, enqueuedCount, periodStart, periodEnd, totalCost, pipelineUrl, currentYear
activity_pipeline_failedactivity.worker.tsfirstName, companyName, periodStart, periodEnd, errorSnippet, planUrl, currentYear
seo-rank-alertgsc-keywords-snapshot.job.tskeyword, change, alertType, currentYear
report-readycustom-report-writer.worker.tsreportLabel, tenantName, requestedBy, currentYear

2. Silent fallback. email-loader.ts returned { subject: slug, html: "", text: "" } when no row was found instead of throwing. The notification server then called provider.send() with an empty body — SendGrid returns HTTP 400 for emails with no content. The BullMQ job failed but since pipeline workers wrap notification calls in try/catch (non-fatal), the pipeline continued without error logs.

Fix

packages/db/src/seed.ts — Added all 7 missing templates to EMAIL_TEMPLATES. Re-run seed to push to DB:

$env:DATABASE_URL="postgresql://leadmetrics:leadmetrics@localhost:5434/leadmetrics" npx ts-node packages/db/src/seed.ts

apps/servers/notifications/src/templates/email-loader.ts — Changed fallback from silent empty return to a thrown error:

// Before (silent — caused SendGrid 400): return { subject: slug, html: "", text: "" }; // After (loud — surfaces missing templates immediately): throw new Error(`Email template "${slug}" not found for tenant ${tenantId} — add it to the seed`);

Secondary fix: web handler bug

The web notification handler (web.handler.ts) was passing web_${type} as a literal Handlebars template string instead of looking up the template message from WEB_TEMPLATES. Fixed to use getWebTemplate(type) ?? type. The channel_suggestions_ready type (used by channel-action-suggester, channel: "web") was also added to WEB_TEMPLATES and NotificationType.

Rule going forward

Every enqueueNotification({ channel: "email", templateSlug: "..." }) call must have a corresponding entry in EMAIL_TEMPLATES in packages/db/src/seed.ts. The loader now throws immediately if a slug is missing, so failures surface on first run rather than as silent SendGrid 400s.

© 2026 Leadmetrics — Internal use only