C# send mail
last modified October 18, 2026
In this article we show how to send emails in C# with
System.Net.Mail and MailKit.
SMTP
The Simple Mail Transfer Protocol (SMTP) is an internet standard communication protocol for electronic mail transmission. Mail clients use SMTP to submit messages to a mail server, and mail servers use it to relay messages to each other.
Mail clients usually submit messages on port 587 and switch to an encrypted connection with the STARTTLS command. Port 465 uses implicit TLS; the connection is encrypted from the start. Port 25 is used for communication between servers and is often blocked by internet providers.
In C#, we can use the built-in System.Net.Mail namespace or the
MailKit library to send emails. Microsoft does not recommend the
System.Net.Mail.SmtpClient class for new development, because it
does not support many modern protocols; for instance, it cannot connect to
port 465 with implicit TLS, and it does not support OAuth2 authentication. It
is still fine for simple tasks; for anything more, MailKit is the recommended
library.
Testing emails with Mailpit
Sending test messages through a real mailbox such as Gmail is not a good idea. We should use a test SMTP server that catches all messages instead of delivering them. In this article we use Mailpit, a free, open source tool that we run locally in a Docker container. Hosted services such as Mailtrap work in a similar way.
$ docker run -d --name mailpit -p 8025:8025 -p 1025:1025 axllent/mailpit \
--smtp-auth-accept-any --smtp-auth-allow-insecure
Mailpit listens for SMTP connections on port 1025 and has a web interface on
port 8025. The two options make it accept any username and password over an
unencrypted connection; this way we can also test authentication. The caught
messages are displayed at http://localhost:8025. The
Mailpit article shows how to check the caught
messages from C# code and in automated tests.
C# simple mail example
In the first example, we send a simple text message with
System.Net.Mail.
using System.Net.Mail;
var from = "john.doe@example.com";
var to = "jane.doe@example.com";
var subject = "Test mail";
var body = "Test body";
using var client = new SmtpClient("localhost", 1025);
client.Send(from, to, subject, body);
Console.WriteLine("Email sent");
An email is sent with SmtpClient.
using var client = new SmtpClient("localhost", 1025);
We create an SmtpClient for the given host and port. The class
implements IDisposable; disposing it sends the QUIT command and
closes the connection.
client.Send(from, to, subject, body);
The Send method sends the message to the SMTP server for delivery.
The sender, recipients, subject, and body are given as strings. There is also
an asynchronous SendMailAsync method.
$ dotnet run Email sent
C# email JSON config
A real SMTP server requires authentication and an encrypted connection. We should not hard-code the server details, and especially the password, in the source code. In the next example, we read the SMTP settings from a JSON file.
$ dotnet add package Microsoft.Extensions.Configuration.Json $ dotnet add package Microsoft.Extensions.Configuration.Binder
We add the configuration packages. The binder package maps configuration sections to .NET objects.
{
"Smtp": {
"Host": "localhost",
"Port": 1025,
"EnableSsl": false,
"Username": "john.doe",
"Password": "s$cret"
}
}
This is the JSON config file. For a real server, we would set the port to 587
and EnableSsl to true. The file must not be committed
to a source code repository; in development, we can keep the password in the
user secrets store, in production in an environment variable or a secret
manager.
using System.Net;
using System.Net.Mail;
using Microsoft.Extensions.Configuration;
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("email.json")
.Build();
var smtp = config.GetSection("Smtp").Get<SmtpSettings>()
?? throw new InvalidOperationException("Missing Smtp section");
using var client = new SmtpClient(smtp.Host, smtp.Port)
{
Credentials = new NetworkCredential(smtp.Username, smtp.Password),
EnableSsl = smtp.EnableSsl
};
client.Send("john.doe@example.com", "jane.doe@example.com",
"Config test", "The SMTP settings were read from email.json");
Console.WriteLine("Email sent");
record SmtpSettings(string Host, int Port, bool EnableSsl,
string Username, string Password);
The host, port, username, and password are read from the JSON config file.
var smtp = config.GetSection("Smtp").Get<SmtpSettings>()
?? throw new InvalidOperationException("Missing Smtp section");
We bind the Smtp section to the SmtpSettings record.
The Get method returns null if the section does not
exist.
using var client = new SmtpClient(smtp.Host, smtp.Port)
{
Credentials = new NetworkCredential(smtp.Username, smtp.Password),
EnableSsl = smtp.EnableSsl
};
We set the credentials and the encryption. With EnableSsl set to
true, the client uses STARTTLS.
C# HTML email
In the next example, we send an HTML email.
using System.Net.Mail;
using var client = new SmtpClient("localhost", 1025);
using var mail = new MailMessage
{
From = new MailAddress("john.doe@example.com", "John Doe"),
Subject = "HTML email",
Body = "<p>An old <b>falcon</b> in the sky.</p>",
IsBodyHtml = true
};
mail.To.Add(new MailAddress("jane.doe@example.com", "Jane Doe"));
client.Send(mail);
Console.WriteLine("Email sent");
We use MailMessage to build the email. The
MailAddress class represents an address with an optional display
name.
IsBodyHtml = true
The IsBodyHtml property indicates that the body is in HTML.
C# email attachment
In the next example, we attach a text file to the email.
sky blue rock water poem
We send this text file in the attachment.
using System.Net.Mail;
using var client = new SmtpClient("localhost", 1025);
using var mail = new MailMessage("john.doe@example.com", "jane.doe@example.com")
{
Subject = "Email with attachment",
Body = "Please find the list of words attached."
};
mail.Attachments.Add(new Attachment("words.txt"));
client.Send(mail);
Console.WriteLine("Email sent");
We use the Attachments property and the Attachment
class to send an attachment with the email. The attachment keeps the file open
until the message is disposed; disposing the MailMessage also
disposes its attachments.
C# send simple mail with MailKit
Now we use the MailKit library to send a simple mail.
$ dotnet add package MailKit
We add the MailKit package to the project. It depends on MimeKit,
which is used to create the messages.
Note: At the time of writing, MailKit 4.18.1 depends on
System.Security.Cryptography.Pkcs 10.0.0, which has known
vulnerabilities, and dotnet build shows NU1902 and NU1903 warnings.
They disappear when we add the latest version of the package directly with
dotnet add package System.Security.Cryptography.Pkcs.
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
var message = new MimeMessage();
message.From.Add(new MailboxAddress("John Doe", "john.doe@example.com"));
message.To.Add(new MailboxAddress("Jane Doe", "jane.doe@example.com"));
message.Subject = "Test subject";
message.Body = new TextPart("plain")
{
Text = "Test body"
};
using var client = new SmtpClient();
await client.ConnectAsync("localhost", 1025, SecureSocketOptions.None);
await client.AuthenticateAsync("john.doe", "s$cret");
string response = await client.SendAsync(message);
await client.DisconnectAsync(true);
Console.WriteLine($"Email sent: {response}");
We use the MimeMessage and SmtpClient classes to
build and send the email. Note that SmtpClient here is the MailKit
class from the MailKit.Net.Smtp namespace.
message.Body = new TextPart("plain")
{
Text = "Test body"
};
The body of a simple text message is a TextPart with the
text/plain media type.
await client.ConnectAsync("localhost", 1025, SecureSocketOptions.None);
await client.AuthenticateAsync("john.doe", "s$cret");
We connect to the server and authenticate. Mailpit does not use encryption,
so we pass SecureSocketOptions.None. For a real server, we use
SecureSocketOptions.StartTls with port 587 or
SecureSocketOptions.SslOnConnect with port 465.
string response = await client.SendAsync(message); await client.DisconnectAsync(true);
The SendAsync method returns the final response of the server.
With DisconnectAsync(true), the client sends the QUIT command
before closing the connection.
$ dotnet run Email sent: 2.0.0 Ok: queued as 5TseOxSenBKENXsDpT7caS
C# HTML email with attachment in MailKit
In the next example, we send an email with an HTML body, a plain text alternative, and an attachment.
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
var message = new MimeMessage();
message.From.Add(new MailboxAddress("John Doe", "john.doe@example.com"));
message.To.Add(new MailboxAddress("Jane Doe", "jane.doe@example.com"));
message.Cc.Add(new MailboxAddress("Roger Roe", "roger.roe@example.com"));
message.Subject = "Monthly report";
var builder = new BodyBuilder
{
TextBody = "Hello Jane,\n\nthe report is attached.\n\nJohn",
HtmlBody = "<p>Hello Jane,</p><p>the <b>report</b> is attached.</p><p>John</p>"
};
await builder.Attachments.AddAsync("words.txt");
message.Body = builder.ToMessageBody();
using var client = new SmtpClient();
await client.ConnectAsync("localhost", 1025, SecureSocketOptions.None);
await client.SendAsync(message);
await client.DisconnectAsync(true);
Console.WriteLine("Email sent");
The BodyBuilder class simplifies building messages with several
parts.
var builder = new BodyBuilder
{
TextBody = "Hello Jane,\n\nthe report is attached.\n\nJohn",
HtmlBody = "<p>Hello Jane,</p>..."
};
When we set both the text and the HTML body, the builder creates a
multipart/alternative part. Mail clients display the HTML version
and fall back to the plain text one if they cannot show HTML. Including a text
version also lowers the chance that the message is marked as spam.
await builder.Attachments.AddAsync("words.txt");
We add the attachment. Its MIME type is determined from the file extension.
message.Body = builder.ToMessageBody();
The ToMessageBody method builds the final MIME structure: a
multipart/mixed part containing the alternative bodies and the
attachment.
$ curl -s localhost:8025/api/v1/messages | jq '.messages[0] | {Subject, Cc, Attachments}'
{
"Subject": "Monthly report",
"Cc": [
{
"Name": "Roger Roe",
"Address": "roger.roe@example.com"
}
],
"Attachments": 1
}
Besides the web interface, Mailpit has a REST API. We use it to check the last received message.
Source
In this article we have sent emails in C#.
Author
List all C# tutorials.