Acontplus.Notifications 1.6.0

dotnet add package Acontplus.Notifications --version 1.6.0
                    
NuGet\Install-Package Acontplus.Notifications -Version 1.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Acontplus.Notifications" Version="1.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Acontplus.Notifications" Version="1.6.0" />
                    
Directory.Packages.props
<PackageReference Include="Acontplus.Notifications" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Acontplus.Notifications --version 1.6.0
                    
#r "nuget: Acontplus.Notifications, 1.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Acontplus.Notifications@1.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Acontplus.Notifications&version=1.6.0
                    
Install as a Cake Addin
#tool nuget:?package=Acontplus.Notifications&version=1.6.0
                    
Install as a Cake Tool

Acontplus.Notifications

NuGet .NET License: MIT

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 IMemoryCache injection

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 / PhoneNumberId on every request (e.g. credentials come from the frontend), you can omit PhoneNumberId and AccessToken entirely from appsettings and only keep TimeoutSeconds.

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

  1. Named account โ€” WhatsAppCredentials.FromAccount("name") โ†’ looks up WhatsApp:Accounts:name
  2. Inline override โ€” WhatsAppCredentials.Inline(phoneId, token)
  3. 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 only TimeoutSeconds is 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed

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.