Skip to content

Notification Channels — Email, SMS, Push, SignalR, SSE & Custom

Every delivery channel implements INotificationChannel. The engine resolves all registered channels and routes delivery commands by name. See email-specific details and SMS, push, and real-time channels for provider configuration.

Every delivery channel implements this interface. The engine resolves all registered INotificationChannel services and routes by Name:

public interface INotificationChannel
{
string Name { get; }
Task SendAsync(NotificationDeliveryContext context,
CancellationToken cancellationToken = default);
}

Channels not registered are silently skipped with a warning log (graceful degradation pattern).

public static class NotificationChannels
{
public const string InApp = "InApp";
public const string SignalR = "SignalR";
public const string Email = "Email";
public const string Sms = "Sms";
public const string WhatsApp = "WhatsApp";
public const string Push = "Push"; // W3C Web Push
public const string MobilePush = "MobilePush";
public const string Sse = "Sse";
public const string Zulip = "Zulip";
}
ChannelRegistrationProvider resolution
InAppBuilt-in (auto-registered)N/A
EmailAddGranitNotificationsEmail()Keyed Service: "Smtp", "Brevo", "AzureCommunicationServices", "Scaleway", "SendGrid"
SMSAddGranitNotificationsSms()Keyed Service: "Brevo", "AzureCommunicationServices", "AwsSns", "Twilio"
WhatsAppAddGranitNotificationsWhatsApp()Keyed Service: "Brevo", "Twilio"
Mobile PushAddGranitNotificationsMobilePush()Keyed Service: "GoogleFcm", "AzureNotificationHubs", "AwsSns"
SignalRAddGranitNotificationsSignalR()Direct (NotificationHub)
Web PushAddGranitNotificationsPush()VAPID (Lib.Net.Http.WebPush)
SSEAddGranitNotificationsSse()Native .NET 10 SSE
ZulipAddGranitNotificationsZulip()Zulip Bot API
public class TeamsNotificationChannel(IRecipientResolver resolver)
: INotificationChannel
{
public string Name => "Teams";
public async Task SendAsync(
NotificationDeliveryContext context,
CancellationToken cancellationToken = default)
{
RecipientInfo? recipient = await resolver
.ResolveAsync(context.RecipientUserId, cancellationToken)
.ConfigureAwait(false);
if (recipient is null) return;
// Send via Microsoft Graph API...
}
}
// Register
services.AddSingleton<INotificationChannel, TeamsNotificationChannel>();

The application must implement this interface to resolve contact information from user identifiers. It is not provided by Granit — each application knows its own user model.

public interface IRecipientResolver
{
Task<RecipientInfo?> ResolveAsync(
string userId, CancellationToken cancellationToken = default);
}
public sealed record RecipientInfo
{
public required string UserId { get; init; }
public string? Email { get; init; }
public string? PhoneNumber { get; init; } // E.164 format
public string? PreferredCulture { get; init; } // BCP 47
public string? DisplayName { get; init; }
}