Skip to content

Notification Service

The Notification service is the centralized messaging hub for Microtec ERP. It delivers email, SMS, and push notifications on behalf of all other microservices, decoupling message delivery logic from business logic across the platform.


Overview

PropertyValue
ServiceNotification.Apis
CAE placementPrivate CAE
Gateway route prefix/notification-apis/
SourcePlatforms/Src/InfrastructureServices/Notification/
ChannelsEmail, SMS, Push (web + mobile)
Template dependencyTemplate.Apis (renders message bodies)
Auth realmmicrotec (Keycloak)

Architecture


Channel Types

ChannelEnum ValueDelivery MechanismUse Cases
EmailNotificationChannel.EmailSMTP / SendGridInvoices, reports, alerts
SMSNotificationChannel.SmsTwilio / local SMS gatewayOTPs, payment reminders
Push (Web)NotificationChannel.WebPushFirebase Cloud Messaging (FCM)Real-time in-app alerts
Push (Mobile)NotificationChannel.MobilePushFCM (Android) + APNs (iOS)Mobile app alerts
In-AppNotificationChannel.InAppStored in DB, polled by frontendWorkflow approvals, system messages

Domain Model


API Endpoints

MethodRouteOperation
POST/Notifications/SendSend an immediate notification
POST/Notifications/SendBulkSend to multiple recipients
POST/Notifications/ScheduleSchedule for future delivery
GET/Notifications/{Id}Get notification status
GET/Notifications/InAppGet in-app notifications for current user
POST/Notifications/InApp/{Id}/ReadMark in-app notification as read
POST/DeviceTokens/RegisterRegister FCM/APNs device token
DELETE/DeviceTokens/{Id}Unregister device token

INotificationPublicApi

All ERP services call the Notification service through this typed client:

csharp
// Defined in Microtec.PublicApi.AppsPortal
public interface INotificationPublicApi
{
    /// <summary>
    /// Send a notification using a pre-defined template.
    /// The Notification service calls Template.Apis to render the body.
    /// </summary>
    Task SendAsync(
        SendNotificationRequest request,
        CancellationToken ct = default);

    /// <summary>
    /// Send the same notification to multiple recipients.
    /// </summary>
    Task SendBulkAsync(
        SendBulkNotificationRequest request,
        CancellationToken ct = default);

    /// <summary>
    /// Schedule a notification for future delivery.
    /// </summary>
    Task ScheduleAsync(
        ScheduleNotificationRequest request,
        CancellationToken ct = default);
}

SendNotificationRequest

csharp
public record SendNotificationRequest(
    int TenantId,
    int RecipientUserId,
    string RecipientEmail,
    string? RecipientPhone,
    NotificationChannel Channel,
    string TemplateCode,
    Dictionary<string, string> TemplateData,
    string Language = "ar");

Usage Examples

Sending an Invoice Email

csharp
// Inside PostInvoiceCommandHandler
public async Task<ApiResponse> Handle(PostInvoiceCommand request, CancellationToken ct)
{
    var invoice = await unitOfWork.Repository<Invoice>().GetByIdAsync(request.Id);
    invoice.Post();
    await unitOfWork.SaveChangesAsync(ct);

    // Notify customer via email
    await notificationApi.SendAsync(new SendNotificationRequest(
        TenantId: tenantProvider.TenantId,
        RecipientUserId: invoice.CustomerId,
        RecipientEmail: invoice.CustomerEmail,
        RecipientPhone: null,
        Channel: NotificationChannel.Email,
        TemplateCode: "INVOICE_POSTED",
        TemplateData: new()
        {
            ["InvoiceNumber"] = invoice.Number,
            ["InvoiceDate"]   = invoice.InvoiceDate.ToString("yyyy-MM-dd"),
            ["TotalAmount"]   = invoice.TotalAfterTax.ToString("N2"),
            ["CustomerName"]  = invoice.CustomerName
        },
        Language: "ar"
    ), ct);

    return ApiResponse.Success();
}

Sending a Payroll Completion SMS

csharp
await notificationApi.SendAsync(new SendNotificationRequest(
    TenantId: tenantProvider.TenantId,
    RecipientUserId: employee.Id,
    RecipientEmail: string.Empty,
    RecipientPhone: employee.MobileNumber,
    Channel: NotificationChannel.Sms,
    TemplateCode: "PAYROLL_SALARY_CREDITED",
    TemplateData: new()
    {
        ["EmployeeName"] = employee.NameAr,
        ["Amount"]       = payrollLine.NetAmount.ToString("N2"),
        ["Month"]        = payrollRun.PeriodMonth.ToString("MMMM yyyy")
    },
    Language: "ar"
), ct);

Bulk Push Notification

csharp
await notificationApi.SendBulkAsync(new SendBulkNotificationRequest(
    TenantId: tenantProvider.TenantId,
    RecipientUserIds: approverIds,
    Channel: NotificationChannel.WebPush,
    TemplateCode: "WORKFLOW_APPROVAL_REQUIRED",
    TemplateData: new()
    {
        ["EntityType"]   = "PurchaseOrder",
        ["EntityNumber"] = purchaseOrder.Number,
        ["Amount"]       = purchaseOrder.Total.ToString("N2")
    },
    Language: "ar"
), ct);

Template Resolution

When a notification request arrives, the Notification service calls Template.Apis to render the message body:


Notification Statuses

StatusMeaning
PendingRequest received, queued for delivery
RenderingCalling Template.Apis to render body
SendingSubmitted to channel provider
SentProvider acknowledged delivery
DeliveredConfirmed delivery (email open tracking / SMS DLR)
FailedDelivery failed after retry exhaustion

Retry Policy

Failed notifications are retried with exponential back-off:

AttemptDelay
1 (initial)Immediate
230 seconds
35 minutes
4 (final)30 minutes

After 4 failed attempts, the status is set to Failed and an in-app alert is generated for the platform admin.


Device Token Registration

Mobile and web apps register their push tokens at login:

http
POST /notification-apis/DeviceTokens/Register
Authorization: Bearer <token>

{
  "token": "fcm-or-apns-token-here",
  "platform": "Android"
}

Platforms: Android, iOS, Web.


Configuration

json
// appsettings.json (Notification.Apis)
{
  "Email": {
    "Provider": "SendGrid",
    "SendGrid": {
      "ApiKey": "<keyvault-ref>",
      "SenderEmail": "noreply@onlinemicrotec.com.sa",
      "SenderName": "Microtec ERP"
    }
  },
  "Sms": {
    "Provider": "Twilio",
    "Twilio": {
      "AccountSid": "<keyvault-ref>",
      "AuthToken": "<keyvault-ref>",
      "FromNumber": "+9665XXXXXXXX"
    }
  },
  "Push": {
    "Fcm": {
      "ServerKey": "<keyvault-ref>"
    }
  },
  "Services": {
    "Template": "https://mic-erp-be-template.internal.<env>.azurecontainerapps.io"
  },
  "Keycloak": {
    "Authority": "https://<keycloak-host>/realms/microtec",
    "Audience": "account"
  }
}

Provider Selection

The Email:Provider key selects the active email provider. Switching between SendGrid and Smtp does not require code changes — only config. Set to Smtp for local development using a local SMTP relay like MailHog.


Deployment

The Notification service is listed in the pipeline service config:

Devops/azure/config/container-backend/services-config.json  →  "notification"

Internal Documentation — Microtec Platform Team