Appearance
Channels
GrydNotifications supports three notification channels out of the box: Email, Push, and In-App. Each channel has its own provider abstraction, allowing you to swap implementations without changing application code.
Channel Architecture
SendNotificationCommand
│
┌───────┴───────┐
│ Channel │
│ Resolver │
└───┬───┬───┬───┘
│ │ │
┌─────────┘ │ └─────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Email │ │ Push │ │ In-App │
│ Provider │ │ Provider │ │ Sender │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
┌────┴────┐ ┌───┴────┐ ┌────┴────┐
│ MailKit │ │ FCM │ │ SignalR │
│ SendGrid│ │ │ │ + DB │
└─────────┘ └────────┘ └──────────┘Email Channel
The email channel supports SMTP via MailKit (default) and SendGrid as an alternative provider.
MailKit (SMTP) — Default
MailKit is the default email provider, recommended by Microsoft over System.Net.Mail. It supports OAuth2, connection pooling, DKIM, and S/MIME.
csharp
options.Email.SmtpHost = "smtp.myapp.com";
options.Email.SmtpPort = 587;
options.Email.SmtpUseSsl = true;
options.Email.SmtpUsername = "user";
options.Email.SmtpPassword = "password";
options.Email.DefaultFrom = "noreply@myapp.com";
options.Email.DefaultFromName = "My App";Sending Emails
csharp
// Via unified pipeline
await _mediator.Send(new SendNotificationCommand
{
Channel = NotificationChannel.Email,
Recipients = [
new RecipientDto("john@example.com", "John Doe"),
new RecipientDto("jane@example.com", "Jane Doe")
],
Subject = "Weekly Report",
HtmlBody = "<h1>Your weekly report is ready</h1>",
TextBody = "Your weekly report is ready"
}, ct);
// Via direct email shortcut
await _mediator.Send(new SendEmailCommand
{
To = ["john@example.com"],
Cc = ["manager@example.com"],
Bcc = ["audit@myapp.com"],
Subject = "Quarterly Summary",
HtmlBody = "<h1>Q4 2025 Summary</h1>",
FromAddress = "reports@myapp.com",
FromName = "Reports System"
}, ct);Email with Attachments
csharp
await _mediator.Send(new SendEmailCommand
{
To = ["user@example.com"],
Subject = "Invoice Attached",
HtmlBody = "<p>Please find your invoice attached.</p>",
Attachments = [
new AttachmentDto
{
FileName = "invoice.pdf",
ContentType = "application/pdf",
Content = pdfBytes
}
]
}, ct);Email Limits
| Limit | Default | Configurable |
|---|---|---|
| Max attachment size | 10 MB | ✅ MaxAttachmentSizeMb |
| Max attachments/email | 5 | ✅ MaxAttachmentsPerEmail |
| Max recipients (To+Cc+Bcc) | 50 | ✅ MaxRecipientsPerEmail |
| Bulk batch size | 100 | ✅ BulkBatchSize |
SendGrid Provider
For high-volume sending, use SendGrid's HTTP API:
csharp
// SendGrid is available as an additional provider
// Configure SMTP credentials for SendGrid
options.Email.SmtpHost = "smtp.sendgrid.net";
options.Email.SmtpUsername = "apikey";
options.Email.SmtpPassword = "SG.your_api_key_here";Push Channel (FCM)
The push channel sends notifications via Firebase Cloud Messaging to mobile devices and web browsers.
Setup
- Create a Firebase project at console.firebase.google.com
- Download the service account JSON
- Set the environment variable:
bash
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/firebase-adminsdk.json"- Enable push in options:
csharp
options.Push.Enabled = true;
options.Push.DefaultTimeToLive = TimeSpan.FromHours(24);
options.Push.AutoRemoveInvalidTokens = true;Sending Push Notifications
csharp
// Direct push to device tokens
await _mediator.Send(new SendPushNotificationCommand
{
Title = "New Message",
Body = "You have a new message from John",
DeviceTokens = ["device_token_1", "device_token_2"],
Data = new Dictionary<string, string>
{
["messageId"] = "abc123",
["type"] = "new_message"
}
}, ct);
// Via unified pipeline
await _mediator.Send(new SendNotificationCommand
{
Channel = NotificationChannel.Push,
Recipients = [new RecipientDto { Address = "device_token_abc" }],
Subject = "New Message",
TextBody = "You have a new message from John"
}, ct);Device Token Management
GrydNotifications tracks device tokens per user with automatic lifecycle management:
| Feature | Description |
|---|---|
| Registration | Store tokens when users sign in on devices |
| Deactivation | Auto-deactivate tokens reported invalid by FCM |
| Stale cleanup | Remove tokens unused for StaleTokenThreshold (default 90 days) |
| Multicast limit | Auto-splits sends into batches of MaxDeviceTokensPerMulticast (500) |
FCM Payload Customization
csharp
await _mediator.Send(new SendPushNotificationCommand
{
Title = "Flash Sale!",
Body = "50% off all items — ends in 2 hours",
DeviceTokens = tokens,
ImageUrl = "https://cdn.myapp.com/sale-banner.jpg",
Data = new Dictionary<string, string>
{
["screen"] = "promotions",
["promo_id"] = "FLASH50"
}
}, ct);In-App Channel
The in-app channel creates notifications for the user's bell/drawer in your application. Notifications are persisted in the database and delivered in real-time via SignalR.
For a detailed guide on the In-App channel, see In-App & SignalR.
Quick Overview
csharp
// Send an in-app notification
await _mediator.Send(new SendInAppNotificationCommand
{
UserId = userId,
Title = "Task Assigned",
Body = "You've been assigned to task #123",
Icon = "📋",
Category = "tasks",
ActionUrl = "/tasks/123",
Priority = NotificationPriority.High
}, ct);Features
| Feature | Description |
|---|---|
| Read/Unread tracking | Mark individual or all as read |
| Categories | Filter by category (alerts, tasks, comments, etc.) |
| Expiration | Auto-expire with ExpiresAfter |
| Collapse keys | Group related notifications |
| Real-time (SignalR) | Instant delivery via WebSocket |
| Badge count | Unread count endpoint for UI badge |
Multi-Channel Sending
Send the same notification to multiple channels:
csharp
// Email + In-App
var notification = new SendNotificationCommand
{
Channel = NotificationChannel.Email,
Recipients = [new RecipientDto("user@example.com")],
TemplateSlug = "order-confirmation",
TemplateData = templateData
};
await _mediator.Send(notification, ct);
// Also create an in-app notification
await _mediator.Send(new SendInAppNotificationCommand
{
UserId = userId,
Title = "Order Confirmed",
Body = $"Your order #{orderId} is confirmed",
Category = "orders",
ActionUrl = $"/orders/{orderId}"
}, ct);Provider Abstraction
Each channel is defined by an abstraction in GrydNotifications.Core:
| Abstraction | Description |
|---|---|
IEmailSender | Email delivery (MailKit, SendGrid) |
IPushNotificationSender | Push delivery (FCM) |
IInAppNotificationSender | In-app create + SignalR broadcast |
INotificationTemplateRenderer | Template compilation + rendering (Scriban) |
INotificationQueue | Async processing queue |
To swap a provider, register your implementation:
csharp
// Replace the default email sender
services.AddSingleton<IEmailSender, MyCustomEmailSender>();