Skip to content

Notification Recipes — Workflow, Authz, Encryption

These recipes show how to combine Granit.Notifications with other framework modules for common real-world scenarios.

Workflow + Notifications: notify on state transition

Section titled “Workflow + Notifications: notify on state transition”

When an entity transitions in a workflow, publish a notification automatically. The WorkflowStateChangedEvent is published by Wolverine — handle it in a notification handler:

public static class InvoiceApprovalNotificationHandler
{
public static async Task Handle(
WorkflowStateChangedEvent @event,
INotificationPublisher publisher,
CancellationToken ct)
{
if (@event.NewState != nameof(InvoiceStatus.PendingApproval))
return;
await publisher.PublishAsync(new NotificationRequest(
Type: "InvoiceApprovalRequired",
EntityType: "Invoice",
EntityId: @event.EntityId,
Recipients: [@event.Properties["ApproverId"]],
Data: new { InvoiceNumber = @event.Properties["Number"] }), ct)
.ConfigureAwait(false);
}
}

Authorization + Notifications: permission-gated channels

Section titled “Authorization + Notifications: permission-gated channels”

Use IPermissionChecker to restrict notification channels based on the sender’s permissions (e.g., only managers can trigger SMS notifications):

public class SmsPermissionFilter(IPermissionChecker permissionChecker)
: INotificationChannelFilter
{
public async Task<bool> ShouldSendAsync(
NotificationContext context, CancellationToken ct)
{
return await permissionChecker
.IsGrantedAsync("Notifications.Sms.Send")
.ConfigureAwait(false);
}
}

Encryption + Notifications: protect PII in notification payloads

Section titled “Encryption + Notifications: protect PII in notification payloads”

When notification data contains PII (patient names, account numbers), encrypt the Data dictionary before persisting. The InApp channel stores the notification in the database — combine with IStringEncryptionService:

public class EncryptedNotificationEnricher(IStringEncryptionService encryption)
: INotificationDataEnricher
{
public Task EnrichAsync(NotificationContext context, CancellationToken ct)
{
if (context.Data.TryGetValue("PatientName", out var name))
context.Data["PatientName"] = encryption.Encrypt(name?.ToString() ?? "");
return Task.CompletedTask;
}
}

Identity + Notifications: zero-boilerplate recipient resolution

Section titled “Identity + Notifications: zero-boilerplate recipient resolution”

Apps on Granit.Identity get a deliverable pipeline without writing an IRecipientResolver. The opt-in Granit.Identity.Notifications bridge is the default path: it resolves recipient contact details straight from the identity layer — local (OpenIddict) and every federated provider (Keycloak, Entra ID, Cognito, Google) through a single adapter.

[DependsOn(typeof(GranitIdentityNotificationsModule))]
public class AppModule : GranitModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddGranitIdentityRecipientResolver();
}
}

It registers with TryAddScoped, so a custom IRecipientResolver still wins if you later need one. See Channels → ready-made resolver for the field mapping and the Identity:RecipientResolver options.

| Category | Key types | Package | |----------|-----------|---------| | Module | GranitNotificationsModule, GranitNotificationsEntityFrameworkCoreModule, GranitNotificationsWolverineModule | — | | Publisher | INotificationPublisher, NotificationType<TData> | Granit.Notifications | | Channels | INotificationChannel, NotificationChannels, NotificationDeliveryContext | Granit.Notifications | | Definitions | NotificationDefinition, INotificationDefinitionProvider, INotificationDefinitionContext, INotificationDefinitionStore | Granit.Notifications | | Entities | UserNotification, NotificationDeliveryAttempt, NotificationPreference, NotificationSubscription, UserNotificationState | Granit.Notifications | | CQRS | IUserNotificationReader, IUserNotificationWriter, INotificationPreferenceReader, INotificationPreferenceWriter, INotificationSubscriptionReader, INotificationSubscriptionWriter, INotificationDeliveryWriter | Granit.Notifications | | Recipient | IRecipientResolver, RecipientInfo | Granit.Notifications | | Recipient resolver (Identity bridge — default path) | GranitIdentityNotificationsModule, IdentityRecipientResolverOptions, AddGranitIdentityRecipientResolver() | Granit.Identity.Notifications | | Entity tracking | ITrackedEntity, TrackedPropertyConfig, EntityStateChangedData, EntityReference | Granit.Notifications | | Messages | NotificationTrigger, DeliverNotificationCommand | Granit.Notifications | | Handlers | NotificationFanoutHandler, NotificationDeliveryHandler | Granit.Notifications | | Options | NotificationsOptions, EmailChannelOptions, SmtpOptions, BrevoOptions, ScalewayEmailOptions, SendGridEmailOptions, AcsEmailOptions, SmsChannelOptions, AcsSmsOptions, AwsSnsSmsOptions, TwilioOptions, MobilePushChannelOptions, GoogleFcmOptions, AzureNotificationHubsOptions, AwsSnsMobilePushOptions, SignalRChannelOptions, PushChannelOptions, SseChannelOptions, ZulipChannelOptions, ZulipBotOptions | various | | Email | IEmailSender, EmailMessage | Granit.Notifications.Email, Granit.Notifications.Email.AzureCommunicationServices, Granit.Notifications.Email.Scaleway, Granit.Notifications.Email.SendGrid | | SMS | ISmsSender, SmsMessage | Granit.Notifications.Sms, Granit.Notifications.Sms.AwsSns, Granit.Notifications.Twilio | | WhatsApp | IWhatsAppSender, WhatsAppMessage | Granit.Notifications.WhatsApp, Granit.Notifications.Twilio | | Mobile Push | IMobilePushSender, MobilePushMessage, IMobilePushTokenReader, IMobilePushTokenWriter, MobilePushTokenInfo, MobilePlatform | Granit.Notifications.MobilePush, Granit.Notifications.MobilePush.AwsSns | | SignalR | NotificationHub, SignalRNotificationMessage | Granit.Notifications.SignalR | | Web Push | IPushSubscriptionReader, IPushSubscriptionWriter, PushSubscriptionInfo | Granit.Notifications.WebPush | | SSE | ISseConnectionManager, SseConnection, SseNotificationMessage | Granit.Notifications.Sse | | Zulip | IZulipSender, ZulipMessage | Granit.Notifications.Zulip | | Exceptions | NotificationDeliveryException | Granit.Notifications | | Endpoints | NotificationEndpointsOptions, MobilePushTokenEndpoints | Granit.Notifications.Endpoints | | Extensions | AddGranitNotifications(), AddGranitNotificationsEntityFrameworkCore(), AddGranitNotificationsEmail(), AddGranitNotificationsEmailSmtp(), AddGranitNotificationsEmailAcs(), AddGranitNotificationsEmailScaleway(), AddGranitNotificationsEmailSendGrid(), AddGranitNotificationsBrevo(), AddGranitNotificationsSms(), AddGranitNotificationsSmsAcs(), AddGranitNotificationsSmsAwsSns(), AddGranitNotificationsTwilio(), AddGranitNotificationsWhatsApp(), AddGranitNotificationsMobilePush(), AddGranitNotificationsMobilePushGoogleFcm(), AddGranitNotificationsMobilePushAzureNotificationHubs(), AddGranitNotificationsMobilePushAwsSns(), AddGranitNotificationsSignalR(), AddGranitNotificationsPush(), AddGranitNotificationsSse(), AddGranitNotificationsZulip(), AddNotificationDefinitions<T>(), MapGranitNotifications(), MapGranitMobilePushTokens() | various |