Pixata.Email
3.1.0
dotnet add package Pixata.Email --version 3.1.0
NuGet\Install-Package Pixata.Email -Version 3.1.0
<PackageReference Include="Pixata.Email" Version="3.1.0" />
<PackageVersion Include="Pixata.Email" Version="3.1.0" />
<PackageReference Include="Pixata.Email" />
paket add Pixata.Email --version 3.1.0
#r "nuget: Pixata.Email, 3.1.0"
#:package Pixata.Email@3.1.0
#addin nuget:?package=Pixata.Email&version=3.1.0
#tool nuget:?package=Pixata.Email&version=3.1.0
Pixata.Email 

An email service for use in .NET Core projects. Backed by Mailkit, it eases the effort needed to add email facilities to your project.
A Nuget package is available for this project.
Version 3.1.0 adds
SendEmailsAsync, which sends several emails over one connection to the SMTP server, which is much quicker than sending them one by one. See sending several emails at once below.
Version 3.0.1 references a patched version of
System.Security.Cryptography.Pkcs, as the one pulled in transitively had known vulnerabilities (GHSA-846f-f764-76mx, GHSA-2fmv-rv2c-59x7). No code changes.
Breaking changes
Version 3.0.0 changes how the connection to the SMTP server is secured.
SmtpSettingshas a newSocketOptionsproperty (a MailKitSecureSocketOptions), andUseSslis now abool?marked[Obsolete]. If you set neither, the service uses implicit TLS on port 465 and required STARTTLS on any other port, where it used to try implicit TLS everywhere (which failed on port 587). See securing the connection below.
As from version 2.0.0, this package does not use LanguageExt, but returns an
ApiResponsefrom the Pixata.Extensions package to indicate success or failure. See the comments at the bottom of this document for how this will change your code.
Setup
First thing you need to do is add your SMTP server and "From" details to your application. There are a few ways to do this, the most simple of which is to use appSettings.json...
"Smtp": {
"Server": "your.smtp.server",
"Port": 999,
"SocketOptions": "StartTls", // Optional, see below
"UserName": "your.username",
"Password": "your.password",
"FromEmail": "jim@spriggs.com",
"FromName": "Jim Spriggs",
"ReplyTo": "jim@spriggs.com" // Optional
}
Unless your name is Jim Spriggs, you'll need to change these to you own name and server settings!
Securing the connection
SocketOptions takes any member of MailKit's SecureSocketOptions enum (None, Auto, SslOnConnect, StartTls or StartTlsWhenAvailable). If you leave it out, the service picks one from the port...
| Port | Default |
|---|---|
| 465 | SslOnConnect (implicit TLS) |
| Anything else (eg 587, the standard submission port used by Microsoft 365, Gmail and most other providers) | StartTls (required, so a man in the middle can't strip the encryption and see your credentials) |
If you need an unencrypted connection (a local test server such as Papercut or smtp4dev, for example), set "SocketOptions": "None" explicitly.
Prior to version 3.0.0, the connection was controlled by a UseSsl flag, which MailKit maps to SslOnConnect (true) or StartTlsWhenAvailable (false). That meant true failed with a handshake error on port 587, and false only used STARTTLS if the server offered it. UseSsl still works, but is marked [Obsolete], and is ignored if you set SocketOptions. If you read UseSsl in your own code, note that it is now a bool?, and is null unless you set it.
Then add the following lines...
// .NET5 - Goes in Startup.cs
SmtpSettings smtpSettings = Configuration.GetSection("Smtp").Get<SmtpSettings>();
services.AddSingleton(smtpSettings);
services.AddTransient<PixataEmailService>();
// .NET6+ - Goes in Program.cs
SmtpSettings smtpSettings = builder.Configuration.GetSection("Smtp").Get<SmtpSettings>();
builder.Services.AddSingleton(smtpSettings);
builder.Services.AddTransient<PixataEmailService>();
This allows you to inject an instance of PixataEmailService into your code as usual, and it will have the settings baked in for you.
If you store your mail settings in secrets, environmental variables, etc, then you'll need to retrieve them from there and set up an instance of SmtpSettings from that.
If you are into interfaces, you can register that instead...
services.AddTransient<PixataEmailServiceInterface, PixataEmailService>();
Allowing multiple email servers or accounts
Note that this feature is only available from v1.2.4 onwards
If you need to be able to send emails from multiple servers or accounts, you can set up a default SmtpSettings as above, and then when you want to send from a different server or account, just change the SmtpSettings property on the service to use the appropriate settings...
SmtpSettings myStmpSettings = // Pick them up from wherever you store them
_emailService.SmtpSettings = myStmpSettings;
Usage
There are three overloads of the SendEmailAsync method, plus SendEmailsAsync for sending several emails at once. Easiest to use is a simple one that just takes the recipient's email address, the subject and the HTML body...
(await _emailService.SendEmailAsync("billy@shears.com", "Hello from Jim Spriggs", htmlBody)))
.Match(_ => /* code on success */, ex => /* code on failure */);
If you want more control over what is sent and how, the second overload takes an EmailParameters object. The various constructors allow you to specify more detail, as well as adding multiple recipients. You can also add attachments, which are tuples of the form (string FileName, string MimeType, byte[] Data).
To copy the email to other people, add them to Cc or Bcc (both List<MailboxAddress>, empty by default), or use the AddCc and AddBcc methods, which parse the addresses the same way as the constructors do...
EmailParameters parameters = new EmailParameters("Your invoice", htmlBody, "billy@shears.com", "Billy Shears")
.AddCc("accounts@shears.com")
.AddBcc("jim@spriggs.com"); // Send me a copy
BCC recipients get the email, but don't appear in the headers that the other recipients see. CC and BCC were added in version 3.0.0.
See the EmailParameters code for more details.
The third overload takes an EmailParameters and a MailKit SecureSocketOptions, which overrides the one from your settings for this email only...
await _emailService.SendEmailAsync(emailParameters, SecureSocketOptions.StartTls);
The other two overloads connect using the socket options from your settings (see securing the connection), so you rarely need this one.
All three return an ApiResponse<Yunit>, so the failure message is the exception's message. The two overloads that use your settings prefix it with the exception type (eg "(AuthenticationException) Authentication failed"), as that is usually the useful part when a send fails.
Sending several emails at once
Note that this feature is only available from v3.1.0 onwards
Each SendEmailAsync overload opens a new connection to the SMTP server, secures it, logs in, sends the email and disconnects. That's fine for one email, but if you're sending a batch (statements to a list of customers, for example), the connecting and logging in can take longer than the sending. SendEmailsAsync takes a collection of EmailParameters, and sends them all over one connection...
List<EmailParameters> emails = customers
.Select(c => new EmailParameters("Your statement", StatementBody(c), c.Email, c.Name))
.ToList();
List<ApiResponse<Yunit>> responses = await _emailService.SendEmailsAsync(emails);
You get back one ApiResponse<Yunit> per email, in the same order as the emails you passed in, so you can match them up to see which ones failed...
foreach ((EmailParameters email, ApiResponse<Yunit> response) in emails.Zip(responses)) {
response.Match(_ => { }, error => Console.WriteLine($"Couldn't send to {email.Recipients.First()}: {error}"));
}
A few things to note...
- An email that fails doesn't stop the rest being sent. If the server dropped the connection when it failed, the service connects again for the next email.
- If the service can't connect or log in at all, every email fails with that error. As it tries again for each one, this can take a while if the server isn't responding.
- It uses the socket options from your settings, and prefixes the failure messages with the exception type, as
SendEmailAsync(EmailParameters)does. - The emails are sent one after the other, not in parallel, so this won't exceed the sending rate of providers that limit it, such as Amazon SES. It's quicker because each email doesn't need its own connection, not because the emails are sent any faster.
It's a separate method, rather than another SendEmailAsync overload, because an overload taking a collection would make existing calls such as SendEmailAsync(new("Hello", body, "billy@shears.com")) ambiguous.
Breaking change in version 2.0.0
As from version 2.0.0, the service uses an ApiResponse from the Pixata.Extensions package to indicate success or failure. Previous versions used LanguageExt, which has now been removed from this package.
If you are upgrading from a LanguageExt version, then you will need to change the code in the failure lambda. The LanguageExt version passed in a System.Exception, whereas the new version passes a string containing the error message. For example, you would change...
(await _emailService.SendEmailAsync("billy@shears.com", "Hello from Jim Spriggs", htmlBody)))
// Note that ex is an Exception, so we need to use the Message property to get the details
.Match(_ => /* code on success */, ex => Console.WriteLine(ex.Message));
...to...
(await _emailService.SendEmailAsync("billy@shears.com", "Hello from Jim Spriggs", htmlBody)))
// Note that we use ex directly, as it is a string, not an Exception, so we don't have a Message property
.Match(_ => /* code on success */, ex => Console.WriteLine(ex));
You will need also to make sure you wrap the first line in brackets (as shown above). This was not necessary before.
If your code captures the return value from SendEmailAsync in a local variable, then you will need to add a using statement for Pixata.Email and change the type of the variable from TryAsync<Unit> to ApiResponse<Yunit> (unless you use var in which case the compiler will correctly infer the return type). However, this is not a common pattern when using this service.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- MailKit (>= 4.16.0)
- Pixata.Extensions (>= 2.22.0)
- System.Security.Cryptography.Pkcs (>= 10.0.13)
-
net8.0
- MailKit (>= 4.16.0)
- Pixata.Extensions (>= 2.22.0)
- System.Security.Cryptography.Pkcs (>= 8.0.2)
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 |
|---|---|---|
| 3.1.0 | 83 | 10/19/2026 |
| 3.0.1 | 111 | 10/15/2026 |
| 3.0.0 | 351 | 10/6/2026 |
| 2.5.0 | 356 | 8/17/2026 |
| 2.3.0 | 165 | 7/19/2026 |
| 2.2.0 | 247 | 4/19/2026 |
| 2.1.0 | 198 | 3/8/2026 |
| 2.0.4 | 168 | 2/23/2026 |
| 2.0.3 | 157 | 2/23/2026 |
| 2.0.1 | 199 | 1/4/2026 |
| 2.0.0 | 194 | 1/4/2026 |
| 1.2.5 | 616 | 11/18/2024 |
| 1.2.4 | 342 | 9/23/2024 |
| 1.2.3 | 1,257 | 9/13/2023 |
| 1.2.1 | 461 | 2/22/2023 |
| 1.2.0 | 655 | 5/17/2022 |
| 1.1.0 | 637 | 4/10/2022 |
| 1.0.0 | 673 | 2/23/2022 |