Acontplus.Notifications
1.6.0
dotnet add package Acontplus.Notifications --version 1.6.0
NuGet\Install-Package Acontplus.Notifications -Version 1.6.0
<PackageReference Include="Acontplus.Notifications" Version="1.6.0" />
<PackageVersion Include="Acontplus.Notifications" Version="1.6.0" />
<PackageReference Include="Acontplus.Notifications" />
paket add Acontplus.Notifications --version 1.6.0
#r "nuget: Acontplus.Notifications, 1.6.0"
#:package Acontplus.Notifications@1.6.0
#addin nuget:?package=Acontplus.Notifications&version=1.6.0
#tool nuget:?package=Acontplus.Notifications&version=1.6.0
Acontplus.Notifications
Production-ready multi-channel notification library for .NET 10. Covers email (MailKit SMTP + Amazon SES) and WhatsApp Cloud API (Meta Graph API v23.0) in a single package โ templating, media, interactive messages, multi-tenant credentials, resilience, and webhook validation included.
๐ Features
v1.6.0 โ WhatsApp Cloud API (Meta Graph API v23.0)
- ๐ฌ Text messages โ plain text with URL preview, in-thread replies
- ๐ Templates โ body/header params, image/video/document headers, quick-reply & URL buttons
- ๐ผ๏ธ Media โ image, document, audio, video, sticker (URL or pre-uploaded ID)
- ๐ Location โ map pin with name and address
- ๐ Interactive โ quick-reply buttons, scrollable list menu, CTA URL button
- ๐ Reactions โ send/remove emoji reactions on received messages
- โ Read receipts โ mark messages as read (double blue ticks)
- ๐ค Media upload โ upload files once, reuse the ID in multiple messages
- ๐ Webhook validation โ HMAC-SHA256 signature verification (X-Hub-Signature-256)
- ๐ข Multi-tenant โ default account + named accounts per company + per-request inline override
- ๐ Built-in resilience โ 3 retries, exponential back-off, circuit breaker, per-attempt timeout
v1.5.0 โ Email Performance & Caching
- โก Template Caching โ 50ร faster template loading (30-min memory cache, sliding expiration)
- ๐ 99% less I/O โ cached templates eliminate repeated disk reads
- ๐ Backward compatible โ optional
IMemoryCacheinjection
Core Email Capabilities
- ๐ง Email via MailKit (SMTP) and Amazon SES (HTTP v2)
- ๐จ Scriban templating with logo embedding and variable interpolation
- ๐ Retry logic with Polly (exponential back-off, jitter)
- ๐ฆ Rate limiting (SES: 14 emails/sec default; SMTP: auth throttling)
- ๐ SMTP connection pooling (configurable pool size)
- ๐ฆ Bulk sending with batching (SES: 50 recipients/batch)
- ๐งต Thread-safe concurrent operations
- ๐จ Attachments (PDF, images, documents)
๐ฆ Installation
dotnet add package Acontplus.Notifications
<PackageReference Include="Acontplus.Notifications" Version="1.6.0" />
โก WhatsApp Cloud API (v1.6.0)
1. Register the service
// Program.cs
builder.Services.AddWhatsAppService(builder.Configuration);
// or inline:
builder.Services.AddWhatsAppService(opts =>
{
opts.PhoneNumberId = "1234567890";
opts.AccessToken = "EAAxxxx...";
opts.ApiVersion = "v23.0";
opts.DefaultCountryCode = "593"; // 09xxxxxxxx โ 593 9xxxxxxxx
});
2. appsettings.json
{
"WhatsApp": {
// REQUIRED (when using server-stored credentials)
"PhoneNumberId": "YOUR_PHONE_NUMBER_ID",
"AccessToken": "YOUR_PERMANENT_SYSTEM_USER_TOKEN",
// OPTIONAL โ defaults shown
"ApiVersion": "v23.0",
"TimeoutSeconds": 30,
// OPTIONAL โ auto-prefix numbers starting with "0" (e.g. 09xxxxxxxx โ 5939xxxxxxxx)
"DefaultCountryCode": "593",
// OPTIONAL โ only needed if you call ValidateWebhookSignature()
"AppSecret": "YOUR_APP_SECRET_FOR_WEBHOOK_VALIDATION",
// OPTIONAL โ only needed if you handle Meta's GET webhook verify handshake
"WebhookVerifyToken": "YOUR_WEBHOOK_VERIFY_TOKEN",
// OPTIONAL โ multi-tenant: named accounts override the default credentials above
"Accounts": {
"company-a": {
"PhoneNumberId": "COMPANY_A_PHONE_ID",
"AccessToken": "COMPANY_A_TOKEN"
}
}
}
}
Per-request credentials: If your callers supply
AccessToken/PhoneNumberIdon every request (e.g. credentials come from the frontend), you can omitPhoneNumberIdandAccessTokenentirely from appsettings and only keepTimeoutSeconds.Obtain credentials: Meta Business Manager โ WhatsApp โ API Setup. Use a permanent system-user token (never expires) instead of a temporary one.
3. Inject and send
public class NotificationService(IWhatsAppService whatsApp)
{
// Text message (requires 24-hour conversation window)
public Task<WhatsAppResult> SendTextAsync(string to, string body) =>
whatsApp.SendTextAsync(new() { To = to, Body = body });
// Template (works any time โ no 24-hour window needed)
public Task<WhatsAppResult> SendInvoiceAsync(string to, string mediaId, string customerName, string invoiceNumber) =>
whatsApp.SendTemplateAsync(new()
{
To = to,
TemplateName = "notificacion_documento",
LanguageCode = "es_EC",
Components =
[
WhatsAppTemplateComponent.HeaderDocument(mediaId, "factura.pdf", isId: true),
WhatsAppTemplateComponent.Body(customerName, invoiceNumber)
]
});
// Upload a file once โ reuse the ID
public async Task<string?> UploadInvoiceAsync(Stream pdfStream) =>
await whatsApp.UploadMediaAsync(new()
{
FileStream = pdfStream,
FileName = "invoice.pdf",
ContentType = "application/pdf"
});
// Interactive quick-reply buttons
public Task<WhatsAppResult> SendConfirmationAsync(string to) =>
whatsApp.SendInteractiveAsync(new()
{
To = to,
InteractiveType = WhatsAppInteractiveType.Button,
BodyText = "Do you confirm your appointment?",
ReplyButtons =
[
new WhatsAppReplyButton { Id = "yes", Title = "Yes, confirm" },
new WhatsAppReplyButton { Id = "no", Title = "No, cancel" }
]
});
// Multi-tenant: named account
public Task<WhatsAppResult> SendForCompanyAsync(string to, string message) =>
whatsApp.SendTextAsync(new()
{
To = to,
Body = message,
Credentials = WhatsAppCredentials.FromAccount("company-a")
});
}
4. Webhook validation
app.MapPost("/webhook/whatsapp", async (
HttpRequest req,
IWhatsAppService whatsApp) =>
{
var signature = req.Headers["X-Hub-Signature-256"].ToString();
var body = await new StreamReader(req.Body).ReadToEndAsync();
if (!whatsApp.ValidateWebhookSignature(signature, body))
return Results.Unauthorized();
// process payload ...
return Results.Ok();
});
// Webhook verification handshake (GET)
app.MapGet("/webhook/whatsapp", (
[FromQuery(Name = "hub.mode")] string mode,
[FromQuery(Name = "hub.verify_token")] string token,
[FromQuery(Name = "hub.challenge")] string challenge,
IConfiguration config) =>
{
var expected = config["WhatsApp:WebhookVerifyToken"];
return mode == "subscribe" && token == expected
? Results.Ok(int.Parse(challenge))
: Results.Unauthorized();
});
Supported message types
| Method | Type | 24-h window? |
|---|---|---|
SendTextAsync |
Plain text with optional preview | Required |
SendTemplateAsync |
Pre-approved template (text + media header, buttons) | Not required |
SendMediaAsync |
Image / Document / Audio / Video / Sticker | Required |
SendLocationAsync |
Map pin | Required |
SendInteractiveAsync |
Quick-reply buttons / List menu / CTA URL | Required |
SendReactionAsync |
Emoji reaction on a received message | Required |
MarkAsReadAsync |
Read receipt (double blue ticks) | โ |
UploadMediaAsync |
Upload file โ get reusable media ID | โ |
ValidateWebhookSignature |
HMAC-SHA256 webhook verification | โ |
Multi-tenant credential resolution order
- Named account โ
WhatsAppCredentials.FromAccount("name")โ looks upWhatsApp:Accounts:name - Inline override โ
WhatsAppCredentials.Inline(phoneId, token) - Default โ top-level
WhatsApp:PhoneNumberId+WhatsApp:AccessToken
๐ง Email (MailKit / Amazon SES)
Quick Start
// Program.cs
builder.Services.AddMemoryCache(); // enables template caching
builder.Services.AddSingleton<IMailKitService, AmazonSesService>(); // or MailKitService
appsettings.json
{
"AWS": {
"SES": {
"Region": "us-east-1",
"DefaultFromEmail": "noreply@example.com",
"MaxSendRate": 14,
"BatchSize": 50,
"BatchDelayMs": 100,
"AccessKey": "AKIA...",
"SecretKey": "your-secret-key"
}
},
"Templates": { "Path": "Templates" },
"Media": { "ImagesPath": "wwwroot/images" }
}
Send an email
var success = await emailService.SendAsync(new EmailModel
{
SenderEmail = "noreply@example.com",
RecipientEmail = "user@example.com",
Subject = "Welcome!",
Template = "welcome.html", // cached 30 min
Body = JsonSerializer.Serialize(new { UserName = "John" }),
IsHtml = false
});
Bulk sending
var emails = recipients.Select(r => new EmailModel { ... }).ToList();
await emailService.SendBulkAsync(emails, ct); // auto-batched, rate-limited
โ๏ธ Configuration Reference
WhatsApp settings
| Key | Default | Required? | Description |
|---|---|---|---|
PhoneNumberId |
โ | Conditional ยน | WhatsApp Business phone number ID |
AccessToken |
โ | Conditional ยน | Meta Graph API token (system-user recommended) |
ApiVersion |
v23.0 |
Optional | Meta Graph API version |
TimeoutSeconds |
30 |
Optional | Per-attempt HTTP timeout |
DefaultCountryCode |
null |
Optional | Auto-prefix numbers starting with 0 (e.g. "593") |
AppSecret |
null |
Optional ยฒ | App secret for webhook HMAC-SHA256 signature validation |
WebhookVerifyToken |
null |
Optional ยณ | Token for Meta's GET webhook verify handshake |
Accounts |
{} |
Optional | Named accounts for multi-tenant (key โ PhoneNumberId + AccessToken) |
ยน Conditional โ required when using server-stored default credentials. Omit when all callers supply credentials per-request via
WhatsAppCredentials(e.g. credentials come from the frontend). In that case onlyTimeoutSecondsis meaningful.ยฒ Only needed if you call
ValidateWebhookSignature().ยณ Only needed if you handle Meta's GET webhook verify handshake endpoint.
Amazon SES settings
| Key | Default | Description |
|---|---|---|
Region |
us-east-1 |
AWS SES region |
DefaultFromEmail |
โ | Default sender |
MaxSendRate |
14 |
Max emails/second |
BatchSize |
50 |
Recipients per bulk batch |
BatchDelayMs |
100 |
Delay between batches |
MailKit SMTP settings
| Key | Default | Description |
|---|---|---|
MaxPoolSize |
3 |
SMTP connection pool size |
MinAuthIntervalSeconds |
30 |
Min time between auth attempts |
MaxAuthAttemptsPerHour |
10 |
Auth rate limit |
๐ Performance
WhatsApp resilience (v1.6.0)
| Layer | Configuration |
|---|---|
| Retries | 3 attempts, 600 ms base delay (exponential) |
| Per-attempt timeout | 30 s |
| Total timeout | 90 s (all retries included) |
| Circuit breaker | Included via AddStandardResilienceHandler |
Email template caching (v1.5.0+)
| Metric | Without cache | With cache | Improvement |
|---|---|---|---|
| Template load time | 10โ50 ms | <1 ms | 50ร faster |
| Disk I/O (high volume) | Every request | Once per 30 min | 99% reduction |
๐ API Reference
IWhatsAppService
Task<WhatsAppResult> SendTextAsync(SendWhatsAppTextRequest, CancellationToken);
Task<WhatsAppResult> SendTemplateAsync(SendWhatsAppTemplateRequest, CancellationToken);
Task<WhatsAppResult> SendMediaAsync(SendWhatsAppMediaRequest, CancellationToken);
Task<WhatsAppResult> SendLocationAsync(SendWhatsAppLocationRequest, CancellationToken);
Task<WhatsAppResult> SendInteractiveAsync(SendWhatsAppInteractiveRequest, CancellationToken);
Task<WhatsAppResult> SendReactionAsync(SendWhatsAppReactionRequest, CancellationToken);
Task<bool> MarkAsReadAsync(string messageId, WhatsAppCredentials?, CancellationToken);
Task<string?> UploadMediaAsync(WhatsAppMediaUpload, WhatsAppCredentials?, CancellationToken);
bool ValidateWebhookSignature(string xHubSignature256, string rawBody, string? appSecret);
IMailKitService
Task<bool> SendAsync(EmailModel email, CancellationToken ct);
Task<bool> SendBulkAsync(IEnumerable<EmailModel> emails, CancellationToken ct);
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Acontplus.Core (>= 2.2.0)
- Acontplus.Utilities (>= 2.0.7)
- AWSSDK.Core (>= 4.0.3.29)
- AWSSDK.SimpleEmailV2 (>= 4.0.12.10)
- BCrypt.Net-Next (>= 4.1.0)
- MailKit (>= 4.15.1)
- Microsoft.Extensions.Caching.Memory (>= 10.0.5)
- Microsoft.Extensions.Configuration (>= 10.0.5)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Http.Resilience (>= 10.4.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
- Polly (>= 8.6.6)
- Scriban (>= 7.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.6.0 | 75 | 4/13/2026 |
| 1.5.12 | 80 | 4/13/2026 |
| 1.5.11 | 111 | 3/30/2026 |
| 1.5.10 | 105 | 3/26/2026 |
| 1.5.9 | 95 | 3/22/2026 |
| 1.5.8 | 143 | 3/17/2026 |
| 1.5.7 | 101 | 3/8/2026 |
| 1.5.6 | 101 | 3/1/2026 |
| 1.5.5 | 98 | 2/22/2026 |
| 1.5.4 | 123 | 1/16/2026 |
| 1.5.3 | 116 | 1/11/2026 |
| 1.5.2 | 213 | 12/25/2025 |
| 1.5.1 | 193 | 12/23/2025 |
| 1.5.0 | 283 | 12/16/2025 |
| 1.4.7 | 157 | 12/11/2025 |
| 1.4.6 | 192 | 12/5/2025 |
| 1.4.5 | 215 | 12/4/2025 |
| 1.4.4 | 220 | 12/3/2025 |
| 1.4.2 | 204 | 11/27/2025 |
| 1.4.1 | 209 | 11/26/2025 |
v1.6.0: WhatsApp Cloud API (Meta Graph API v23.0) โ text, templates (full
component support), media (image/document/audio/video/sticker), location, interactive messages
(buttons/list/CTA), reactions, read receipts, media upload, HMAC-SHA256 webhook validation,
and multi-tenant named accounts.
v1.5.0: Added template caching with IMemoryCache (30min sliding expiration)
for improved performance on high-frequency templates. Reduces I/O operations and speeds up
email rendering. Backward compatible - optional IMemoryCache injection in AmazonSesService
constructor.