RepletoryLib.Communication.Email
1.0.0
dotnet add package RepletoryLib.Communication.Email --version 1.0.0
NuGet\Install-Package RepletoryLib.Communication.Email -Version 1.0.0
<PackageReference Include="RepletoryLib.Communication.Email" Version="1.0.0" />
<PackageVersion Include="RepletoryLib.Communication.Email" Version="1.0.0" />
<PackageReference Include="RepletoryLib.Communication.Email" />
paket add RepletoryLib.Communication.Email --version 1.0.0
#r "nuget: RepletoryLib.Communication.Email, 1.0.0"
#:package RepletoryLib.Communication.Email@1.0.0
#addin nuget:?package=RepletoryLib.Communication.Email&version=1.0.0
#tool nuget:?package=RepletoryLib.Communication.Email&version=1.0.0
RepletoryLib.Communication.Email
AWS SES v2 email service implementation with support for HTML/text emails, attachments, and provider-hosted templates.
Part of the RepletoryLib ecosystem -- standalone, reusable .NET 10 libraries with zero business logic.
Overview
RepletoryLib.Communication.Email provides an AWS Simple Email Service (SES) v2 implementation of the IEmailService interface from RepletoryLib.Communication.Abstractions. It supports simple text/HTML emails, raw MIME messages with file attachments, and templated emails using SES-hosted templates.
The service handles sender address formatting, configuration set integration, reply-to defaults, custom headers, and CC/BCC recipients out of the box. It also automatically registers the unified ICommunicationService facade so messages can be routed across channels.
Key Features
- Simple emails -- HTML and/or plain text body with subject, CC, BCC, reply-to, and custom headers
- File attachments -- Raw MIME message construction for emails with binary attachments
- Templated emails -- SES-hosted template support with template data substitution
- Bulk send -- Send multiple emails with per-message success/failure tracking
- Configuration set -- Optional SES configuration set for delivery tracking and event publishing
- Flexible authentication -- Explicit access/secret keys or default AWS credential chain
- Unified routing -- Automatically registers
ICommunicationServicefor cross-channel messaging
Installation
dotnet add package RepletoryLib.Communication.Email
Or add to your .csproj:
<PackageReference Include="RepletoryLib.Communication.Email" Version="1.0.0" />
Note: RepletoryLib packages are published to a local BaGet feed. See the main repository README for feed configuration.
Dependencies
| Package | Type |
|---|---|
RepletoryLib.Communication.Abstractions |
RepletoryLib |
AWSSDK.SimpleEmailV2 |
NuGet (3.7.402) |
Microsoft.Extensions.Configuration.Abstractions |
NuGet (10.0.0) |
Microsoft.Extensions.Configuration.Binder |
NuGet (10.0.0) |
Microsoft.Extensions.DependencyInjection.Abstractions |
NuGet (10.0.0) |
Microsoft.Extensions.Logging.Abstractions |
NuGet (10.0.0) |
Microsoft.Extensions.Options.ConfigurationExtensions |
NuGet (10.0.0) |
Prerequisites
- AWS account with SES v2 enabled
- Verified sender email address or domain in SES
- AWS credentials (access key/secret key or IAM role)
Quick Start
using RepletoryLib.Communication.Email;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRepletoryEmail(builder.Configuration);
{
"Communication": {
"Email": {
"Region": "af-south-1",
"FromAddress": "noreply@example.com",
"FromName": "My Application",
"AccessKey": "AKIAIOSFODNN7EXAMPLE",
"SecretKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
}
Configuration
SesOptions
| Property | Type | Default | Description |
|---|---|---|---|
Region |
string |
"af-south-1" |
AWS region for the SES service |
AccessKey |
string? |
null |
AWS access key. When null, the default AWS credential chain is used |
SecretKey |
string? |
null |
AWS secret key. When null, the default AWS credential chain is used |
FromAddress |
string |
"" |
Sender email address used in the From header |
FromName |
string? |
null |
Sender display name. When set, From is formatted as "FromName <FromAddress>" |
ConfigurationSetName |
string? |
null |
Optional SES configuration set name for delivery tracking |
ReplyToAddress |
string? |
null |
Default reply-to address when the message does not specify one |
Section name: "Communication:Email"
Usage Examples
Register Services
using RepletoryLib.Communication.Email;
var builder = WebApplication.CreateBuilder(args);
// Register the SES email service
builder.Services.AddRepletoryEmail(builder.Configuration);
var app = builder.Build();
Send a Simple Email
using RepletoryLib.Communication.Abstractions.Interfaces;
using RepletoryLib.Communication.Abstractions.Models.Email;
public class OrderService
{
private readonly IEmailService _emailService;
public OrderService(IEmailService emailService) => _emailService = emailService;
public async Task SendOrderConfirmationAsync(string customerEmail, string orderId, decimal total)
{
var result = await _emailService.SendAsync(new EmailMessage
{
To = [customerEmail],
Subject = $"Order Confirmation - #{orderId}",
HtmlBody = $"""
<h1>Thank you for your order!</h1>
<p>Order ID: <strong>{orderId}</strong></p>
<p>Total: <strong>{total:C}</strong></p>
""",
TextBody = $"Thank you for your order!\nOrder ID: {orderId}\nTotal: {total:C}"
});
if (!result.Success)
throw new InvalidOperationException($"Failed to send confirmation: {result.Error}");
}
}
Send Email with CC, BCC, and Reply-To
var result = await _emailService.SendAsync(new EmailMessage
{
To = ["customer@example.com"],
Cc = ["manager@example.com"],
Bcc = ["audit@example.com"],
Subject = "Invoice #12345",
HtmlBody = "<p>Please find your invoice attached.</p>",
ReplyTo = "billing@example.com",
Headers = new Dictionary<string, string>
{
["X-Custom-Tracking"] = "invoice-12345"
}
});
Send Email with Attachments
var pdfBytes = await File.ReadAllBytesAsync("/path/to/invoice.pdf");
var result = await _emailService.SendAsync(new EmailMessage
{
To = ["customer@example.com"],
Subject = "Your Invoice",
HtmlBody = "<p>Please find your invoice attached.</p>",
Attachments =
[
new EmailAttachment
{
FileName = "invoice-2024-001.pdf",
ContentType = "application/pdf",
Content = pdfBytes
}
]
});
Send Templated Email
using RepletoryLib.Communication.Abstractions.Models.Email;
var result = await _emailService.SendTemplateAsync(new TemplatedEmailMessage
{
To = ["customer@example.com"],
TemplateName = "OrderShipped",
TemplateData = new Dictionary<string, string>
{
["customerName"] = "John Doe",
["orderId"] = "ORD-2024-001",
["trackingUrl"] = "https://tracking.example.com/abc123",
["estimatedDelivery"] = "March 5, 2026"
},
Cc = ["notifications@example.com"]
});
Bulk Send
var emails = customers.Select(c => new EmailMessage
{
To = [c.Email],
Subject = "Monthly Newsletter - March 2026",
HtmlBody = newsletterHtml,
TextBody = newsletterText
});
var bulkResult = await _emailService.SendBulkAsync(emails);
Console.WriteLine($"Sent: {bulkResult.SuccessCount}, Failed: {bulkResult.FailureCount}");
foreach (var failure in bulkResult.Results.Where(r => !r.Success))
{
Console.WriteLine($"Failed: {failure.Error}");
}
Using the Unified Communication Facade
using RepletoryLib.Communication.Abstractions.Enums;
using RepletoryLib.Communication.Abstractions.Interfaces;
using RepletoryLib.Communication.Abstractions.Models.Common;
public class NotificationService
{
private readonly ICommunicationService _communication;
public NotificationService(ICommunicationService communication) => _communication = communication;
public async Task NotifyAsync(string email, string subject, string body)
{
var result = await _communication.SendAsync(new CommunicationMessage
{
Channel = CommunicationChannel.Email,
Recipient = email,
Subject = subject,
Body = body
});
}
}
API Reference
ServiceCollectionExtensions
| Method | Returns | Description |
|---|---|---|
AddRepletoryEmail(configuration) |
IServiceCollection |
Registers IAmazonSimpleEmailServiceV2 (singleton), IEmailService (scoped), and ICommunicationService (scoped) |
SesEmailService (implements IEmailService)
| Method | Returns | Description |
|---|---|---|
SendAsync(EmailMessage, ct) |
Task<SendResult> |
Sends a single email (simple or raw MIME with attachments) |
SendBulkAsync(IEnumerable<EmailMessage>, ct) |
Task<BulkSendResult> |
Sends multiple emails with per-message results |
SendTemplateAsync(TemplatedEmailMessage, ct) |
Task<SendResult> |
Sends an email using an SES-hosted template |
SesOptions
| Property | Type | Default | Description |
|---|---|---|---|
Region |
string |
"af-south-1" |
AWS region endpoint |
AccessKey |
string? |
null |
AWS access key (optional) |
SecretKey |
string? |
null |
AWS secret key (optional) |
FromAddress |
string |
"" |
Sender email address |
FromName |
string? |
null |
Sender display name |
ConfigurationSetName |
string? |
null |
SES configuration set name |
ReplyToAddress |
string? |
null |
Default reply-to address |
Integration with Other RepletoryLib Packages
| Package | Relationship |
|---|---|
RepletoryLib.Communication.Abstractions |
Direct dependency -- provides IEmailService, EmailMessage, SendResult |
RepletoryLib.Communication.Sms |
Combine with SMS for multi-channel notifications |
RepletoryLib.Communication.WhatsApp |
Combine with WhatsApp for multi-channel notifications |
RepletoryLib.Communication.Push |
Combine with push for multi-channel notifications |
RepletoryLib.Common |
Transitive dependency via Abstractions |
Testing
using Moq;
using Amazon.SimpleEmailV2;
using Amazon.SimpleEmailV2.Model;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using RepletoryLib.Communication.Abstractions.Enums;
using RepletoryLib.Communication.Abstractions.Models.Email;
using RepletoryLib.Communication.Email.Options;
using RepletoryLib.Communication.Email.Services;
[Fact]
public async Task SendAsync_returns_success_with_message_id()
{
var mockSes = new Mock<IAmazonSimpleEmailServiceV2>();
mockSes
.Setup(s => s.SendEmailAsync(It.IsAny<SendEmailRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SendEmailResponse { MessageId = "ses-msg-001" });
var options = Options.Create(new SesOptions
{
Region = "af-south-1",
FromAddress = "noreply@example.com",
FromName = "Test App"
});
var service = new SesEmailService(mockSes.Object, options, NullLogger<SesEmailService>.Instance);
var result = await service.SendAsync(new EmailMessage
{
To = ["user@example.com"],
Subject = "Test",
HtmlBody = "<p>Hello</p>"
});
result.Success.Should().BeTrue();
result.MessageId.Should().Be("ses-msg-001");
result.Channel.Should().Be(CommunicationChannel.Email);
}
Troubleshooting
| Issue | Solution |
|---|---|
AmazonSimpleEmailServiceV2Exception: Email address is not verified |
Verify the sender email address or domain in the AWS SES console |
AmazonSimpleEmailServiceV2Exception: Access Denied |
Verify your AWS access key/secret key, or ensure the IAM role has ses:SendEmail permissions |
| Emails not arriving | Check the SES sending dashboard for bounces/complaints. Ensure you are out of the SES sandbox for production sends |
FromAddress is empty |
Set Communication:Email:FromAddress in appsettings.json |
| Attachments not sending | Ensure EmailAttachment.Content is a non-empty byte array and ContentType is a valid MIME type |
| Template data not applied | Ensure TemplateData keys match the placeholders in the SES template exactly |
License
This project is licensed under the MIT License.
Copyright (c) 2024-2026 Repletory.
For complete documentation, infrastructure setup, and configuration reference, see the RepletoryLib main repository.
| 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
- AWSSDK.SimpleEmailV2 (>= 3.7.402)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.0)
- RepletoryLib.Communication.Abstractions (>= 1.0.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.0.0 | 72 | 3/2/2026 |