ZetCode

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.

Program.cs
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.

email.json
{
  "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.

Program.cs
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.

Program.cs
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.

words.txt
sky
blue
rock
water
poem

We send this text file in the attachment.

Program.cs
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.

Program.cs
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.

Program.cs
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

MailKit GitHub page

In this article we have sent emails in C#.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.