Getting Started with Notification Engine
Configure your email provider, create custom templates, enable push notifications, manage user preferences, and send your first notification — all via the unified Notification Engine API.
Configure Your Email Provider
Navigate: System → Notifications → Settings → Email Provider
SendGrid:
```bash # Environment variables SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SENDGRID_FROM_EMAIL=noreply@your-domain.com SENDGRID_FROM_NAME="Your Company"
# Or in notification_settings collection: { provider: "sendgrid", apiKey: "SG.xxx", // Encrypted at rest fromEmail: "noreply@your-domain.com", fromName: "Your Company", trackOpens: true, trackClicks: true, templates: { orderConfirmation: "d-abc123", passwordReset: "d-def456", } } ```
Mailgun:
```bash MAILGUN_API_KEY=key-xxxxxxxxxxxxxxxxxxxxx MAILGUN_DOMAIN=mg.your-domain.com MAILGUN_FROM_EMAIL=noreply@your-domain.com
# In settings: { provider: "mailgun", apiKey: "key-xxx", domain: "mg.your-domain.com", } ```
Amazon SES:
```bash AWS_ACCESS_KEY_ID=AKIA... AWS_SECRET_ACCESS_KEY=... AWS_REGION=us-east-1 SES_FROM_EMAIL=noreply@your-domain.com
# In settings: { provider: "ses", accessKeyId: "AKIA...", secretAccessKey: "...", region: "us-east-1", } ```
Email provider fallback: If no provider configured, Aeion uses built-in SMTP delivery for moderate volumes. Upgrade to SendGrid/Mailgun for higher volume and deliverability analytics.
Configure SMS (Twilio)
Navigate: System → Notifications → Settings → SMS Provider
bash
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_PHONE_NUMBER=+15551234567
SMS settings:
typescript
{
provider: "twilio",
accountSid: "ACxxx",
authToken: "xxx", // Encrypted at rest
fromNumber: "+15551234567",
maxLength: 160, // Single SMS limit
unicodeEnabled: true, // Support emoji and international chars
trackDelivery: true, // Delivery receipts
}
SMS templates (short, actionable):
```typescript // Order shipped: "Your order #{{orderNumber}} shipped! Track: {{trackingUrl}}";
// Appointment reminder: "Reminder: {{eventName}} tomorrow at {{time}}. Join: {{link}}";
// Payment failed: "{{tenantName}}: Payment failed. Update now: {{updateUrl}}"; ```
Enable Push Notifications (Web)
Navigate: System → Notifications → Settings → Push
VAPID keys for Web Push:
```bash # Generate VAPID key pair (one-time) # Use web-push npm package: npx web-push generate-vapid-keys
# Output: # { # publicKey: "BEl62iUYgUivxEfkvtf...", # privateKey: "UUxI4O8...", # }
VAPID_PUBLIC_KEY=BEl62iUYgUivxEfkvtf... VAPID_PRIVATE_KEY=UUxI4O8... VAPID_SUBJECT="mailto:notifications@your-domain.com" ```
Firebase Cloud Messaging (FCM) for mobile:
```bash # In Firebase Console: Project Settings → Cloud Messaging # Copy Server Key or use V1 API with Service Account
FCM_SERVER_KEY=... FCM_PROJECT_ID=your-project-id ```
Push settings:
typescript
{
web: {
vapidPublicKey: "BEl62i...",
vapidPrivateKey: "UUxI4...", // Encrypted
subject: "mailto:notifications@your-domain.com",
TTL: 86400, // 24 hours
urgency: "normal",
},
mobile: {
fcm: {
serverKey: "...",
projectId: "your-project",
},
apns: {
keyId: "...",
teamId: "...",
bundleId: "com.your.app",
keyFile: "/path/to/AuthKey_xxx.p8", // Encrypted
},
},
}
Client-side subscription (service worker registration):
```typescript // In your app: async function subscribeToPush() { const sw = await navigator.serviceWorker.ready; const sub = await sw.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY), });
// Send subscription to server await fetch("/api/notifications/push/subscribe", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ endpoint: sub.endpoint, keys: { p256dh: sub.getKey("p256dh"), auth: sub.getKey("auth"), }, platform: "web", browser: navigator.userAgent, }), }); } ```
Send Your First Notification
Via API:
```typescript // Using the unified send API const result = await notificationService.send({ userId: "usr_abc123", channel: "email", subject: "Welcome to Acme!", body: "<h1>Welcome!</h1><p>We're glad you're here.</p>", priority: "normal", });
// Or with template: const result = await notificationService.send({ userId: "usr_abc123", channel: "email", template: "welcome", data: { userName: "John Doe", tenantName: "Acme Corp", verifyEmailUrl: "https://your-domain.com/verify?token=xyz", }, priority: "high", // High priority bypasses queue }); ```
Via event (automatic):
No code required — the Notification Engine already listens for every standard system event and fires the matching built-in handler:
auth.user.registered→ welcome email, the moment a user signs upcommerce.order.created→ order confirmation, the moment an order is placedhelpdesk.ticket.resolved→ CSAT survey, the moment a ticket closes
Any module's standard action — placing an order, resolving a ticket, registering a user — triggers its corresponding notification automatically. You only write custom send code (Step 4 above) for messages outside the 24 built-in handlers.
Multi-channel fallback:
```typescript // Send email first, fall back to SMS if email fails const result = await notificationService.send({ userId: "usr_abc123", channels: ["email", "sms"], // Try email, then SMS template: "payment_failed", data: { amount: 150.0, updateUrl: "..." }, });
// Result shows both delivery attempts: { deliveries: [ { method: "email", success: false, error: "bounced" }, { method: "sms", success: true, messageId: "SMxxx" }, ]; } ```
Create Custom Templates
Navigate: System → Notifications → Templates → New Template
Email template (HTML):
```html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>{{subject}}</title> </head> <body style="font-family:Arial,sans-serif;"> <div style="max-width:600px;margin:0 auto;padding:20px;"> <div style="background:#2563eb;padding:20px;border-radius:8px 8px 0 0;"> <h1 style="color:#fff;margin:0;">{{tenantName}}</h1> </div> <div style="background:#f9fafb;padding:20px;border-radius:0 0 8px 8px;"> <h2 style="color:#1f2937;">{{title}}</h2> <p style="color:#6b7280;">{{message}}</p>
{{#if actionUrl}} <a href="{{actionUrl}}" style="display:inline-block;background:#2563eb;color:#fff;padding:12px 24px;border-radius:6px;text-decoration:none;margin:16px 0;" > {{actionLabel}} </a> {{/if}}
<p style="color:#9ca3af;font-size:12px;"> Questions? Reply to this email or contact support. </p> </div> </div> </body> </html> ```
Variables:
typescript
// Template variables that can be passed in data:
{
subject: "Your order shipped", // Email subject
title: "Order Shipped!", // Email heading
message: "Your order #12345 is on its way.", // Body text
tenantName: "Acme Corp",
actionUrl: "https://your-domain.com/track/12345",
actionLabel: "Track Order",
orderNumber: "12345",
shippingCarrier: "FedEx",
trackingNumber: "1234567890",
}
SMS template:
typescript
// Keep under 160 chars (single SMS)
"{{tenantName}}: Your order #{{orderNumber}} shipped via {{carrier}}. Track: {{trackingUrl}}";
Push template:
typescript
{
title: "{{tenantName}}",
body: "{{title}}",
icon: "/icons/notification-icon.png",
badge: "/icons/badge.png",
tag: "order-update", // Deduplication
data: {
type: "order_shipped",
orderId: "{{orderId}}",
actionUrl: "{{actionUrl}}",
},
}
Configure User Preferences
Navigate: Users → [User] → Notification Preferences
User preference management:
```typescript // Get user's preferences const prefs = await notificationService.getUserPreferences("usr_abc123"); // → Returns NotificationPreference object
// Update preferences await notificationService.updatePreferences("usr_abc123", { channels: { email: { enabled: true, address: "john@work.com" }, sms: { enabled: false }, push: { enabled: true }, inApp: { enabled: true }, }, quietHours: { enabled: true, start: "22:00", end: "08:00", timezone: "America/New_York", allowUrgent: true, // Payment failures always send }, frequencyCap: { maxPerHour: 3, maxPerDay: 10, consolidate: true, // Batch excess into digest }, });
// Unsubscribe from category await notificationService.unsubscribeCategory("usr_abc123", "marketing"); // → Sets marketing.enabled = false ```
Quiet hours UI:
User sees in their notification preferences:
┌─────────────────────────────────────────┐
│ Quiet Hours │
│ [✓] Enable quiet hours │
│ │
│ Start: [22:00] End: [08:00] │
│ Timezone: [America/New_York ▼] │
│ │
│ [✓] Allow urgent notifications │
│ (payment failures, security alerts) │
│ │
│ During quiet hours, notifications are │
│ queued and delivered at 08:00. │
└─────────────────────────────────────────┘
Snooze a single channel (Do-Not-Disturb). Separate from recurring quiet hours, a user can mute an individual channel until a chosen time via the mutedUntil preference — e.g. silence push for the afternoon while email keeps flowing. The channel auto-unmutes when the timestamp passes; quiet hours and per-channel snooze stack independently.
Set Up Webhook Delivery Tracking
Navigate: System → Notifications → Settings → Webhooks
Configure outbound webhook:
```typescript // Your endpoint receives delivery events // POST https://your-domain.com/api/webhooks/notifications
{ event: "notification.delivered", notificationId: "not_abc123", userId: "usr_xyz", channel: "email", provider: "sendgrid", timestamp: "2026-05-11T10:30:00Z", data: { ... } }
// Verify HMAC signature: import crypto from "crypto";
function verifyWebhook(req) { const payload = JSON.stringify(req.body); const signature = req.headers["x-aeion-signature"]?.replace("sha256=", ""); const expected = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(payload) .digest("hex"); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); } ```
Webhook events:
notification.sent— Notification dispatched to providernotification.delivered— Confirmed delivered to devicenotification.opened— Email opened (pixel)notification.clicked— Link clickednotification.bounced— Bounce detectednotification.failed— Delivery failed after retriesnotification.unsubscribed— User unsubscribed
Delivery status dashboard:
``` Notification Console → Delivery Logs
┌────────────┬─────────┬───────────┬────────────┬────────┐ │ User │ Channel │ Template │ Status │ Time │ ├────────────┼─────────┼───────────┼────────────┼────────┤ │ john@... │ Email │ order... │ ✅ Delivered│ 10:30 │ │ jane@... │ Push │ ticket... │ ✅ Delivered│ 10:28 │ │ bob@... │ Email │ billing │ ⚠️ Bounced │ 10:25 │ │ alice@... │ SMS │ remind... │ ✅ Delivered│ 10:20 │ └────────────┴─────────┴───────────┴────────────┴────────┘
Open rate: 72% | Click rate: 34% | Bounce rate: 2.1% ```
Complete Example — Order Shipped Notification Flow
When an order ships, the flow runs end to end with zero custom code:
- The Commerce module marks the order shipped (tracking URL, carrier, estimated delivery).
- The Notification Engine picks up the shipment event automatically.
- It looks up the customer's email address and notification preferences.
- Preferences are checked — order updates enabled, quiet hours 22:00–08:00 America/New_York, current time 10:30 AM (outside quiet hours), frequency cap 3/hour with 2 already sent this hour → send is allowed.
- The HTML email is rendered with the order's details.
- It's sent through your configured email provider (SendGrid, in this example).
- The delivery is logged for the tracking dashboard.
User receives:
``` Subject: Your order ORD-2026-00123 has shipped! From: Acme Corp <noreply@acme.com>
[Logo] Your order is on its way!
Order #ORD-2026-00123 Shipped via FedEx
Track Shipment: [https://track.fedex.com/7894561230]
Estimated delivery: May 14, 2026
Questions? Reply to this email. [Unsubscribe] | [Notification Preferences] ```
When the user opens the email or clicks the tracking link, those events update the delivery status in real time — visible on the Delivery Logs dashboard.