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.

1

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.

2

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}}"; ```

3

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, }), }); } ```

4

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 up
  • commerce.order.created → order confirmation, the moment an order is placed
  • helpdesk.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" }, ]; } ```

5

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}}", }, }

6

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.

7

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 provider
  • notification.delivered — Confirmed delivered to device
  • notification.opened — Email opened (pixel)
  • notification.clicked — Link clicked
  • notification.bounced — Bounce detected
  • notification.failed — Delivery failed after retries
  • notification.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:

  1. The Commerce module marks the order shipped (tracking URL, carrier, estimated delivery).
  2. The Notification Engine picks up the shipment event automatically.
  3. It looks up the customer's email address and notification preferences.
  4. 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.
  5. The HTML email is rendered with the order's details.
  6. It's sent through your configured email provider (SendGrid, in this example).
  7. 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.

Multi-Channel Notifications — Built Into Every Deployment